Skip to content

Commit 2b72e6b

Browse files
committed
Added support to handle accepted risks #264
1 parent 900e41c commit 2b72e6b

File tree

6 files changed

+78
-17
lines changed

6 files changed

+78
-17
lines changed

tenb2jira/tenable/generators.py

+19-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
import typing
1+
from typing import TYPE_CHECKING, Generator, Optional, Any
22
import arrow
33
from restfly.utils import dict_flatten, dict_merge
44
import uuid
55

6-
if typing.TYPE_CHECKING:
6+
if TYPE_CHECKING:
77
from tenable.io.exports.iterator import ExportsIterator
88
from tenable.sc.analysis import AnalysisResultsIterator
99

1010

11-
def tvm_asset_cleanup(*assets_iters: 'ExportsIterator') -> dict:
11+
def tvm_asset_cleanup(*assets_iters: 'ExportsIterator'
12+
) -> Generator[Any, Any, Any]:
1213
"""
1314
A simple wrapper to coalesce the multiple terminated asset states within
1415
TVM.
@@ -26,8 +27,9 @@ def tvm_asset_cleanup(*assets_iters: 'ExportsIterator') -> dict:
2627

2728
def tvm_merged_data(assets_iter: 'ExportsIterator',
2829
vulns_iter: 'ExportsIterator',
29-
asset_fields: list[str] = None
30-
) -> dict:
30+
asset_fields: Optional[list[str]] = None,
31+
close_accepted: bool = True,
32+
) -> Generator[Any, Any, Any]:
3133
"""
3234
Merges the asset and vulnerability finding data together into a single
3335
object and adds in a computed finding id based on the following attributes:
@@ -86,11 +88,19 @@ def spf(value: str) -> str:
8688
))
8789
f['integration_pid_updated'] = pid
8890

91+
# If accepted risks shoudl be flagged as closed, then we will replace
92+
# the state field with "fixed" if the risk was indeed accepted.
93+
sevmod = f.get('severity_modification_type')
94+
if close_accepted and sevmod == 'ACCEPTED':
95+
f['state'] = 'FIXED'
96+
8997
# Return the augmented finding to the caller.
9098
yield f
9199

92100

