From 8fef8defaa89c94b4d8ccf664f56a3b2db7c39b4 Mon Sep 17 00:00:00 2001 From: ziad hany Date: Thu, 14 Aug 2025 05:05:21 +0300 Subject: [PATCH] Add CVSSv4 scoring support to OSV parser Signed-off-by: ziad hany --- vulnerabilities/importers/osv.py | 16 +- .../github_osv/github_osv_expected_8.json | 226 ++++++++++++++++++ .../github_osv/github_osv_test_8.json | 89 +++++++ vulnerabilities/tests/test_github_osv.py | 13 + 4 files changed, 339 insertions(+), 5 deletions(-) create mode 100644 vulnerabilities/tests/test_data/github_osv/github_osv_expected_8.json create mode 100644 vulnerabilities/tests/test_data/github_osv/github_osv_test_8.json diff --git a/vulnerabilities/importers/osv.py b/vulnerabilities/importers/osv.py index 8bd4ad274..cc70ea843 100644 --- a/vulnerabilities/importers/osv.py +++ b/vulnerabilities/importers/osv.py @@ -15,6 +15,7 @@ import dateparser from cvss.exceptions import CVSS3MalformedError +from cvss.exceptions import CVSS4MalformedError from packageurl import PackageURL from univers.version_range import RANGE_CLASS_BY_SCHEMES from univers.versions import InvalidVersion @@ -218,19 +219,24 @@ def get_severities(raw_data) -> Iterable[VulnerabilitySeverity]: """ try: for severity in raw_data.get("severity") or []: + vector = severity.get("score") + valid_vector = vector[:-1] if vector and vector.endswith("/") else vector + if severity.get("type") == "CVSS_V3": - vector = severity.get("score") - # remove the / from the end of the vector if / exist - valid_vector = vector[:-1] if vector and vector[-1] == "/" else vector system = SCORING_SYSTEMS["cvssv3.1"] score = system.compute(valid_vector) yield VulnerabilitySeverity(system=system, value=score, scoring_elements=vector) + elif severity.get("type") == "CVSS_V4": + system = SCORING_SYSTEMS["cvssv4"] + score = system.compute(valid_vector) + yield VulnerabilitySeverity(system=system, value=score, scoring_elements=vector) + else: logger.error( - f"Unsupported severity type: {severity!r} for OSV id: {raw_data['id']!r}" + f"Unsupported severity type: {severity!r} for OSV id: {raw_data.get('id')!r}" ) - except CVSS3MalformedError as e: + except (CVSS3MalformedError, CVSS4MalformedError) as e: logger.error(f"Invalid severity {e}") ecosystem_specific = raw_data.get("ecosystem_specific") or {} diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_8.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_8.json new file mode 100644 index 000000000..057a88d01 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_8.json @@ -0,0 +1,226 @@ +{ + "aliases": [ + "CVE-2016-4009", + "GHSA-hvr8-466p-75rh" + ], + "summary": "Pillow Integer overflow in ImagingResampleHorizontal\nInteger overflow in the `ImagingResampleHorizontal` function in `libImaging/Resample.c` in Pillow before 3.1.1 allows remote attackers to have unspecified impact via negative values of the new size, which triggers a heap-based buffer overflow.", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": "", + "name": "pillow", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": null, + "fixed_version": "3.1.1" + } + ], + "references": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2016-4009", + "severities": [ + { + "system": "cvssv3.1", + "value": "9.8", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + }, + { + "system": "cvssv4", + "value": "9.3", + "scoring_elements": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "system": "generic_textual", + "value": "CRITICAL", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://github.com/python-pillow/Pillow/pull/1714", + "severities": [ + { + "system": "cvssv3.1", + "value": "9.8", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + }, + { + "system": "cvssv4", + "value": "9.3", + "scoring_elements": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "system": "generic_textual", + "value": "CRITICAL", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://github.com/python-pillow/Pillow/commit/4e0d9b0b9740d258ade40cce248c93777362ac1e", + "severities": [ + { + "system": "cvssv3.1", + "value": "9.8", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + }, + { + "system": "cvssv4", + "value": "9.3", + "scoring_elements": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "system": "generic_textual", + "value": "CRITICAL", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://github.com/advisories/GHSA-hvr8-466p-75rh", + "severities": [ + { + "system": "cvssv3.1", + "value": "9.8", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + }, + { + "system": "cvssv4", + "value": "9.3", + "scoring_elements": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "system": "generic_textual", + "value": "CRITICAL", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://github.com/pypa/advisory-database/tree/main/vulns/pillow/PYSEC-2016-7.yaml", + "severities": [ + { + "system": "cvssv3.1", + "value": "9.8", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + }, + { + "system": "cvssv4", + "value": "9.3", + "scoring_elements": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "system": "generic_textual", + "value": "CRITICAL", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://github.com/python-pillow/Pillow", + "severities": [ + { + "system": "cvssv3.1", + "value": "9.8", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + }, + { + "system": "cvssv4", + "value": "9.3", + "scoring_elements": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "system": "generic_textual", + "value": "CRITICAL", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://github.com/python-pillow/Pillow/blob/c3cb690fed5d4bf0c45576759de55d054916c165/CHANGES.rst", + "severities": [ + { + "system": "cvssv3.1", + "value": "9.8", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + }, + { + "system": "cvssv4", + "value": "9.3", + "scoring_elements": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "system": "generic_textual", + "value": "CRITICAL", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://security.gentoo.org/glsa/201612-52", + "severities": [ + { + "system": "cvssv3.1", + "value": "9.8", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + }, + { + "system": "cvssv4", + "value": "9.3", + "scoring_elements": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "system": "generic_textual", + "value": "CRITICAL", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "reference_type": "", + "url": "http://www.securityfocus.com/bid/86064", + "severities": [ + { + "system": "cvssv3.1", + "value": "9.8", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + }, + { + "system": "cvssv4", + "value": "9.3", + "scoring_elements": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + }, + { + "system": "generic_textual", + "value": "CRITICAL", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2018-07-24T20:15:48+00:00", + "weaknesses": [ + 119 + ], + "url": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/github_osv_test_8.json" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_8.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_8.json new file mode 100644 index 000000000..d89ef71a4 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_8.json @@ -0,0 +1,89 @@ +{ + "schema_version": "1.4.0", + "id": "GHSA-hvr8-466p-75rh", + "modified": "2024-10-08T13:06:58Z", + "published": "2018-07-24T20:15:48Z", + "aliases": [ + "CVE-2016-4009" + ], + "summary": "Pillow Integer overflow in ImagingResampleHorizontal", + "details": "Integer overflow in the `ImagingResampleHorizontal` function in `libImaging/Resample.c` in Pillow before 3.1.1 allows remote attackers to have unspecified impact via negative values of the new size, which triggers a heap-based buffer overflow.", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H" + }, + { + "type": "CVSS_V4", + "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N" + } + ], + "affected": [ + { + "package": { + "ecosystem": "PyPI", + "name": "pillow" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "3.1.1" + } + ] + } + ] + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2016-4009" + }, + { + "type": "WEB", + "url": "https://github.com/python-pillow/Pillow/pull/1714" + }, + { + "type": "WEB", + "url": "https://github.com/python-pillow/Pillow/commit/4e0d9b0b9740d258ade40cce248c93777362ac1e" + }, + { + "type": "ADVISORY", + "url": "https://github.com/advisories/GHSA-hvr8-466p-75rh" + }, + { + "type": "WEB", + "url": "https://github.com/pypa/advisory-database/tree/main/vulns/pillow/PYSEC-2016-7.yaml" + }, + { + "type": "PACKAGE", + "url": "https://github.com/python-pillow/Pillow" + }, + { + "type": "WEB", + "url": "https://github.com/python-pillow/Pillow/blob/c3cb690fed5d4bf0c45576759de55d054916c165/CHANGES.rst" + }, + { + "type": "WEB", + "url": "https://security.gentoo.org/glsa/201612-52" + }, + { + "type": "WEB", + "url": "http://www.securityfocus.com/bid/86064" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-119" + ], + "severity": "CRITICAL", + "github_reviewed": true, + "github_reviewed_at": "2020-06-16T21:41:06Z", + "nvd_published_at": null + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_github_osv.py b/vulnerabilities/tests/test_github_osv.py index bcb5fdda5..081cfa0e1 100644 --- a/vulnerabilities/tests/test_github_osv.py +++ b/vulnerabilities/tests/test_github_osv.py @@ -111,3 +111,16 @@ def test_github_osv_importer7(self): ) result = imported_data.to_dict() util_tests.check_results_against_json(result, expected_file) + + def test_github_osv_importer8(self): + with open(os.path.join(TEST_DATA, "github_osv_test_8.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_8.json") + imported_data = parse_advisory_data( + mock_response, + supported_ecosystems=["pypi"], + advisory_url="https://github.com/github/advisory-database" + "/blob/main/advisories/github-reviewed/github_osv_test_8.json", + ) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file)