From 4064dbd785c88efbbc2f20c01d4e1b3491c7ced0 Mon Sep 17 00:00:00 2001 From: ziadhany Date: Wed, 21 Sep 2022 15:04:30 +0200 Subject: [PATCH] Add support for nuget , hex , gem Add support for maven , go and packagist ecosystem. Import data from github advisory-database using osv format and add support for all osv ecosystems Signed-off-by: ziadhany --- vulnerabilities/importers/__init__.py | 2 + vulnerabilities/importers/github_osv.py | 55 +++++++ vulnerabilities/importers/osv.py | 61 +++++--- vulnerabilities/importers/pypa.py | 2 +- vulnerabilities/importers/pysec.py | 2 +- .../github_osv/github_osv_expected_1.json | 120 +++++++++++++++ .../github_osv/github_osv_expected_2.json | 77 ++++++++++ .../github_osv/github_osv_expected_3.json | 140 ++++++++++++++++++ .../github_osv/github_osv_test_1.json | 71 +++++++++ .../github_osv/github_osv_test_2.json | 63 ++++++++ .../github_osv/github_osv_test_3.json | 136 +++++++++++++++++ vulnerabilities/tests/test_github_osv.py | 44 ++++++ vulnerabilities/tests/test_osv.py | 22 ++- vulnerabilities/tests/test_pypa.py | 4 +- 14 files changed, 772 insertions(+), 27 deletions(-) create mode 100644 vulnerabilities/importers/github_osv.py create mode 100644 vulnerabilities/tests/test_data/github_osv/github_osv_expected_1.json create mode 100644 vulnerabilities/tests/test_data/github_osv/github_osv_expected_2.json create mode 100644 vulnerabilities/tests/test_data/github_osv/github_osv_expected_3.json create mode 100644 vulnerabilities/tests/test_data/github_osv/github_osv_test_1.json create mode 100644 vulnerabilities/tests/test_data/github_osv/github_osv_test_2.json create mode 100644 vulnerabilities/tests/test_data/github_osv/github_osv_test_3.json create mode 100644 vulnerabilities/tests/test_github_osv.py diff --git a/vulnerabilities/importers/__init__.py b/vulnerabilities/importers/__init__.py index 5f4957421..1bea975ca 100644 --- a/vulnerabilities/importers/__init__.py +++ b/vulnerabilities/importers/__init__.py @@ -14,6 +14,7 @@ from vulnerabilities.importers import debian_oval from vulnerabilities.importers import gentoo from vulnerabilities.importers import github +from vulnerabilities.importers import github_osv from vulnerabilities.importers import gitlab from vulnerabilities.importers import istio from vulnerabilities.importers import mozilla @@ -49,6 +50,7 @@ mozilla.MozillaImporter, gentoo.GentooImporter, istio.IstioImporter, + github_osv.GithubOSVImporter, ] IMPORTERS_REGISTRY = {x.qualified_name: x for x in IMPORTERS_REGISTRY} diff --git a/vulnerabilities/importers/github_osv.py b/vulnerabilities/importers/github_osv.py new file mode 100644 index 000000000..5007d134e --- /dev/null +++ b/vulnerabilities/importers/github_osv.py @@ -0,0 +1,55 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/nexB/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# +import json +import logging +from io import BytesIO +from pathlib import Path +from typing import Iterable +from zipfile import ZipFile + +import requests + +from vulnerabilities.importer import AdvisoryData +from vulnerabilities.importer import Importer +from vulnerabilities.importers.osv import parse_advisory_data + +logger = logging.getLogger(__name__) + + +class GithubOSVImporter(Importer): + license_url = "https://github.com/github/advisory-database/blob/main/LICENSE.md" + spdx_license_expression = "CC-BY-4.0" + url = "https://codeload.github.com/github/advisory-database/zip/refs/heads/main" + + def advisory_data(self) -> Iterable[AdvisoryData]: + response = requests.get(self.url).content + with ZipFile(BytesIO(response)) as zip_file: + for file_name in filter( + lambda filename_list: ("github-reviewed" in Path(filename_list).parts) + and Path(filename_list).suffix == ".json", + zip_file.namelist(), + ): + with zip_file.open(file_name) as f: + raw_data = json.load(f) + try: + yield parse_advisory_data( + raw_data, + supported_ecosystems=[ + "pypi", + "npm", + "maven", + "go", + "packagist", + "hex", + "gem", + "nuget", + ], + ) + except Exception as e: + logger.error(f"Invalid file name: {file_name} - {e}") diff --git a/vulnerabilities/importers/osv.py b/vulnerabilities/importers/osv.py index c4ee58685..6db70e5ec 100644 --- a/vulnerabilities/importers/osv.py +++ b/vulnerabilities/importers/osv.py @@ -16,7 +16,6 @@ from packageurl import PackageURL from univers.version_range import RANGE_CLASS_BY_SCHEMES from univers.versions import InvalidVersion -from univers.versions import PypiVersion from univers.versions import SemverVersion from univers.versions import Version @@ -31,7 +30,7 @@ logger = logging.getLogger(__name__) -def parse_advisory_data(raw_data: dict, supported_ecosystem) -> Optional[AdvisoryData]: +def parse_advisory_data(raw_data: dict, supported_ecosystems: List) -> Optional[AdvisoryData]: """ Return an AdvisoryData build from a ``raw_data`` mapping of OSV advisory and a ``supported_ecosystem`` string. @@ -53,18 +52,20 @@ def parse_advisory_data(raw_data: dict, supported_ecosystem) -> Optional[Advisor for affected_pkg in raw_data.get("affected") or []: purl = get_affected_purl(affected_pkg=affected_pkg, raw_id=raw_id) - if purl.type != supported_ecosystem: + if purl.type not in supported_ecosystems: logger.error(f"Unsupported package type: {purl!r} in OSV: {raw_id!r}") continue affected_version_range = get_affected_version_range( affected_pkg=affected_pkg, raw_id=raw_id, - supported_ecosystem=supported_ecosystem, + supported_ecosystem=purl.type, ) for fixed_range in affected_pkg.get("ranges") or []: - fixed_version = get_fixed_versions(fixed_range=fixed_range, raw_id=raw_id) + fixed_version = get_fixed_versions( + fixed_range=fixed_range, raw_id=raw_id, supported_ecosystem=purl.type + ) for version in fixed_version: affected_packages.append( @@ -74,7 +75,6 @@ def parse_advisory_data(raw_data: dict, supported_ecosystem) -> Optional[Advisor fixed_version=version, ) ) - return AdvisoryData( aliases=aliases, summary=summary, @@ -113,14 +113,19 @@ def get_severities(raw_data) -> Iterable[VulnerabilitySeverity]: """ Yield VulnerabilitySeverity extracted from a mapping of OSV ``raw_data`` """ - for severity in raw_data.get("severity") or []: - if severity.get("type") == "CVSS_V3": - vector = severity["score"] - system = SCORING_SYSTEMS["cvssv3.1"] - score = system.compute(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}") + try: + for severity in raw_data.get("severity") or []: + if severity.get("type") == "CVSS_V3": + vector = severity["score"] + system = SCORING_SYSTEMS["cvssv3.1"] + score = system.compute(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}" + ) + except Exception as e: + logger.error(f"Invalid severity {e}") ecosystem_specific = raw_data.get("ecosystem_specific") or {} severity = ecosystem_specific.get("severity") @@ -199,18 +204,19 @@ def get_affected_version_range(affected_pkg, raw_id, supported_ecosystem): ) -def get_fixed_versions(fixed_range, raw_id) -> List[Version]: +def get_fixed_versions(fixed_range, raw_id, supported_ecosystem) -> List[Version]: """ Return a list of unique fixed univers Versions given a ``fixed_range`` univers VersionRange and a ``raw_id``. For example:: - >>> get_fixed_versions(fixed_range={}, raw_id="GHSA-j3f7-7rmc-6wqj") + >>> get_fixed_versions(fixed_range={}, raw_id="GHSA-j3f7-7rmc-6wqj", supported_ecosystem="pypi",) [] >>> get_fixed_versions( ... fixed_range={"type": "ECOSYSTEM", "events": [{"fixed": "1.7.0"}]}, - ... raw_id="GHSA-j3f7-7rmc-6wqj" + ... raw_id="GHSA-j3f7-7rmc-6wqj", + ... supported_ecosystem="pypi", ... ) [PypiVersion(string='1.7.0')] """ @@ -221,21 +227,32 @@ def get_fixed_versions(fixed_range, raw_id) -> List[Version]: fixed_range_type = fixed_range["type"] - for version in extract_fixed_versions(fixed_range): + version_class = None + try: + version_class = RANGE_CLASS_BY_SCHEMES[supported_ecosystem].version_class + except KeyError: + logger.error( + f"Unknown version range for ecosystem {supported_ecosystem} for OSV id: {raw_id!r}" + ) - # FIXME: ECOSYSTEM does not imply PyPI!!!! + for version in extract_fixed_versions(fixed_range): if fixed_range_type == "ECOSYSTEM": try: - fixed_versions.append(PypiVersion(version)) + if not version_class: + raise InvalidVersion( + f"Unsupported version for ecosystem: {supported_ecosystem}" + ) + fixed_versions.append(version_class(version)) except InvalidVersion: - logger.error(f"Invalid PypiVersion: {version!r} for OSV id: {raw_id!r}") + logger.error( + f"Invalid version class: {version_class} - {version!r} for OSV id: {raw_id!r}" + ) elif fixed_range_type == "SEMVER": try: fixed_versions.append(SemverVersion(version)) except InvalidVersion: logger.error(f"Invalid SemverVersion: {version!r} for OSV id: {raw_id!r}") - else: logger.error(f"Unsupported fixed version type: {version!r} for OSV id: {raw_id!r}") diff --git a/vulnerabilities/importers/pypa.py b/vulnerabilities/importers/pypa.py index 6ffbaae9f..c2c6bbaa6 100644 --- a/vulnerabilities/importers/pypa.py +++ b/vulnerabilities/importers/pypa.py @@ -27,7 +27,7 @@ class PyPaImporter(Importer): def advisory_data(self) -> Iterable[AdvisoryData]: for raw_data in fork_and_get_files(self.url): - yield parse_advisory_data(raw_data=raw_data, supported_ecosystem="pypi") + yield parse_advisory_data(raw_data=raw_data, supported_ecosystems=["pypi"]) class ForkError(Exception): diff --git a/vulnerabilities/importers/pysec.py b/vulnerabilities/importers/pysec.py index 4110c0d9a..e500504af 100644 --- a/vulnerabilities/importers/pysec.py +++ b/vulnerabilities/importers/pysec.py @@ -38,4 +38,4 @@ def advisory_data(self) -> Iterable[AdvisoryData]: continue with zip_file.open(file_name) as f: vul_info = json.load(f) - yield parse_advisory_data(raw_data=vul_info, supported_ecosystem="pypi") + yield parse_advisory_data(raw_data=vul_info, supported_ecosystems=["pypi"]) diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_1.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_1.json new file mode 100644 index 000000000..f481a6fd1 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_1.json @@ -0,0 +1,120 @@ +{ + "aliases": [ + "CVE-2015-8315", + "GHSA-3fx5-fwvr-xrjg" + ], + "summary": "Regular Expression Denial of Service in ms\nVersions of `ms` prior to 0.7.1 are affected by a regular expression denial of service vulnerability when extremely long version strings are parsed.\n\n## Proof of Concept\n```javascript\nvar ms = require('ms');\nvar genstr = function (len, chr) {\n var result = \"\";\n for (i=0; i<=len; i++) {\n result = result + chr;\n }\n\n return result;\n}\n\nms(genstr(process.argv[2], \"5\") + \" minutea\");\n\n```\n\n### Results\nShowing increase in execution time based on the input string.\n```\n$ time node ms.js 10000\n\nreal\t0m0.758s\nuser\t0m0.724s\nsys\t0m0.031s\n\n$ time node ms.js 20000\n\nreal\t0m2.580s\nuser\t0m2.494s\nsys\t0m0.047s\n\n$ time node ms.js 30000\n\nreal\t0m5.747s\nuser\t0m5.483s\nsys\t0m0.080s\n\n$ time node ms.js 80000\n\nreal\t0m41.022s\nuser\t0m38.894s\nsys\t0m0.529s\n```", + "affected_packages": [ + { + "package": { + "type": "npm", + "namespace": null, + "name": "ms", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "0.7.1" + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2015-8315", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/unshiftio/millisecond/", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://support.f5.com/csp/article/K46337613?utm_source=f5support&utm_medium=RSS", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://www.npmjs.com/advisories/46", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://www.openwall.com/lists/oss-security/2016/04/20/11", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "http://www.securityfocus.com/bid/96389", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2017-10-24T18:33:36+00:00" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_2.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_2.json new file mode 100644 index 000000000..2d4e34251 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_2.json @@ -0,0 +1,77 @@ +{ + "aliases": [ + "CVE-2022-1036", + "GHSA-3qr6-qrqm-8v86" + ], + "summary": "Integer Overflow or Wraparound in Microweber\nIn Microweber prior to 1.2.12, a user can create an account with a password thousands of characters in length, leading to memory corruption/integer overflow. Version 1.2.2 sets maximum password length at 500 characters.", + "affected_packages": [ + + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-1036", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/microweber/microweber/commit/82be4f0b4729be870ccefdae99a04833f134aa6a", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/microweber/microweber", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://huntr.dev/bounties/db615581-d5a9-4ca5-a3e9-7a39eceaa424", + "severities": [ + { + "system": "cvssv3.1", + "value": "7.5", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + }, + { + "system": "generic_textual", + "value": "HIGH", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2022-03-23T00:00:23+00:00" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_expected_3.json b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_3.json new file mode 100644 index 000000000..6c4d98092 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_expected_3.json @@ -0,0 +1,140 @@ +{ + "aliases": [ + "CVE-2020-10688", + "GHSA-29qj-rvv6-qrmv" + ], + "summary": "Cross-site scripting in RESTEasy\nA cross-site scripting (XSS) flaw was found in RESTEasy in versions before 3.11.1.Final and before 4.5.3.Final, where it did not properly handle URL encoding when the RESTEASY003870 exception occurs. An attacker could use this flaw to launch a reflected XSS attack.", + "affected_packages": [ + { + "package": { + "type": "maven", + "namespace": null, + "name": "org.jboss.resteasy:resteasy-bom", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "3.11.1.Final" + }, + { + "package": { + "type": "maven", + "namespace": null, + "name": "org.jboss.resteasy:resteasy-bom", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "4.5.3.Final" + }, + { + "package": { + "type": "maven", + "namespace": null, + "name": "org.jboss.resteasy:resteasy-core", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "3.11.1.Final" + }, + { + "package": { + "type": "maven", + "namespace": null, + "name": "org.jboss.resteasy:resteasy-core", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": null, + "fixed_version": "4.5.3.Final" + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-10688", + "severities": [ + { + "system": "cvssv3.1", + "value": "5.4", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/quarkusio/quarkus/issues/7248", + "severities": [ + { + "system": "cvssv3.1", + "value": "5.4", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1814974", + "severities": [ + { + "system": "cvssv3.1", + "value": "5.4", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://issues.redhat.com/browse/RESTEASY-2519", + "severities": [ + { + "system": "cvssv3.1", + "value": "5.4", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://security.netapp.com/advisory/ntap-20210706-0008/", + "severities": [ + { + "system": "cvssv3.1", + "value": "5.4", + "scoring_elements": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + }, + { + "system": "generic_textual", + "value": "MODERATE", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2021-06-15T16:05:22+00:00" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_1.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_1.json new file mode 100644 index 000000000..02583ee61 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_1.json @@ -0,0 +1,71 @@ +{ + "schema_version": "1.3.0", + "id": "GHSA-3fx5-fwvr-xrjg", + "modified": "2021-09-22T20:19:52Z", + "published": "2017-10-24T18:33:36Z", + "aliases": [ + "CVE-2015-8315" + ], + "summary": "Regular Expression Denial of Service in ms", + "details": "Versions of `ms` prior to 0.7.1 are affected by a regular expression denial of service vulnerability when extremely long version strings are parsed.\n\n## Proof of Concept\n```javascript\nvar ms = require('ms');\nvar genstr = function (len, chr) {\n var result = \"\";\n for (i=0; i<=len; i++) {\n result = result + chr;\n }\n\n return result;\n}\n\nms(genstr(process.argv[2], \"5\") + \" minutea\");\n\n```\n\n### Results\nShowing increase in execution time based on the input string.\n```\n$ time node ms.js 10000\n\nreal\t0m0.758s\nuser\t0m0.724s\nsys\t0m0.031s\n\n$ time node ms.js 20000\n\nreal\t0m2.580s\nuser\t0m2.494s\nsys\t0m0.047s\n\n$ time node ms.js 30000\n\nreal\t0m5.747s\nuser\t0m5.483s\nsys\t0m0.080s\n\n$ time node ms.js 80000\n\nreal\t0m41.022s\nuser\t0m38.894s\nsys\t0m0.529s\n```\n", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + } + ], + "affected": [ + { + "package": { + "ecosystem": "npm", + "name": "ms" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "0.7.1" + } + ] + } + ] + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2015-8315" + }, + { + "type": "PACKAGE", + "url": "https://github.com/unshiftio/millisecond/" + }, + { + "type": "WEB", + "url": "https://support.f5.com/csp/article/K46337613?utm_source=f5support&utm_medium=RSS" + }, + { + "type": "WEB", + "url": "https://www.npmjs.com/advisories/46" + }, + { + "type": "WEB", + "url": "http://www.openwall.com/lists/oss-security/2016/04/20/11" + }, + { + "type": "WEB", + "url": "http://www.securityfocus.com/bid/96389" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-400" + ], + "severity": "HIGH", + "github_reviewed": true + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_2.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_2.json new file mode 100644 index 000000000..fef8553f2 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_2.json @@ -0,0 +1,63 @@ +{ + "schema_version": "1.3.0", + "id": "GHSA-3qr6-qrqm-8v86", + "modified": "2022-03-30T20:02:35Z", + "published": "2022-03-23T00:00:23Z", + "aliases": [ + "CVE-2022-1036" + ], + "summary": "Integer Overflow or Wraparound in Microweber", + "details": "In Microweber prior to 1.2.12, a user can create an account with a password thousands of characters in length, leading to memory corruption/integer overflow. Version 1.2.2 sets maximum password length at 500 characters.", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H" + } + ], + "affected": [ + { + "package": { + "ecosystem": "Packagist", + "name": "microweber/microweber" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "1.2.12" + } + ] + } + ] + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-1036" + }, + { + "type": "WEB", + "url": "https://github.com/microweber/microweber/commit/82be4f0b4729be870ccefdae99a04833f134aa6a" + }, + { + "type": "PACKAGE", + "url": "https://github.com/microweber/microweber" + }, + { + "type": "WEB", + "url": "https://huntr.dev/bounties/db615581-d5a9-4ca5-a3e9-7a39eceaa424" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-190" + ], + "severity": "HIGH", + "github_reviewed": true + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_osv/github_osv_test_3.json b/vulnerabilities/tests/test_data/github_osv/github_osv_test_3.json new file mode 100644 index 000000000..f8a3f0f44 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_osv/github_osv_test_3.json @@ -0,0 +1,136 @@ +{ + "schema_version": "1.3.0", + "id": "GHSA-29qj-rvv6-qrmv", + "modified": "2021-06-01T20:09:20Z", + "published": "2021-06-15T16:05:22Z", + "aliases": [ + "CVE-2020-10688" + ], + "summary": "Cross-site scripting in RESTEasy", + "details": "A cross-site scripting (XSS) flaw was found in RESTEasy in versions before 3.11.1.Final and before 4.5.3.Final, where it did not properly handle URL encoding when the RESTEASY003870 exception occurs. An attacker could use this flaw to launch a reflected XSS attack.", + "severity": [ + { + "type": "CVSS_V3", + "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N" + } + ], + "affected": [ + { + "package": { + "ecosystem": "Maven", + "name": "org.jboss.resteasy:resteasy-bom" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "3.11.1.Final" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 3.11.0.Final" + } + }, + { + "package": { + "ecosystem": "Maven", + "name": "org.jboss.resteasy:resteasy-bom" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "4.0.0" + }, + { + "fixed": "4.5.3.Final" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 4.5.2.Final" + } + }, + { + "package": { + "ecosystem": "Maven", + "name": "org.jboss.resteasy:resteasy-core" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "0" + }, + { + "fixed": "3.11.1.Final" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 3.11.0.Final" + } + }, + { + "package": { + "ecosystem": "Maven", + "name": "org.jboss.resteasy:resteasy-core" + }, + "ranges": [ + { + "type": "ECOSYSTEM", + "events": [ + { + "introduced": "4.0.0" + }, + { + "fixed": "4.5.3.Final" + } + ] + } + ], + "database_specific": { + "last_known_affected_version_range": "<= 4.5.2.Final" + } + } + ], + "references": [ + { + "type": "ADVISORY", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2020-10688" + }, + { + "type": "WEB", + "url": "https://github.com/quarkusio/quarkus/issues/7248" + }, + { + "type": "WEB", + "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1814974" + }, + { + "type": "WEB", + "url": "https://issues.redhat.com/browse/RESTEASY-2519" + }, + { + "type": "WEB", + "url": "https://security.netapp.com/advisory/ntap-20210706-0008/" + } + ], + "database_specific": { + "cwe_ids": [ + "CWE-79" + ], + "severity": "MODERATE", + "github_reviewed": true + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_github_osv.py b/vulnerabilities/tests/test_github_osv.py new file mode 100644 index 000000000..703d55939 --- /dev/null +++ b/vulnerabilities/tests/test_github_osv.py @@ -0,0 +1,44 @@ +# +# Copyright (c) nexB Inc. and others. All rights reserved. +# VulnerableCode is a trademark of nexB Inc. +# SPDX-License-Identifier: Apache-2.0 +# See http://www.apache.org/licenses/LICENSE-2.0 for the license text. +# See https://github.com/nexB/vulnerablecode for support or download. +# See https://aboutcode.org for more information about nexB OSS projects. +# +import json +import os +from unittest import TestCase + +from vulnerabilities.importers.osv import parse_advisory_data +from vulnerabilities.tests import util_tests + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_DATA = os.path.join(BASE_DIR, "test_data/github_osv") + + +class GithubOSVImporter(TestCase): + def test_to_advisories1(self): + with open(os.path.join(TEST_DATA, "github_osv_test_1.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_1.json") + imported_data = parse_advisory_data(mock_response, supported_ecosystems=["npm"]) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file) + + def test_to_advisories2(self): + with open(os.path.join(TEST_DATA, "github_osv_test_2.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_2.json") + # if supported_ecosystems = [] : the expected affected_packages = [] + imported_data = parse_advisory_data(mock_response, supported_ecosystems=[]) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file) + + def test_to_advisories3(self): + with open(os.path.join(TEST_DATA, "github_osv_test_3.json")) as f: + mock_response = json.load(f) + expected_file = os.path.join(TEST_DATA, "github_osv_expected_3.json") + imported_data = parse_advisory_data(mock_response, supported_ecosystems=["maven"]) + result = imported_data.to_dict() + util_tests.check_results_against_json(result, expected_file) diff --git a/vulnerabilities/tests/test_osv.py b/vulnerabilities/tests/test_osv.py index 3ca50cebf..5779d0589 100644 --- a/vulnerabilities/tests/test_osv.py +++ b/vulnerabilities/tests/test_osv.py @@ -13,6 +13,7 @@ from univers.version_constraint import VersionConstraint from univers.version_range import PypiVersionRange from univers.versions import PypiVersion +from univers.versions import SemverVersion from vulnerabilities.importer import Reference from vulnerabilities.importer import VulnerabilitySeverity @@ -353,12 +354,18 @@ def test_get_affected_version_range(self): assert results == expected def test_get_fixed_versions1(self): - assert get_fixed_versions(fixed_range={}, raw_id="GHSA-j3f7-7rmc-6wqj") == [] + assert ( + get_fixed_versions( + fixed_range={}, raw_id="GHSA-j3f7-7rmc-6wqj", supported_ecosystem="pypi" + ) + == [] + ) def test_get_fixed_versions2(self): results = get_fixed_versions( fixed_range={"type": "ECOSYSTEM", "events": [{"introduced": "0"}, {"fixed": "1.7.0"}]}, raw_id="GHSA-j3f7-7rmc-6wqj", + supported_ecosystem="pypi", ) assert results == [PypiVersion("1.7.0")] @@ -374,6 +381,19 @@ def test_get_fixed_versions3(self): ], }, raw_id="GHSA-j3f7-7rmc-6wqj", + supported_ecosystem="pypi", ) assert results == [PypiVersion("9.0.0"), PypiVersion("9.0.1")] + + def test_get_fixed_versions4(self): + results = get_fixed_versions( + fixed_range={ + "type": "ECOSYSTEM", + "events": [{"introduced": "0"}, {"fixed": "6.5.4"}], + }, + raw_id="GHSA-r9p9-mrjm-926w", + supported_ecosystem="npm", + ) + + assert results == [SemverVersion("6.5.4")] diff --git a/vulnerabilities/tests/test_pypa.py b/vulnerabilities/tests/test_pypa.py index 64329b9e2..d09f6021a 100644 --- a/vulnerabilities/tests/test_pypa.py +++ b/vulnerabilities/tests/test_pypa.py @@ -22,7 +22,7 @@ class TestPyPaImporter(TestCase): def test_to_advisories_with_summary(self): with open(os.path.join(TEST_DATA, "pypa_test.yaml")) as f: mock_response = saneyaml.load(f) - expected_file = os.path.join(TEST_DATA, f"pypa-expected.json") - imported_data = parse_advisory_data(mock_response, "pypi") + expected_file = os.path.join(TEST_DATA, "pypa-expected.json") + imported_data = parse_advisory_data(mock_response, ["pypi"]) result = imported_data.to_dict() util_tests.check_results_against_json(result, expected_file)