From 92400730557dce4c6a95dc48001592706b6fcb5a Mon Sep 17 00:00:00 2001 From: Manuel Sommer Date: Wed, 19 Nov 2025 12:13:55 +0100 Subject: [PATCH 1/4] :bug: fix severity order of trivy #13647 --- dojo/tools/trivy/parser.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/dojo/tools/trivy/parser.py b/dojo/tools/trivy/parser.py index ea01d38b6db..17d2020bda4 100644 --- a/dojo/tools/trivy/parser.py +++ b/dojo/tools/trivy/parser.py @@ -255,26 +255,30 @@ def get_result_items(self, test, results, service_name=None, artifact_name=""): cvssclass = None cvssv3 = None cvssv3_score = None - # Iterate over the possible severity sources tom find the first match - for severity_source in [detected_severity_source, *CVSS_SEVERITY_SOURCES]: - cvssclass = cvss.get(severity_source, None) + # Use severity field if present, otherwise fallback to CVSS + if vuln.get("Severity"): + severity = TRIVY_SEVERITIES[vuln["Severity"]] + else: + # Iterate over the possible severity sources to find the first match + for severity_source in [detected_severity_source, *CVSS_SEVERITY_SOURCES]: + cvssclass = cvss.get(severity_source, None) + if cvssclass is not None: + break + # Parse the CVSS class if it is not None if cvssclass is not None: - break - # Parse the CVSS class if it is not None - if cvssclass is not None: - if cvss_data := parse_cvss_data(cvssclass.get("V3Vector", "")): - cvssv3 = cvss_data.get("cvssv3") - cvssv3_score = cvss_data.get("cvssv3_score") - severity = cvss_data.get("severity") - elif (cvss_v3_score := cvssclass.get("V3Score")) is not None: - cvssv3_score = cvss_v3_score - severity = self.convert_cvss_score(cvss_v3_score) - elif (cvss_v2_score := cvssclass.get("V2Score")) is not None: - severity = self.convert_cvss_score(cvss_v2_score) + if cvss_data := parse_cvss_data(cvssclass.get("V3Vector", "")): + cvssv3 = cvss_data.get("cvssv3") + cvssv3_score = cvss_data.get("cvssv3_score") + severity = cvss_data.get("severity") + elif (cvss_v3_score := cvssclass.get("V3Score")) is not None: + cvssv3_score = cvss_v3_score + severity = self.convert_cvss_score(cvss_v3_score) + elif (cvss_v2_score := cvssclass.get("V2Score")) is not None: + severity = self.convert_cvss_score(cvss_v2_score) + else: + severity = self.convert_cvss_score(None) else: severity = self.convert_cvss_score(None) - else: - severity = TRIVY_SEVERITIES[vuln["Severity"]] if target_class in {"os-pkgs", "lang-pkgs"}: file_path = vuln.get("PkgPath") if file_path is None: From 90b9fd7fecf2e13b7ac26bcd33461a9d3656f93a Mon Sep 17 00:00:00 2001 From: Manuel Sommer Date: Wed, 19 Nov 2025 12:22:55 +0100 Subject: [PATCH 2/4] udpate, fix unittests --- dojo/tools/trivy/parser.py | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/dojo/tools/trivy/parser.py b/dojo/tools/trivy/parser.py index 17d2020bda4..184aa88c2ac 100644 --- a/dojo/tools/trivy/parser.py +++ b/dojo/tools/trivy/parser.py @@ -255,30 +255,28 @@ def get_result_items(self, test, results, service_name=None, artifact_name=""): cvssclass = None cvssv3 = None cvssv3_score = None - # Use severity field if present, otherwise fallback to CVSS - if vuln.get("Severity"): - severity = TRIVY_SEVERITIES[vuln["Severity"]] - else: - # Iterate over the possible severity sources to find the first match - for severity_source in [detected_severity_source, *CVSS_SEVERITY_SOURCES]: - cvssclass = cvss.get(severity_source, None) - if cvssclass is not None: - break - # Parse the CVSS class if it is not None + severity = TRIVY_SEVERITIES[vuln["Severity"]] if vuln.get("Severity") else None + # Iterate over the possible severity sources tom find the first match + for severity_source in [detected_severity_source, *CVSS_SEVERITY_SOURCES]: + cvssclass = cvss.get(severity_source, None) if cvssclass is not None: - if cvss_data := parse_cvss_data(cvssclass.get("V3Vector", "")): - cvssv3 = cvss_data.get("cvssv3") - cvssv3_score = cvss_data.get("cvssv3_score") + break + # Parse the CVSS class if it is not None + if cvssclass is not None: + if cvss_data := parse_cvss_data(cvssclass.get("V3Vector", "")): + cvssv3 = cvss_data.get("cvssv3") + cvssv3_score = cvss_data.get("cvssv3_score") + if severity is None: severity = cvss_data.get("severity") - elif (cvss_v3_score := cvssclass.get("V3Score")) is not None: - cvssv3_score = cvss_v3_score + elif (cvss_v3_score := cvssclass.get("V3Score")) is not None: + cvssv3_score = cvss_v3_score + if severity is None: severity = self.convert_cvss_score(cvss_v3_score) - elif (cvss_v2_score := cvssclass.get("V2Score")) is not None: + elif (cvss_v2_score := cvssclass.get("V2Score")) is not None: + if severity is None: severity = self.convert_cvss_score(cvss_v2_score) - else: - severity = self.convert_cvss_score(None) - else: - severity = self.convert_cvss_score(None) + if severity is None: + severity = self.convert_cvss_score(None) if target_class in {"os-pkgs", "lang-pkgs"}: file_path = vuln.get("PkgPath") if file_path is None: From f701688ee933b4ed2a239724a39178e065df2fae Mon Sep 17 00:00:00 2001 From: Manuel Sommer Date: Wed, 19 Nov 2025 13:38:02 +0100 Subject: [PATCH 3/4] update --- unittests/scans/trivy/severity_prio.json | 148 +++++++++++++++++++++++ unittests/tools/test_trivy_parser.py | 17 +++ 2 files changed, 165 insertions(+) create mode 100644 unittests/scans/trivy/severity_prio.json diff --git a/unittests/scans/trivy/severity_prio.json b/unittests/scans/trivy/severity_prio.json new file mode 100644 index 00000000000..07f4d22d218 --- /dev/null +++ b/unittests/scans/trivy/severity_prio.json @@ -0,0 +1,148 @@ +{ + "SchemaVersion": 2, + "ArtifactName": "sbom.json", + "ArtifactType": "cyclonedx", + "Metadata": { + "ImageConfig": { + "architecture": "", + "created": "0001-01-01T00:00:00Z", + "os": "", + "rootfs": { + "type": "", + "diff_ids": null + }, + "config": {} + } + }, + "Results": [ + { + "Target": "requirements.txt", + "Class": "lang-pkgs", + "Type": "pip", + "Vulnerabilities": [ + { + "VulnerabilityID": "CVE-2023-46218", + "PkgID": "curl@7.81.0-1ubuntu1.14", + "PkgName": "curl", + "PkgIdentifier": { + "PURL": "pkg:deb/ubuntu/curl@7.81.0-1ubuntu1.14?arch=amd64\u0026distro=ubuntu-22.04", + "UID": "43a41104920d137" + }, + "InstalledVersion": "7.81.0-1ubuntu1.14", + "FixedVersion": "7.81.0-1ubuntu1.15", + "Status": "fixed", + "Layer": { + "DiffID": "sha256:3a9073a4d18e5ed2ae6f9fd9fee81ea43774907ce603ba955bba8fc0819aa250" + }, + "SeveritySource": "ubuntu", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2023-46218", + "DataSource": { + "ID": "ubuntu", + "Name": "Ubuntu CVE Tracker", + "URL": "https://git.launchpad.net/ubuntu-cve-tracker" + }, + "Title": "curl: information disclosure by exploiting a mixed case flaw", + "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.", + "Severity": "LOW", + "VendorSeverity": { + "alma": 2, + "amazon": 2, + "azure": 2, + "cbl-mariner": 2, + "nvd": 2, + "oracle-oval": 2, + "photon": 2, + "redhat": 2, + "rocky": 2, + "ubuntu": 2 + }, + "CVSS": { + "nvd": { + "V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N", + "V3Score": 6.5 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N", + "V3Score": 5.3 + } + }, + "References": [ + "https://access.redhat.com/errata/RHSA-2024:1129", + "https://access.redhat.com/security/cve/CVE-2023-46218", + "https://bugzilla.redhat.com/2252030", + "https://bugzilla.redhat.com/show_bug.cgi?id=2196793", + "https://bugzilla.redhat.com/show_bug.cgi?id=2240033", + "https://bugzilla.redhat.com/show_bug.cgi?id=2241938", + "https://bugzilla.redhat.com/show_bug.cgi?id=2252030", + "https://curl.se/docs/CVE-2023-46218.html", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-28322", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-38546", + "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-46218", + "https://errata.almalinux.org/9/ALSA-2024-1129.html", + "https://errata.rockylinux.org/RLSA-2024:1601", + "https://hackerone.com/reports/2212193", + "https://linux.oracle.com/cve/CVE-2023-46218.html", + "https://linux.oracle.com/errata/ELSA-2024-1601.html", + "https://lists.debian.org/debian-lts-announce/2023/12/msg00015.html", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/3ZX3VW67N4ACRAPMV2QS2LVYGD7H2MVE/", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/UOGXU25FMMT2X6UUITQ7EZZYMJ42YWWD/", + "https://nvd.nist.gov/vuln/detail/CVE-2023-46218", + "https://security.netapp.com/advisory/ntap-20240125-0007/", + "https://ubuntu.com/security/notices/USN-6535-1", + "https://ubuntu.com/security/notices/USN-6641-1", + "https://www.cve.org/CVERecord?id=CVE-2023-46218", + "https://www.debian.org/security/2023/dsa-5587" + ], + "PublishedDate": "2023-12-07T01:15:07.16Z", + "LastModifiedDate": "2025-02-13T18:15:33.843Z" + }, + { + "VulnerabilityID": "CVE-2023-37920", + "PkgName": "certifi", + "InstalledVersion": "2022.5.18.1", + "FixedVersion": "2023.7.22", + "Layer": {}, + "SeveritySource": "ghsa", + "PrimaryURL": "https://avd.aquasec.com/nvd/cve-2023-37920", + "Ref": "pkg:pypi/certifi@2022.5.18.1", + "DataSource": { + "ID": "ghsa", + "Name": "GitHub Security Advisory pip", + "URL": "https://github.com/advisories?query=type%3Areviewed+ecosystem%3Apip" + }, + "Title": "python-certifi: Removal of e-Tugra root certificate", + "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.", + "Severity": "CRITICAL", + "CweIDs": ["CWE-345"], + "CVSS": { + "ghsa": { + "V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N", + "V3Score": 7.5 + }, + "nvd": { + "V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + "V3Score": 9.8 + }, + "redhat": { + "V3Vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N", + "V3Score": 7.5 + } + }, + "References": [ + "https://access.redhat.com/security/cve/CVE-2023-37920", + "https://github.com/certifi/python-certifi", + "https://github.com/certifi/python-certifi/commit/8fb96ed81f71e7097ed11bc4d9b19afd7ea5c909", + "https://github.com/certifi/python-certifi/security/advisories/GHSA-xqr8-7jwr-rhp7", + "https://github.com/pypa/advisory-database/tree/main/vulns/certifi/PYSEC-2023-135.yaml", + "https://groups.google.com/a/mozilla.org/g/dev-security-policy/c/C-HrP1SEq1A", + "https://lists.fedoraproject.org/archives/list/package-announce@lists.fedoraproject.org/message/5EX6NG7WUFNUKGFHLM35KHHU3GAKXRTG/", + "https://nvd.nist.gov/vuln/detail/CVE-2023-37920", + "https://www.cve.org/CVERecord?id=CVE-2023-37920" + ], + "PublishedDate": "2023-07-25T21:15:00Z", + "LastModifiedDate": "2023-08-12T06:16:00Z" + } + ] + } + ] +} diff --git a/unittests/tools/test_trivy_parser.py b/unittests/tools/test_trivy_parser.py index 0ae377d33e6..7465c237ec5 100644 --- a/unittests/tools/test_trivy_parser.py +++ b/unittests/tools/test_trivy_parser.py @@ -319,3 +319,20 @@ def test_cvss_severity_sources(self): self.assertEqual("High", finding.severity) self.assertEqual("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N", finding.cvssv3) self.assertEqual(7.5, finding.cvssv3_score) + + def test_severity_prio(self): + with sample_path("severity_prio.json").open(encoding="utf-8") as test_file: + parser = TrivyParser() + findings = parser.get_findings(test_file, Test()) + self.assertEqual(len(findings), 2) + with self.subTest("SeveritySource matches the CVSS entry"): + finding = findings[0] + self.assertEqual("Low", finding.severity) + self.assertEqual("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N", finding.cvssv3) + self.assertEqual(6.5, finding.cvssv3_score) + + with self.subTest("SeveritySource does not match the CVSS entry"): + finding = findings[1] + self.assertEqual("Critical", finding.severity) + self.assertEqual("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N", finding.cvssv3) + self.assertEqual(7.5, finding.cvssv3_score) From 6adb0447ac01c9583e8fd77ed82b094a6c92d97c Mon Sep 17 00:00:00 2001 From: Manuel Sommer Date: Wed, 19 Nov 2025 13:41:39 +0100 Subject: [PATCH 4/4] update --- unittests/tools/test_trivy_parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/unittests/tools/test_trivy_parser.py b/unittests/tools/test_trivy_parser.py index 7465c237ec5..f6c08156fee 100644 --- a/unittests/tools/test_trivy_parser.py +++ b/unittests/tools/test_trivy_parser.py @@ -321,6 +321,7 @@ def test_cvss_severity_sources(self): self.assertEqual(7.5, finding.cvssv3_score) def test_severity_prio(self): + # this tests issue #13647. The unittest file is just a copy of cvss_severity_source.json with edited severities with sample_path("severity_prio.json").open(encoding="utf-8") as test_file: parser = TrivyParser() findings = parser.get_findings(test_file, Test())