Skip to content

Commit afd8651

Browse files
CarolMebiomMaria Carolina Conceição
andauthored
Feature request 91 (#115)
* FR-91: Add cli arg only fixable vulnerability; use the variable in get_vuln_counts * Revert "FR-91: Add cli arg only fixable vulnerability; use the variable in get_vuln_counts" This reverts commit bc532d4. * FR-91: Add cli arg only fixable vulnerability; use the variable in get_vuln_counts * FR-91: Fix unit tests * FR-91: Fix typo in unit tests * Revert "FR-91: Fix typo in unit tests" This reverts commit e645542. * Revert "FR-91: Fix unit tests" This reverts commit f9157c9. * Revert "FR-91: Add cli arg only fixable vulnerability; use the variable in get_vuln_counts" This reverts commit 812c685. * FR-91: Change orchestrator to only find fixed vulnerabilities if flag show-only-fixed-vulnerabilities is present * FR-91: Fixed missing variable * FR-91: Fixed typo * FR-91: Fixed typo * FR-91: Another fix * FR-91: Another fix * FR-91: Another fix * FR-91: Another fix * FR-91: Another fix * FR-91: Another fix * FR-91: Another fix * Add unit test for get_vuln_count * Fix unit test for get_vuln_count --------- Co-authored-by: Maria Carolina Conceição <carolina.bento@floy.com>
1 parent 5dc8a4b commit afd8651

File tree

3 files changed

+120
-6
lines changed

3 files changed

+120
-6
lines changed

entrypoint/entrypoint/cli.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def init(sys_argv=None) -> argparse.Namespace:
5050
help="Specifies one or more files and/or directories that should NOT be inventoried.")
5151
parser.add_argument("--timeout", type=str, default="600",
5252
help="The amount of time in seconds that inspector-sbomgne will run. When this timeout is exceeded, sbomgen will gracefully conclude and present any findings discovered up to that point.")
53+
parser.add_argument("--show-only-fixed-vulnerabilities", action="store_true", help="If set, this program will only show fixed vulnerabilities in the GitHub Actions job summary page.")
5354