93-
def tsc_merged_data(*vuln_iters: 'AnalysisResultsIterator') -> dict:
101+
def tsc_merged_data(*vuln_iters: 'AnalysisResultsIterator',
102+
close_accepted: bool = True,
103+
) -> Generator[Any, Any, Any]:
94104
"""
95105
Flattens and extends the vulnerability results returned from the
96106
Security Center analysis API. The following fields are added to the
@@ -124,7 +134,9 @@ def tsc_merged_data(*vuln_iters: 'AnalysisResultsIterator') -> dict:
124134
# If the hasBeenMitigated flag was flipped, then the finding isn't
125135
# open, but is reopened. We want to confer state accurately so we
126136
# will check that here.
127-
if f['hasBeenMitigated'] == '1' and state == 'open':
137+
if close_accepted and f['acceptRisk'] == '1':
138+
f['integration_state'] = 'fixed'
139+
elif f['hasBeenMitigated'] == '1' and state == 'open':
128140
f['integration_state'] = 'reopened'
129141
else:
130142
f['integration_state'] = state

tenb2jira/tenable/tenable.py

+17-9
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Generator
1+
from typing import Generator, Any
22
import arrow
33
from tenable.io import TenableIO
44
from tenable.sc import TenableSC
@@ -14,6 +14,7 @@ class Tenable:
1414
platform: str
1515
timestamp: int
1616
age: int
17+
close_accepted: bool
1718
severity: list[str]
1819
chunk_size: int = 1000
1920
page_size: int = 1000
@@ -29,6 +30,9 @@ def __init__(self, config: dict):
2930
self.chunk_size = self.config['tenable']['tvm_chunk_size']
3031
self.page_size = self.config['tenable']['tsc_page_size']
3132
self.query_id = self.config['tenable'].get('tsc_query_id')
33+
self.close_accepted = self.config['tenable'].get('fix_accepted_risks',
34+
True
35+
)
3236

3337
if not self.timestamp:
3438
self.timestamp = int(arrow.now()
@@ -53,7 +57,7 @@ def __init__(self, config: dict):
5357
)
5458

5559
def get_generator(self) -> Generator:
56-
self.last_run = arrow.now().timestamp()
60+
self.last_run = int(arrow.now().timestamp())
5761
if self.platform == 'tvm':
5862
assets = self.tvm.exports.assets(updated_at=self.timestamp,
5963
chunk_size=self.chunk_size
@@ -62,9 +66,12 @@ def get_generator(self) -> Generator:
6266
severity=self.severity,
6367
state=['open', 'reopened', 'fixed'],
6468
include_unlicensed=True,
65-
num_assets=self.chunk_size
69+
num_assets=self.chunk_size,
6670
)
67-
return tvm_merged_data(assets, vulns)
71+
return tvm_merged_data(assets,
72+
vulns,
73+
close_accepted=self.close_accepted,
74+
)
6875
if self.platform == 'tsc':
6976
sevmap = {
7077
'info': '0',
@@ -87,9 +94,12 @@ def get_generator(self) -> Generator:
8794
query_id=self.query_id,
8895
limit=self.page_size
8996
)
90-
return tsc_merged_data(cumulative, patched)
97+
return tsc_merged_data(cumulative,
98+
patched,
99+
close_accepted=self.close_accepted,
100+
)
91101

92-
def get_asset_cleanup(self) -> Generator:
102+
def get_asset_cleanup(self) -> (Generator[Any, Any, Any] | list):
93103
if self.platform == 'tvm':
94104
dassets = self.tvm.exports.assets(deleted_at=self.timestamp,
95105
chunk_size=self.chunk_size
@@ -98,7 +108,5 @@ def get_asset_cleanup(self) -> Generator:
98108
chunk_size=self.chunk_size
99109
)
100110
return tvm_asset_cleanup(dassets, tassets)
101-
if self.platform == 'tsc':
111+
else:
102112
return []
103-
104-

tenb2jira/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version = '2.0.5'
1+
version = '2.0.6'

tests/tenable/test_generators.py

+35
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,31 @@ def test_tvm_merged_data(tvm_assets, tvm_finding):
158158
assert finding['asset.test'] == 'value'
159159

160160

161+
@responses.activate
162+
def test_tvm_merged_data_accepted(tvm_assets, tvm_finding):
163+
pmoddate = arrow.get('2020-04-27T00:00:00Z')
164+
test_uuid = UUID('dd13a88d-2fbf-3d2a-930f-38fdc850f86d')
165+
responses.get('https://cloud.tenable.com/assets/export/0/status',
166+
json={'status': 'FINISHED', 'available_chunks': []})
167+
tvm = TenableIO(access_key='None', secret_key='None')
168+
asset_iter = ExportsIterator(tvm)
169+
asset_iter.uuid = 0
170+
asset_iter.type = 'assets'
171+
asset_iter.page = tvm_assets
172+
tvm_finding['severity_modification_type'] = 'ACCEPTED'
173+
finding_iter = ExportsIterator(tvm)
174+
finding_iter.page = [tvm_finding for _ in range(100)]
175+
tvm_generator = tvm_merged_data(assets_iter=asset_iter,
176+
vulns_iter=finding_iter,
177+
close_accepted=True,
178+
)
179+
finding = next(tvm_generator)
180+
assert finding['state'] == 'FIXED'
181+
assert finding['asset.uuid'] == '7f68f334-17ba-4ba0-b057-b77ddd783e60'
182+
assert finding['integration_finding_id'] == test_uuid
183+
assert finding['integration_pid_updated'] == pmoddate
184+
185+
161186
def test_tsc_merged_data(tsc_finding):
162187
test_uuid = UUID('d90cdab5-b745-3e7e-9268-aa0f445ed924')
163188
fuuid = UUID('bd371510-001f-3c13-86f4-20883ef0cd09')
@@ -184,3 +209,13 @@ def test_tsc_merged_data(tsc_finding):
184209
tsc_generator = tsc_merged_data(findings)
185210
finding = next(tsc_generator)
186211
assert finding['integration_state'] == 'fixed'
212+
213+
tsc_finding['acceptRisk'] = '1'
214+
findings = AnalysisResultsIterator(None)
215+
findings.page = [tsc_finding for _ in range(100)]
216+
findings._query = {'sourceType': 'cumulative'}
217+
tsc_generator = tsc_merged_data(findings)
218+
finding = next(tsc_generator)
219+
assert finding['integration_state'] == 'fixed'
220+
221+

tmpl_v1_conversion_config.toml

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ tsc_page_size = 1000
5757
# Management. You may need to adjust this number based on memory restrictions.
5858
tvm_chunk_size = 1000
5959

60+
# Should accepted risks be treated as closed findings?
61+
fix_accepted_risks = true
62+
6063
# The names of the platforms to be relayed to Jira for the "Tenable Platform"
6164
# field. Generally it's best to leave these values alone.
6265
platforms.tvm = "Tenable Vulnerability Management"

tmpl_v2_new_config.toml

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ tsc_page_size = 1000
5757
# Management. You may need to adjust this number based on memory restrictions.
5858
tvm_chunk_size = 1000
5959

60+
# Should accepted risks be treated as closed findings?
61+
fix_accepted_risks = true
62+
6063
# The names of the platforms to be relayed to Jira for the "Tenable Platform"
6164
# field. Generally it's best to leave these values alone.
6265
platforms.tvm = "Tenable Vulnerability Management"

0 commit comments

Comments
 (0)