Skip to content

Commit 32f23a4

Browse files
authored
Show severity and score from providers in addition to NVD in the summary report (#72)
Co-authored-by: Kenji Sugimura <kensugim@amazon.co.jp>
1 parent 6e5c583 commit 32f23a4

File tree

3 files changed

+102
-37
lines changed

3 files changed

+102
-37
lines changed

entrypoint/entrypoint/pkg_vuln.py

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import os
1111
import urllib.parse
1212

13+
from dataclasses import dataclass
1314
from io import StringIO
1415
from typing import List
1516

@@ -38,6 +39,12 @@ def __init__(self):
3839
self.cwes = "null"
3940

4041

42+
@dataclass
43+
class CvssRating:
44+
severity: str = "null"
45+
cvss_score: str = "null"
46+
47+
4148
def vulns_to_obj(inspector_scan_json) -> List[Vulnerability]:
4249
"""
4350
this function parses JSON from Inspector's ScanSbom API
@@ -70,18 +77,7 @@ def vulns_to_obj(inspector_scan_json) -> List[Vulnerability]:
7077

7178
# get vuln severity and EPSS score
7279
ratings = v.get("ratings")
73-
if ratings:
74-
nvd_severity = get_nvd_severity(ratings)
75-
if nvd_severity:
76-
vuln_obj.severity = nvd_severity
77-
78-
nvd_score = get_nvd_score(ratings)
79-
if nvd_score:
80-
vuln_obj.cvss_score = nvd_score
81-
82-
epss_score = get_epss_score(ratings)
83-
if epss_score:
84-
vuln_obj.epss_score = epss_score
80+
add_ratings(ratings, vuln_obj)
8581

8682
# get vulnerability published date
8783
published = v.get("created")
@@ -207,34 +203,37 @@ def getPropertyValueFromKey(vuln_json, key):
207203
return None
208204

209205

210-
def get_nvd_severity(ratings):
211-
for rating in ratings:
212-
source = rating.get("source")
213-
if not source:
214-
continue
206+
def add_ratings(ratings, vulnerability):
207+
if ratings is None:
208+
return
215209

216-
if source["name"] == "NVD":
217-
severity = rating["severity"]
218-
if severity:
219-
return severity
220-
return None
210+
rating = get_cvss_rating(ratings, vulnerability)
211+
vulnerability.severity = rating.severity
212+
vulnerability.cvss_score = rating.cvss_score
221213

214+
epss_score = get_epss_score(ratings)
215+
if epss_score:
216+
vulnerability.epss_score = epss_score
222217

223-
def get_nvd_score(ratings):
224-
for rating in ratings:
225-
source = rating.get("source")
226-
if not source:
227-
continue
228218

229-
method = rating.get("method")
230-
if not method:
231-
continue
219+
def get_cvss_rating(ratings, vulnerability) -> CvssRating:
220+
rating_provider_priority = [
221+
'NVD',
222+
'MITRE',
223+
'AMAZON_INSPECTOR'
224+
]
225+
for provider in rating_provider_priority:
226+
for rating in ratings:
227+
if rating['source']['name'] != provider:
228+
continue
232229

233-
if source["name"] == "NVD" and method == "CVSSv31":
234-
score = rating["score"]
235-
if score:
236-
return score
237-
return None
230+
severity = rating["severity"]
231+
cvss_score = rating["score"] if rating['method'] == 'CVSSv31' else "null"
232+
if severity and cvss_score:
233+
return CvssRating(severity=severity, cvss_score=cvss_score)
234+
235+
logging.info(f"No CVSS rating is provided for {vulnerability.vuln_id}")
236+
return CvssRating()
238237

239238

240239
def get_epss_score(ratings):

entrypoint/tests/__init__.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1-
from entrypoint import log_conf
1+
import logging
22

3-
log_conf.init(enable_verbose=True)
3+
4+
logger = logging.getLogger()
5+
logger.setLevel(logging.ERROR)
6+
7+
handler = logging.StreamHandler()
8+
handler.setLevel(logging.ERROR)

entrypoint/tests/test_pkg_vuln.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,67 @@ def test_post_github_step_summary_no_vulns(self):
6464
self.assertIn(expected, zero_vuln_summary_md)
6565
cleanup_stale_markdown_report(markdown_dst_path)
6666

67+
def test_get_rating(self):
68+
tests = [
69+
{
70+
"name": "NVD should take a priority over other providers",
71+
"ratings": [
72+
{
73+
"method": "CVSSv31",
74+
"score": 7.2,
75+
"severity": "high",
76+
"source": {"name": "MITRE"},
77+
},
78+
{
79+
"method": "CVSSv31",
80+
"score": 5.3,
81+
"severity": "medium",
82+
"source": {"name": "NVD"},
83+
},
84+
{
85+
"method": "other",
86+
"score": 0.00051,
87+
"severity": "none",
88+
"source": {"name": "EPSS"},
89+
},
90+
],
91+
"expected": pkg_vuln.CvssRating("medium", 5.3),
92+
},
93+
{
94+
"name": 'score should be "null" when method is "other"',
95+
"ratings": [
96+
{
97+
"method": "other",
98+
"severity": "medium",
99+
"source": {"name": "AMAZON_INSPECTOR"},
100+
},
101+
{
102+
"method": "other",
103+
"score": 0.00051,
104+
"severity": "none",
105+
"source": {"name": "EPSS"},
106+
},
107+
],
108+
"expected": pkg_vuln.CvssRating("medium"),
109+
},
110+
{
111+
"name": 'rating should be "null" when neither NVD, MITRE, nor AMAZON_INSPECTOR provides a rating',
112+
"ratings": [
113+
{
114+
"method": "other",
115+
"score": 0.00051,
116+
"severity": "none",
117+
"source": {"name": "EPSS"},
118+
},
119+
],
120+
"expected": pkg_vuln.CvssRating(),
121+
},
122+
]
123+
for test in tests:
124+
with self.subTest(msg=test["name"]):
125+
rating = pkg_vuln.get_cvss_rating(test["ratings"], pkg_vuln.Vulnerability())
126+
self.assertEqual(test["expected"], rating)
127+
67128

68129
def get_scan_body(test_file):
69130
# test_file = "tests/test_data/artifacts/containers/dockerfile_checks/inspector-scan-cdx.json"

0 commit comments

Comments
 (0)