5455
parser.add_argument("--platform", type=str,
5556
help="Specifies the OS and CPU arch of the container image you wish to scan. Valid inputs are "

entrypoint/entrypoint/orchestrator.py

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def invoke_sbomgen(args) -> int:
166166

167167
elif "archive" in args.artifact_type.lower():
168168
args.artifact_type = "archive"
169-
path_arg = "--path"
169+
path_arg = "--path"
170170

171171
else:
172172
logging.error(
@@ -233,18 +233,27 @@ def invoke_inspector_scan(src_sbom, dst_scan):
233233

234234

235235
def get_scan_result(args) -> tuple[bool, exporter.InspectorScanResult]:
236-
succeeded, criticals, highs, mediums, lows, others = get_vuln_counts(args.out_scan)
237-
if succeeded is False:
238-
return False, None
236+
if args.show_only_fixed_vulnerabilities == True:
237+
succeeded, criticals, highs, mediums, lows, others = get_fixed_vuln_counts(args.out_scan)
238+
if succeeded is False:
239+
return False, None
240+
else:
241+
succeeded, criticals, highs, mediums, lows, others = get_vuln_counts(args.out_scan)
242+
if succeeded is False:
243+
return False, None
239244

240245
try:
241246
with open(args.out_scan, "r") as f:
242247
inspector_scan = json.load(f)
243248
vulns = pkg_vuln.parse_inspector_scan_result(inspector_scan)
249+
if args.show_only_fixed_vulnerabilities:
250+
for vuln in vulns:
251+
if vuln.fixed_ver == "null":
252+
vulns.remove(vuln)
244253
except Exception as e:
245254
logging.error(e)
246255
return False, None
247-
256+
248257
scan_result = exporter.InspectorScanResult(
249258
vulnerabilities=vulns,
250259
artifact_name=args.artifact_path,
@@ -270,7 +279,7 @@ def set_github_actions_output(key, value):
270279
return
271280

272281

273-
def get_vuln_counts(inspector_scan_path: str):
282+
def get_vuln_counts(inspector_scan_path: str) -> tuple[bool, int, int, int, int, int]:
274283
# vuln severities
275284
criticals = 0
276285
highs = 0
@@ -331,6 +340,78 @@ def get_vuln_counts(inspector_scan_path: str):
331340

332341
return True, criticals, highs, mediums, lows, others
333342

343+
def get_fixed_vuln_counts(inspector_scan_path: str) -> tuple[bool, int, int, int, int, int]:
344+
criticals_fixed = 0
345+
highs_fixed = 0
346+
mediums_fixed = 0
347+
lows_fixed = 0
348+
others_fixed = 0
349+
350+
scan_contents = ""
351+
try:
352+
with open(inspector_scan_path, 'r') as f:
353+
scan_contents = json.load(f)
354+
except Exception as e:
355+
logging.error(e)
356+
return False, criticals, highs, mediums, lows, others
357+
358+
# find the sbom->metadata->properties object
359+
scan_contents = scan_contents.get("sbom")
360+
if scan_contents is None:
361+
logging.error(
362+
f"expected Inspector scan results with 'sbom' as root object, but it was not found in file {inspector_scan_path}")
363+
return False, criticals, highs, mediums, lows, others
364+
365+
vulnerabilities = scan_contents.get("vulnerabilities")
366+
367+
if vulnerabilities is None:
368+
# no vulnerabilities found
369+
return True, criticals_fixed, highs_fixed, mediums_fixed, lows_fixed, others_fixed
370+
371+
for vuln in vulnerabilities:
372+
props = vuln.get("properties")
373+
if props is None:
374+
logging.error(f"expected vulnerability with 'properties' key but none was found in file {inspector_scan_path}")
375+
continue
376+
377+
for prop in props:
378+
name = prop.get("name")
379+
if name is None:
380+
logging.error(f"expected vulnerability with 'name' key but none was found in file {inspector_scan_path}")
381+
continue
382+
if "amazon:inspector:sbom_scanner:fixed_version:comp-" in name:
383+
rating = vuln.get("ratings")
384+
if rating is None:
385+
logging.error(f"expected vulnerability with 'rating' key but none was found in file {inspector_scan_path}")
386+
continue
387+
388+
previous_score = 0
389+
previous_severity = ""
390+
for each_rating in rating:
391+
severity = each_rating.get("severity")
392+
if severity is None:
393+
logging.error(f"expected vulnerability with 'severity' key but none was found in file {inspector_scan_path}")
394+
continue
395+
score = each_rating.get("score")
396+
if score is None:
397+
logging.error(f"expected vulnerability with 'score' key but none was found in file {inspector_scan_path}")
398+
continue
399+
if previous_score < score:
400+
previous_score = score
401+
previous_severity = severity
402+
403+
if previous_severity == "none":
404+
others_fixed += 1
405+
elif previous_severity == "critical":
406+
criticals_fixed += 1
407+
elif previous_severity == "high":
408+
highs_fixed += 1
409+
elif previous_severity == "medium":
410+
mediums_fixed += 1
411+
elif previous_severity == "low":
412+
lows_fixed += 1
413+
414+
return True, criticals_fixed, highs_fixed, mediums_fixed, lows_fixed, others_fixed
334415

335416
def install_sbomgen(args):
336417
os_name = platform.system()

entrypoint/tests/test_orchestrator.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def test_system_against_dockerfile_findings(self):
125125
"out_scan_markdown",
126126
"out_dockerfile_scan_csv",
127127
"out_dockerfile_scan_md",
128+
"show_only_fixed_vulnerabilities",
128129
],
129130
)
130131
args = ArgMock(
@@ -135,6 +136,7 @@ def test_system_against_dockerfile_findings(self):
135136
out_scan_markdown="/tmp/out_scan.md",
136137
out_dockerfile_scan_csv="/tmp/out_dockerfile_scan.csv",
137138
out_dockerfile_scan_md="/tmp/out_dockerfile_scan.md",
139+
show_only_fixed_vulnerabilities=False,
138140
)
139141

140142
succeeded, scan_result = orchestrator.get_scan_result(args)
@@ -201,6 +203,36 @@ def test_is_valid_container_platform(self):
201203
for each_test in test_cases:
202204
result = orchestrator.is_valid_container_platform(each_test["input"])
203205
self.assertEqual(result, each_test["expected"])
206+
207+
def test_get_fixed_vuln_counts(self):
208+
# verify we can successfully parse all known-valid Inspector scans
209+
test_dir = "tests/test_data/scans/"
210+
file_list = os.listdir(test_dir)
211+
for file in file_list:
212+
path = os.path.join(test_dir, file)
213+
succeeded, _, _, _, _, _ = orchestrator.get_fixed_vuln_counts(path)
214+
self.assertTrue(succeeded)
215+
216+
# verify our fixed vulnerability counts are correct for alpine:3.18.2.json.scan
217+
succeeded, criticals_fixed, highs_fixed, mediums_fixed, lows_fixed, others_fixed = orchestrator.get_fixed_vuln_counts(
218+
"tests/test_data/scans/alpine:3.18.2.json.scan"
219+
)
220+
self.assertTrue(succeeded)
221+
self.assertEqual(criticals, 1)
222+
self.assertEqual(highs, 1)
223+
self.assertEqual(mediums, 7)
224+
self.assertEqual(lows, 0)
225+
self.assertEqual(others, 3)
226+
227+
succeeded, criticals_fixed, highs_fixed, mediums_fixed, lows_fixed, others_fixed = orchestrator.get_fixed_vuln_counts(
228+
"tests/test_data/scans/debian:latest.json.scan"
229+
)
230+
self.assertTrue(succeeded)
231+
self.assertEqual(criticals_fixed, 0)
232+
self.assertEqual(highs_fixed, 0)
233+
self.assertEqual(mediums_fixed, 0)
234+
self.assertEqual(lows_fixed, 0)
235+
self.assertEqual(others_fixed, 6)
204236

205237
if __name__ == "__main__":
206238
unittest.main()

0 commit comments

Comments
 (0)