Skip to content

Commit 8bac8dd

Browse files
authored
Trivy: Use CVSS scores from other vendors where applicable (#12826)
1 parent c6481a5 commit 8bac8dd

File tree

4 files changed

+195
-21
lines changed

4 files changed

+195
-21
lines changed

dojo/tools/trivy/parser.py

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,13 @@
1717
"UNKNOWN": "Info",
1818
}
1919

20+
CVSS_SEVERITY_SOURCES = [
21+
"nvd",
22+
"ghsa",
23+
"redhat",
24+
"bitnami",
25+
]
26+
2027
DESCRIPTION_TEMPLATE = """{title}
2128
**Target:** {target}
2229
**Type:** {type}
@@ -243,28 +250,29 @@ def get_result_items(self, test, results, service_name=None, artifact_name=""):
243250
try:
244251
vuln_id = vuln.get("VulnerabilityID", "0")
245252
package_name = vuln["PkgName"]
246-
severity_source = vuln.get("SeveritySource", None)
247-
cvss = vuln.get("CVSS", None)
253+
detected_severity_source = vuln.get("SeveritySource", None)
254+
cvss = vuln.get("CVSS", {})
255+
cvssclass = None
248256
cvssv3 = None
249257
cvssv3_score = None
250-
if severity_source is not None and cvss is not None:
258+
# Iterate over the possible severity sources tom find the first match
259+
for severity_source in [detected_severity_source, *CVSS_SEVERITY_SOURCES]:
251260
cvssclass = cvss.get(severity_source, None)
252261
if cvssclass is not None:
253-
if cvssclass.get("V3Score") is not None:
254-
severity = self.convert_cvss_score(cvssclass.get("V3Score"))
255-
cvssv3_string = dict(cvssclass).get("V3Vector")
256-
cvss_data = parse_cvss_data(cvssv3_string)
257-
if cvss_data:
258-
cvssv3 = cvss_data.get("vector")
259-
cvssv3_score = cvss_data.get("score")
260-
elif cvssclass.get("V3Score") is not None:
261-
cvssv3_score = cvssclass.get("V3Score")
262-
elif cvssclass.get("V2Score") is not None:
263-
severity = self.convert_cvss_score(cvssclass.get("V2Score"))
264-
else:
265-
severity = self.convert_cvss_score(None)
262+
break
263+
# Parse the CVSS class if it is not None
264+
if cvssclass is not None:
265+
if cvss_data := parse_cvss_data(cvssclass.get("V3Vector", "")):
266+
cvssv3 = cvss_data.get("vector")
267+
cvssv3_score = cvss_data.get("score")
268+
severity = cvss_data.get("severity")
269+
elif (cvss_v3_score := cvssclass.get("V3Score")) is not None:
270+
cvssv3_score = cvss_v3_score
271+
severity = self.convert_cvss_score(cvss_v3_score)
272+
elif (cvss_v2_score := cvssclass.get("V2Score")) is not None:
273+
severity = self.convert_cvss_score(cvss_v2_score)
266274
else:
267-
severity = TRIVY_SEVERITIES[vuln["Severity"]]
275+
severity = self.convert_cvss_score(None)
268276
else:
269277
severity = TRIVY_SEVERITIES[vuln["Severity"]]
270278
if target_class == "os-pkgs" or target_class == "lang-pkgs":
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
{
2+
"SchemaVersion": 2,
3+
"ArtifactName": "sbom.json",
4+
"ArtifactType": "cyclonedx",
5+
"Metadata": {
6+
"ImageConfig": {
7+
"architecture": "",
8+
"created": "0001-01-01T00:00:00Z",
9+
"os": "",
10+
"rootfs": {
11+
"type": "",
12+
"diff_ids": null
13+
},
14+
"config": {}
15+
}
16+
},
17+
"Results": [
18+
{
19+
"Target": "requirements.txt",
20+
"Class": "lang-pkgs",
21+
"Type": "pip",
22+
"Vulnerabilities": [
23+
{
24+
"VulnerabilityID": "CVE-2023-46218",
25+
"PkgID": "curl@7.81.0-1ubuntu1.14",
26+
"PkgName": "curl",
27+
"PkgIdentifier": {
28+
"PURL": "pkg:deb/ubuntu/curl@7.81.0-1ubuntu1.14?arch=amd64\u0026distro=ubuntu-22.04",
29+
"UID": "43a41104920d137"
30+
},
31+
"InstalledVersion": "7.81.0-1ubuntu1.14",
32+
"FixedVersion": "7.81.0-1ubuntu1.15",
33+
"Status": "fixed",
34+
"Layer": {
35+
"DiffID": "sha256:3a9073a4d18e5ed2ae6f9fd9fee81ea43774907ce603ba955bba8fc0819aa250"
36+
},
37+
"SeveritySource": "ubuntu",
38+
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2023-46218",
39+
"DataSource": {
40+
"ID": "ubuntu",
41+
"Name": "Ubuntu CVE Tracker",
42+
"URL": "https://git.launchpad.net/ubuntu-cve-tracker"
43+
},
44+
"Title": "curl: information disclosure by exploiting a mixed case flaw",
45+
"Description": "This flaw allows a malicious HTTP server to set \"super cookies\" in curl that\nare then passed back to more origins than what is otherwise allowed or\npossible. This allows a site to set cookies that then would get sent to\ndifferent and unrelated sites and domains.\n\nIt could do this by exploiting a mixed case flaw in curl's function that\nverifies a given cookie domain against the Public Suffix List (PSL). For\nexample a cookie could be set with `domain=co.UK` when the URL used a lower\ncase hostname `curl.co.uk`, even though `co.uk` is listed as a PSL domain.",
46+
"Severity": "MEDIUM",
47+
"VendorSeverity": {
48+
"alma": 2,
49+
"amazon": 2,
50+
"azure": 2,
51+
"cbl-mariner": 2,
52+
"nvd": 2,
53+
"oracle-oval": 2,
54+
"photon": 2,
55+
"redhat": 2,
56+
"rocky": 2,
57+
"ubuntu": 2
58+
},
59+
"CVSS": {
60+
"nvd": {
61+
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
62+
"V3Score": 6.5
63+
},
64+
"redhat": {
65+
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N",
66+
"V3Score": 5.3
67+
}
68+
},
69+
"References": [
70+
"https://access.redhat.com/errata/RHSA-2024:1129",
71+
"https://access.redhat.com/security/cve/CVE-2023-46218",
72+
"https://bugzilla.redhat.com/2252030",
73+
"https://bugzilla.redhat.com/show_bug.cgi?id=2196793",
74+
"https://bugzilla.redhat.com/show_bug.cgi?id=2240033",
75+
"https://bugzilla.redhat.com/show_bug.cgi?id=2241938",
76+
"https://bugzilla.redhat.com/show_bug.cgi?id=2252030",
77+
"https://curl.se/docs/CVE-2023-46218.html",
78+
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-28322",
79+
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-38546",
80+
"https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-46218",
81+
"https://errata.almalinux.org/9/ALSA-2024-1129.html",
82+
"https://errata.rockylinux.org/RLSA-2024:1601",
83+
"https://hackerone.com/reports/2212193",
84+
"https://linux.oracle.com/cve/CVE-2023-46218.html",
85+
"https://linux.oracle.com/errata/ELSA-2024-1601.html",
86+
"https://lists.debian.org/debian-lts-announce/2023/12/msg00015.html",
87+
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3ZX3VW67N4ACRAPMV2QS2LVYGD7H2MVE/",
88+
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UOGXU25FMMT2X6UUITQ7EZZYMJ42YWWD/",
89+
"https://nvd.nist.gov/vuln/detail/CVE-2023-46218",
90+
"https://security.netapp.com/advisory/ntap-20240125-0007/",
91+
"https://ubuntu.com/security/notices/USN-6535-1",
92+
"https://ubuntu.com/security/notices/USN-6641-1",
93+
"https://www.cve.org/CVERecord?id=CVE-2023-46218",
94+
"https://www.debian.org/security/2023/dsa-5587"
95+
],
96+
"PublishedDate": "2023-12-07T01:15:07.16Z",
97+
"LastModifiedDate": "2025-02-13T18:15:33.843Z"
98+
},
99+
{
100+
"VulnerabilityID": "CVE-2023-37920",
101+
"PkgName": "certifi",
102+
"InstalledVersion": "2022.5.18.1",
103+
"FixedVersion": "2023.7.22",
104+
"Layer": {},
105+
"SeveritySource": "ghsa",
106+
"PrimaryURL": "https://avd.aquasec.com/nvd/cve-2023-37920",
107+
"Ref": "pkg:pypi/certifi@2022.5.18.1",
108+
"DataSource": {
109+
"ID": "ghsa",
110+
"Name": "GitHub Security Advisory pip",
111+
"URL": "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip"
112+
},
113+
"Title": "python-certifi: Removal of e-Tugra root certificate",
114+
"Description": "Certifi is a curated collection of Root Certificates for validating the trustworthiness of SSL certificates while verifying the identity of TLS hosts. Certifi prior to version 2023.07.22 recognizes \"e-Tugra\" root certificates. e-Tugra's root certificates were subject to an investigation prompted by reporting of security issues in their systems. Certifi 2023.07.22 removes root certificates from \"e-Tugra\" from the root store.",
115+
"Severity": "HIGH",
116+
"CweIDs": ["CWE-345"],
117+
"CVSS": {
118+
"ghsa": {
119+
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
120+
"V3Score": 7.5
121+
},
122+
"nvd": {
123+
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
124+
"V3Score": 9.8
125+
},
126+
"redhat": {
127+
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
128+
"V3Score": 7.5
129+
}
130+
},
131+
"References": [
132+
"https://access.redhat.com/security/cve/CVE-2023-37920",
133+
"https://github.com/certifi/python-certifi",
134+
"https://github.com/certifi/python-certifi/commit/8fb96ed81f71e7097ed11bc4d9b19afd7ea5c909",
135+
"https://github.com/certifi/python-certifi/security/advisories/GHSA-xqr8-7jwr-rhp7",
136+
"https://github.com/pypa/advisory-database/tree/main/vulns/certifi/PYSEC-2023-135.yaml",
137+
"https://groups.google.com/a/mozilla.org/g/dev-security-policy/c/C-HrP1SEq1A",
138+
"https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5EX6NG7WUFNUKGFHLM35KHHU3GAKXRTG/",
139+
"https://nvd.nist.gov/vuln/detail/CVE-2023-37920",
140+
"https://www.cve.org/CVERecord?id=CVE-2023-37920"
141+
],
142+
"PublishedDate": "2023-07-25T21:15:00Z",
143+
"LastModifiedDate": "2023-08-12T06:16:00Z"
144+
}
145+
]
146+
}
147+
]
148+
}

unittests/scans/trivy/issue_9092.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@
4343
"CVSS": {
4444
"ghsa": {
4545
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
46-
"V3Score": 9.8
46+
"V3Score": 7.5
4747
},
4848
"nvd": {
4949
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
50-
"V3Score": 7.5
50+
"V3Score": 9.8
5151
},
5252
"redhat": {
5353
"V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",

unittests/tools/test_trivy_parser.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def test_scheme_2_many_vulns(self):
6868
self.assertIsNotNone(finding.description)
6969
self.assertIsNotNone(finding.references)
7070
self.assertEqual("1.15.5-r1", finding.mitigation)
71-
self.assertIsNone(finding.cvssv3)
71+
self.assertEqual("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", finding.cvssv3)
7272
self.assertTrue(finding.static_finding)
7373
self.assertFalse(finding.dynamic_finding)
7474

@@ -197,7 +197,7 @@ def test_issue_9092(self):
197197
findings = parser.get_findings(test_file, Test())
198198
self.assertEqual(len(findings), 1)
199199
finding = findings[0]
200-
self.assertEqual("Critical", finding.severity)
200+
self.assertEqual("High", finding.severity)
201201
self.assertEqual(finding.file_path, "requirements.txt")
202202

203203
def test_issue_9170(self):
@@ -300,3 +300,21 @@ def test_all_statuses(self):
300300
self.assertEqual(False, finding.false_p)
301301
self.assertEqual(False, finding.out_of_scope)
302302
self.assertEqual(False, finding.is_mitigated)
303+
304+
def test_cvss_severity_sources(self):
305+
"""Testing with two findings - one where SeveritySource matches the CVSS entry, and one that does not"""
306+
with sample_path("cvss_severity_source.json").open(encoding="utf-8") as test_file:
307+
parser = TrivyParser()
308+
findings = parser.get_findings(test_file, Test())
309+
self.assertEqual(len(findings), 2)
310+
with self.subTest("SeveritySource matches the CVSS entry"):
311+
finding = findings[0]
312+
self.assertEqual("Medium", finding.severity)
313+
self.assertEqual("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N", finding.cvssv3)
314+
self.assertEqual(6.5, finding.cvssv3_score)
315+
316+
with self.subTest("SeveritySource does not match the CVSS entry"):
317+
finding = findings[1]
318+
self.assertEqual("High", finding.severity)
319+
self.assertEqual("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N", finding.cvssv3)
320+
self.assertEqual(7.5, finding.cvssv3_score)

0 commit comments

Comments
 (0)