diff --git a/requirements.txt b/requirements.txt index 06369f5c7..5d11627e8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ django-widget-tweaks>=1.4.8 packageurl-python>=0.9.4 binaryornot>=0.4.4 GitPython>=3.1.17 -univers>=30.1.0 +univers>=30.3.1 saneyaml>=0.5.2 beautifulsoup4>=4.9.3 python-dateutil>=2.8.1 diff --git a/vulnerabilities/helpers.py b/vulnerabilities/helpers.py index b43cc0242..d857a72db 100644 --- a/vulnerabilities/helpers.py +++ b/vulnerabilities/helpers.py @@ -23,7 +23,9 @@ import bisect import dataclasses import json +import logging import re +from functools import total_ordering from typing import List from typing import Optional from typing import Tuple @@ -34,8 +36,9 @@ import toml import urllib3 from packageurl import PackageURL +from univers.version_range import RANGE_CLASS_BY_SCHEMES -# TODO add logging here +LOGGER = logging.getLogger(__name__) cve_regex = re.compile(r"CVE-\d{4}-\d{4,7}", re.IGNORECASE) is_cve = cve_regex.match @@ -133,32 +136,36 @@ def requests_with_5xx_retry(max_retries=5, backoff_factor=0.5): return session -def nearest_patched_package( - vulnerable_packages: List[PackageURL], resolved_packages: List[PackageURL] -) -> List[AffectedPackage]: - class PackageURLWithVersionComparator: - """ - This class is used to get around bisect module's lack of supplying custom - compartor. Get rid of this once we use python 3.10 which supports this. - See https://github.com/python/cpython/pull/20556 - """ +@total_ordering +class VersionedPackage: + """ + A PackageURL with a Version class. + This class is used to get around bisect module's lack of supplying custom + comparator. Get rid of this once we use python 3.10 which supports this. + See https://github.com/python/cpython/pull/20556 + """ - def __init__(self, package): - self.package = package - self.version_object = version_class_by_package_type[package.type](package.version) + def __init__(self, purl: PackageURL): + self.purl = purl + vrc = RANGE_CLASS_BY_SCHEMES.get(purl.type) + self.version = vrc.version_class(purl.version) - def __eq__(self, other): - return self.version_object == other.version_object + def __eq__(self, other): + return self.version == other.version - def __lt__(self, other): - return self.version_object < other.version_object + def __lt__(self, other): + return self.version < other.version - vulnerable_packages = sorted( - [PackageURLWithVersionComparator(package) for package in vulnerable_packages] - ) - resolved_packages = sorted( - [PackageURLWithVersionComparator(package) for package in resolved_packages] - ) + +def nearest_patched_package( + vulnerable_packages: List[PackageURL], resolved_packages: List[PackageURL] +) -> List[AffectedPackage]: + """ + Return a list of Affected Packages for each Patched package. + """ + + vulnerable_packages = sorted([VersionedPackage(package) for package in vulnerable_packages]) + resolved_packages = sorted([VersionedPackage(package) for package in resolved_packages]) resolved_package_count = len(resolved_packages) affected_package_with_patched_package_objects = [] @@ -167,11 +174,11 @@ def __lt__(self, other): patched_package_index = bisect.bisect_right(resolved_packages, vulnerable_package) patched_package = None if patched_package_index < resolved_package_count: - patched_package = resolved_packages[patched_package_index].package + patched_package = resolved_packages[patched_package_index] affected_package_with_patched_package_objects.append( AffectedPackage( - vulnerable_package=vulnerable_package.package, patched_package=patched_package + vulnerable_package=vulnerable_package.purl, patched_package=patched_package.purl ) ) @@ -211,3 +218,26 @@ def __init__(self, fget): def __get__(self, owner_self, owner_cls): return self.fget(owner_cls) + + +def get_item(object: dict, *attributes): + """ + Return `item` by going through all the `attributes` present in the `json_object` + + Do a DFS for the `item` in the `json_object` by traversing the `attributes` + and return None if can not traverse through the `attributes` + For example: + >>> get_item({'a': {'b': {'c': 'd'}}}, 'a', 'b', 'c') + 'd' + >>> assert(get_item({'a': {'b': {'c': 'd'}}}, 'a', 'b', 'e')) == None + """ + if not object: + LOGGER.error(f"Object is empty: {object}") + return + item = object + for attribute in attributes: + if attribute not in item: + LOGGER.error(f"Missing attribute {attribute} in {item}") + return None + item = item[attribute] + return item diff --git a/vulnerabilities/importer.py b/vulnerabilities/importer.py index c7408dc48..a9cc7be54 100644 --- a/vulnerabilities/importer.py +++ b/vulnerabilities/importer.py @@ -113,6 +113,18 @@ def from_dict(cls, ref: dict): ) +class UnMergeablePackageError(Exception): + """ + Raised when a package cannot be merged with another one. + """ + + +class NoAffectedPackages(Exception): + """ + Raised when there were no affected packages found. + """ + + @dataclasses.dataclass(order=True, frozen=True) class AffectedPackage: """ @@ -155,18 +167,23 @@ def merge(cls, affected_packages: Iterable): affected_version_range: set(VersionRange) fixed_versions: set(Version) """ - affected_version_ranges = set() - fixed_versions = set() + affected_packages = list(affected_packages) + if not affected_packages: + raise NoAffectedPackages("No affected packages found") + affected_version_ranges = list() + fixed_versions = list() purls = set() for pkg in affected_packages: if pkg.affected_version_range: - affected_version_ranges.add(pkg.affected_version_range) + if pkg.affected_version_range not in affected_version_ranges: + affected_version_ranges.append(pkg.affected_version_range) if pkg.fixed_version: - fixed_versions.add(pkg.fixed_version) + if pkg.fixed_version not in fixed_versions: + fixed_versions.append(pkg.fixed_version) purls.add(pkg.package) if len(purls) > 1: - raise TypeError("Cannot merge with different purls", purls) - return purls.pop(), affected_version_ranges, fixed_versions + raise UnMergeablePackageError("Cannot merge with different purls", purls) + return purls.pop(), sorted(affected_version_ranges), sorted(fixed_versions) def to_dict(self): """ @@ -230,6 +247,15 @@ def __post_init__(self): if self.date_published and not self.date_published.tzinfo: logger.warn(f"AdvisoryData with no tzinfo: {self!r}") + def to_dict(self): + return { + "aliases": self.aliases, + "summary": self.summary, + "affected_packages": [pkg.to_dict() for pkg in self.affected_packages], + "references": [ref.to_dict() for ref in self.references], + "date_published": self.date_published.isoformat() if self.date_published else None, + } + class NoLicenseError(Exception): pass diff --git a/vulnerabilities/importers/__init__.py b/vulnerabilities/importers/__init__.py index d3316316d..b9ccaa512 100644 --- a/vulnerabilities/importers/__init__.py +++ b/vulnerabilities/importers/__init__.py @@ -20,8 +20,9 @@ # VulnerableCode is a free software code scanning tool from nexB Inc. and others. # Visit https://github.com/nexB/vulnerablecode/ for support and download. from vulnerabilities.importers import alpine_linux +from vulnerabilities.importers import github from vulnerabilities.importers import nginx -IMPORTERS_REGISTRY = [nginx.NginxImporter, alpine_linux.AlpineImporter] +IMPORTERS_REGISTRY = [nginx.NginxImporter, alpine_linux.AlpineImporter, github.GitHubAPIImporter] IMPORTERS_REGISTRY = {x.qualified_name: x for x in IMPORTERS_REGISTRY} diff --git a/vulnerabilities/importers/github.py b/vulnerabilities/importers/github.py index 28fc08a8c..f72950aa5 100644 --- a/vulnerabilities/importers/github.py +++ b/vulnerabilities/importers/github.py @@ -20,315 +20,525 @@ # VulnerableCode is a free software from nexB Inc. and others. # Visit https://github.com/nexB/vulnerablecode/ for support and download. -import asyncio -import dataclasses +import logging import os +from datetime import datetime +from typing import Iterable from typing import List from typing import Mapping from typing import Optional -from typing import Set from typing import Tuple import requests from dateutil import parser as dateparser +from django.db.models.query import QuerySet from packageurl import PackageURL -from univers.version_specifier import VersionSpecifier -from univers.versions import version_class_by_package_type +from univers.version_range import VersionRange +from univers.version_range import build_range_from_github_advisory_constraint +from vulnerabilities.helpers import AffectedPackage as LegacyAffectedPackage +from vulnerabilities.helpers import get_item from vulnerabilities.helpers import nearest_patched_package -from vulnerabilities.importer import Advisory +from vulnerabilities.importer import AdvisoryData +from vulnerabilities.importer import AffectedPackage from vulnerabilities.importer import Importer from vulnerabilities.importer import Reference +from vulnerabilities.importer import UnMergeablePackageError from vulnerabilities.importer import VulnerabilitySeverity -from vulnerabilities.package_managers import ComposerVersionAPI -from vulnerabilities.package_managers import GoproxyVersionAPI -from vulnerabilities.package_managers import MavenVersionAPI -from vulnerabilities.package_managers import NugetVersionAPI -from vulnerabilities.package_managers import PypiVersionAPI -from vulnerabilities.package_managers import RubyVersionAPI -from vulnerabilities.severity_systems import scoring_systems +from vulnerabilities.improver import Improver +from vulnerabilities.improver import Inference +from vulnerabilities.models import Advisory +from vulnerabilities.package_managers_2 import ComposerVersionAPI +from vulnerabilities.package_managers_2 import GoproxyVersionAPI +from vulnerabilities.package_managers_2 import MavenVersionAPI +from vulnerabilities.package_managers_2 import NugetVersionAPI +from vulnerabilities.package_managers_2 import PypiVersionAPI +from vulnerabilities.package_managers_2 import RubyVersionAPI +from vulnerabilities.package_managers_2 import VersionAPI +from vulnerabilities.severity_systems import SCORING_SYSTEMS +LOGGER = logging.getLogger(__name__) + +WEIRD_IGNORABLE_VERSIONS = frozenset( + [ + "0.1-bulbasaur", + "0.1-charmander", + "0.3m1", + "0.3m2", + "0.3m3", + "0.3m4", + "0.3m5", + "0.4m1", + "0.4m2", + "0.4m3", + "0.4m4", + "0.4m5", + "0.5m1", + "0.5m2", + "0.5m3", + "0.5m4", + "0.5m5", + "0.6m1", + "0.6m2", + "0.6m3", + "0.6m4", + "0.6m5", + "0.6m6", + "0.7.10p1", + "0.7.11p1", + "0.7.11p2", + "0.7.11p3", + "0.8.1p1", + "0.8.3p1", + "0.8.4p1", + "0.8.4p2", + "0.8.6p1", + "0.8.7p1", + "0.9-doduo", + "0.9-eevee", + "0.9-fearow", + "0.9-gyarados", + "0.9-horsea", + "0.9-ivysaur", + "2013-01-21T20:33:09+0100", + "2013-01-23T17:11:52+0100", + "2013-02-01T20:50:46+0100", + "2013-02-02T19:59:03+0100", + "2013-02-02T20:23:17+0100", + "2013-02-08T17:40:57+0000", + "2013-03-27T16:32:26+0100", + "2013-05-09T12:47:53+0200", + "2013-05-10T17:55:56+0200", + "2013-05-14T20:16:05+0200", + "2013-06-01T10:32:51+0200", + "2013-07-19T09:11:08+0000", + "2013-08-12T21:48:56+0200", + "2013-09-11T19-27-10", + "2013-12-23T17-51-15", + "2014-01-12T15-52-10", + "2.0.1rc2-git", + "3.0.0b3-", + "3.0b6dev-r41684", + "-class.-jw.util.version.Version-", + ] +) + +PACKAGE_TYPE_BY_GITHUB_ECOSYSTEM = { + "MAVEN": "maven", + "NUGET": "nuget", + "COMPOSER": "composer", + "PIP": "pypi", + "RUBYGEMS": "gem", + "GO": "golang", +} + + +GITHUB_ECOSYSTEM_BY_PACKAGE_TYPE = { + value: key for (key, value) in PACKAGE_TYPE_BY_GITHUB_ECOSYSTEM.items() +} + +# TODO: We will try to gather more info from GH API +# Check https://github.com/nexB/vulnerablecode/issues/645 # set of all possible values of first '%s' = {'MAVEN','COMPOSER', 'NUGET', 'RUBYGEMS', 'PYPI'} # second '%s' is interesting, it will have the value '' for the first request, -# since we don't have any value for endCursor at the beginning -# for all the subsequent requests it will have value 'after: "{endCursor}"" -query = """ - query{ - securityVulnerabilities(first: 100, ecosystem: %s, %s) { - edges { - node { - advisory { - identifiers { - type - value - } - summary - references { - url - } - severity - publishedAt - } - package { - name - } - vulnerableVersionRange - } - } - pageInfo { - hasNextPage - endCursor - } +GRAPHQL_VULNERABILITY_QUERY_TEMPLATE = """ +query{ +securityVulnerabilities(first: 100, ecosystem: %s, %s) { + edges { + node { + advisory { + identifiers { + type + value } + summary + references { + url } - """ - -# See https://github.com/nexB/vulnerablecode/issues/486 -IGNORE_VERSIONS = { - "0.1-bulbasaur", - "0.1-charmander", - "0.3m1", - "0.3m2", - "0.3m3", - "0.3m4", - "0.3m5", - "0.4m1", - "0.4m2", - "0.4m3", - "0.4m4", - "0.4m5", - "0.5m1", - "0.5m2", - "0.5m3", - "0.5m4", - "0.5m5", - "0.6m1", - "0.6m2", - "0.6m3", - "0.6m4", - "0.6m5", - "0.6m6", - "0.7.10p1", - "0.7.11p1", - "0.7.11p2", - "0.7.11p3", - "0.8.1p1", - "0.8.3p1", - "0.8.4p1", - "0.8.4p2", - "0.8.6p1", - "0.8.7p1", - "0.9-doduo", - "0.9-eevee", - "0.9-fearow", - "0.9-gyarados", - "0.9-horsea", - "0.9-ivysaur", - "2013-01-21T20:33:09+0100", - "2013-01-23T17:11:52+0100", - "2013-02-01T20:50:46+0100", - "2013-02-02T19:59:03+0100", - "2013-02-02T20:23:17+0100", - "2013-02-08T17:40:57+0000", - "2013-03-27T16:32:26+0100", - "2013-05-09T12:47:53+0200", - "2013-05-10T17:55:56+0200", - "2013-05-14T20:16:05+0200", - "2013-06-01T10:32:51+0200", - "2013-07-19T09:11:08+0000", - "2013-08-12T21:48:56+0200", - "2013-09-11T19-27-10", - "2013-12-23T17-51-15", - "2014-01-12T15-52-10", - "2.0.1rc2-git", - "3.0.0b3-", - "3.0b6dev-r41684", - "-class.-jw.util.version.Version-", + severity + publishedAt + } + package { + name + } + vulnerableVersionRange + } + } + pageInfo { + hasNextPage + endCursor + } } +} +""" + +VERSION_API_CLASSES = [ + MavenVersionAPI, + NugetVersionAPI, + ComposerVersionAPI, + PypiVersionAPI, + RubyVersionAPI, + GoproxyVersionAPI, +] + +VERSION_API_CLASSES_BY_PACKAGE_TYPE = {cls.package_type: cls for cls in VERSION_API_CLASSES} class GitHubTokenError(Exception): pass +# Isolated network call for simplicity of testing +def get_response(endpoint: str, headers: dict, query: dict): + return requests.post(endpoint, headers=headers, json=query).json() + + class GitHubAPIImporter(Importer): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) + spdx_license_expression = "CC-BY-4.0" + endpoint = "https://api.github.com/graphql" + + def advisory_data(self) -> Iterable[AdvisoryData]: + """ + Return a list of AdvisoryData objects + """ try: - self.gh_token = os.environ["GH_TOKEN"] - except KeyError: - raise GitHubTokenError("Environment variable GH_TOKEN is missing") + token = os.environ["GH_TOKEN"] + except Exception as e: + LOGGER.error("No GitHub token found. Please set the GH_TOKEN environment variable.") + raise GitHubTokenError(e) + headers = {"Authorization": f"token {token}"} + advisories = [] + for ecosystem, package_type in PACKAGE_TYPE_BY_GITHUB_ECOSYSTEM.items(): + end_cursor_exp = "" + while True: + query = { + "query": GRAPHQL_VULNERABILITY_QUERY_TEMPLATE % (ecosystem, end_cursor_exp) + } + resp = get_response(endpoint=self.endpoint, headers=headers, query=query) + message = resp.get("message") + if message and message == "Bad credentials": + raise GitHubTokenError("Invalid GitHub token") + page_info = get_item(resp, "data", "securityVulnerabilities", "pageInfo") + end_cursor = get_item(page_info, "endCursor") + if end_cursor: + end_cursor = f'"{end_cursor}"' + end_cursor_exp = f"after: {end_cursor}" + advisories.extend(process_response(resp, package_type=package_type)) + if not get_item(page_info, "hasNextPage"): + break + return advisories - def __enter__(self): - self.advisories = self.fetch() - def set_api(self, packages): - asyncio.run(self.version_api.load_api(packages)) +def get_reference_id(url: str): + """ + Return the reference id from a URL + For example: + >>> get_reference_id("https://github.com/advisories/GHSA-c9hw-wf7x-jp9j") + 'GHSA-c9hw-wf7x-jp9j' + """ + url_parts = url.split("/") + last_url_part = url_parts[-1] + return last_url_part - def updated_advisories(self) -> Set[Advisory]: - return self.batch_advisories(self.process_response()) - def fetch(self) -> Mapping[str, List[Mapping]]: - headers = {"Authorization": "token " + self.gh_token} - api_data = {} - for ecosystem in self.config.ecosystems: +def extract_references(reference_data: List[dict]) -> Iterable[Reference]: + """ + Yield `reference` by iterating over `reference_data` + >>> list(extract_references([{'url': "https://github.com/advisories/GHSA-c9hw-wf7x-jp9j"}])) + [Reference(url="https://github.com/advisories/GHSA-c9hw-wf7x-jp9j"), reference_id = "GHSA-c9hw-wf7x-jp9j" ] + >>> list(extract_references([{'url': "https://github.com/advisories/c9hw-wf7x-jp9j"}])) + [Reference(url="https://github.com/advisories/c9hw-wf7x-jp9j")] + """ + for ref in reference_data: + url = ref["url"] + if not isinstance(url, str): + LOGGER.error(f"extract_references: url is not of type `str`: {url}") + continue + if "GHSA-" in url.upper(): + reference = Reference(url=url, reference_id=get_reference_id(url)) + else: + reference = Reference(url=url) + yield reference - api_data[ecosystem] = [] - end_cursor_exp = "" - while True: - query_json = {"query": query % (ecosystem, end_cursor_exp)} - resp = requests.post(self.config.endpoint, headers=headers, json=query_json).json() - if resp.get("message") == "Bad credentials": - raise GitHubTokenError("Invalid GitHub token") +def get_purl(pkg_type: str, github_name: str) -> Optional[PackageURL]: + """ + Return a PackageURL by splitting the `github_name` using the `pkg_type` convention. + Return None and log an error if we can not split or it is an unknown package type. + >>> get_purl("maven", "org.apache.commons:commons-lang3") + PackageURL(type="maven", namespace="org.apache.commons", name="commons-lang3") + >>> get_purl("composer", "foo/bar") + PackageURL(type="composer", namespace="foo", name="bar") + """ + if pkg_type == "maven": + if ":" not in github_name: + LOGGER.error(f"get_purl: Invalid maven package name {github_name}") + return + ns, _, name = github_name.partition(":") + return PackageURL(type=pkg_type, namespace=ns, name=name) - end_cursor = resp["data"]["securityVulnerabilities"]["pageInfo"]["endCursor"] - end_cursor_exp = "after: {}".format('"{}"'.format(end_cursor)) - api_data[ecosystem].append(resp) + if pkg_type == "composer": + if "/" not in github_name: + LOGGER.error(f"get_purl: Invalid composer package name {github_name}") + return + vendor, _, name = github_name.partition("/") + return PackageURL(type=pkg_type, namespace=vendor, name=name) - if not resp["data"]["securityVulnerabilities"]["pageInfo"]["hasNextPage"]: - break - return api_data - - def set_version_api(self, ecosystem: str) -> None: - versioners = { - "MAVEN": MavenVersionAPI, - "NUGET": NugetVersionAPI, - "COMPOSER": ComposerVersionAPI, - "PIP": PypiVersionAPI, - "RUBYGEMS": RubyVersionAPI, - "GO": GoproxyVersionAPI, - } - versioner = versioners.get(ecosystem) - if versioner: - self.version_api = versioner() - self.set_api(self.collect_packages(ecosystem)) - - @staticmethod - def process_name(ecosystem: str, pkg_name: str) -> Optional[Tuple[Optional[str], str]]: - if ecosystem == "MAVEN": - artifact_comps = pkg_name.split(":") - if len(artifact_comps) != 2: - return - ns, name = artifact_comps - return ns, name - - if ecosystem == "COMPOSER": - try: - vendor, name = pkg_name.split("/") - except ValueError: - # TODO log this - return None - return vendor, name - - if ecosystem in ("NUGET", "PIP", "RUBYGEMS", "GO"): - return None, pkg_name - - @staticmethod - def extract_references(reference_data): - references = [] - for ref in reference_data: - url = ref["url"] - if "GHSA-" in url.upper(): - reference = Reference(url=url, reference_id=url.split("/")[-1]) - else: - reference = Reference(url=url) - references.append(reference) - - return references - - def collect_packages(self, ecosystem): - packages = set() - for page in self.advisories[ecosystem]: - for adv in page["data"]["securityVulnerabilities"]["edges"]: - packages.add(adv["node"]["package"]["name"]) - return packages - - def process_response(self) -> List[Advisory]: - adv_list = [] - for ecosystem in self.advisories: - self.set_version_api(ecosystem) - pkg_type = self.version_api.package_type - for resp_page in self.advisories[ecosystem]: - for adv in resp_page["data"]["securityVulnerabilities"]["edges"]: - name = adv["node"]["package"]["name"] - cutoff_time = dateparser.parse(adv["node"]["advisory"]["publishedAt"]) - affected_purls = [] - unaffected_purls = [] - if self.process_name(ecosystem, name): - ns, pkg_name = self.process_name(ecosystem, name) - if hasattr(self.version_api, "module_name_by_package_name"): - pkg_name = self.version_api.module_name_by_package_name.get( - name, pkg_name - ) - aff_range = adv["node"]["vulnerableVersionRange"] - aff_vers, unaff_vers = self.categorize_versions( - self.version_api.package_type, - aff_range, - self.version_api.get(name, until=cutoff_time).valid_versions, - ) - affected_purls = [ - PackageURL(name=pkg_name, namespace=ns, version=version, type=pkg_type) - for version in aff_vers - ] - - unaffected_purls = [ - PackageURL(name=pkg_name, namespace=ns, version=version, type=pkg_type) - for version in unaff_vers - ] - cve_ids = set() - references = self.extract_references(adv["node"]["advisory"]["references"]) - vuln_desc = adv["node"]["advisory"]["summary"] - - for identifier in adv["node"]["advisory"]["identifiers"]: - # collect CVEs - if identifier["type"] == "CVE": - cve_ids.add(identifier["value"]) - - # attach the GHSA with severity score - if identifier["type"] == "GHSA": - for ref in references: - if ref.reference_id == identifier["value"]: - ref.severities = [ - VulnerabilitySeverity( - system=scoring_systems["cvssv3.1_qr"], - value=adv["node"]["advisory"]["severity"], - ) - ] - # Each Node has only one GHSA, hence exit after attaching - # score to this GHSA - break - - for cve_id in cve_ids: - adv_list.append( - Advisory( - vulnerability_id=cve_id, - summary=vuln_desc, - affected_packages=nearest_patched_package( - affected_purls, unaffected_purls - ), - references=references, - ) - ) - return adv_list - - @staticmethod - def categorize_versions( - package_type: str, version_range: str, all_versions: Set[str] - ) -> Tuple[List[str], List[str]]: - version_class = version_class_by_package_type[package_type] - version_scheme = version_class.scheme - version_range = VersionSpecifier.from_scheme_version_spec_string( - version_scheme, version_range + if pkg_type in ("nuget", "pypi", "gem", "golang"): + return PackageURL(type=pkg_type, name=github_name) + + LOGGER.error(f"get_purl: Unknown package type {pkg_type}") + + +class InvalidVersionRange(Exception): + """ + Raises exception when the version range is invalid + """ + + +def get_api_package_name(purl: PackageURL) -> str: + """ + Return the package name expected by the GitHub API given a PackageURL + >>> get_api_package_name(PackageURL(type="maven", namespace="org.apache.commons", name="commons-lang3")) + "org.apache.commons:commons-lang3" + >>> get_api_package_name(PackageURL(type="composer", namespace="foo", name="bar")) + "foo/bar" + """ + if purl.type == "maven": + return f"{purl.namespace}:{purl.name}" + + if purl.type == "composer": + return f"{purl.namespace}/{purl.name}" + + if purl.type in ("nuget", "pypi", "gem", "golang"): + return purl.name + + LOGGER.error(f"get_api_package_name: Unknown PURL {purl!r}") + + +def process_response(resp: dict, package_type: str) -> Iterable[AdvisoryData]: + """ + Yield `AdvisoryData` by taking + `resp` and `ecosystem` as input + """ + vulnerabilities = get_item(resp, "data", "securityVulnerabilities", "edges") or [] + if not vulnerabilities: + LOGGER.error( + f"No vulnerabilities found for package_type: {package_type!r} in response: {resp!r}" ) - affected_versions = [] - unaffected_versions = [] - for version in all_versions: - if version in IGNORE_VERSIONS: - continue - - if version_class(version) in version_range: - affected_versions.append(version) + return + + for vulnerability in vulnerabilities: + affected_packages = [] + aliases = set() + github_advisory = get_item(vulnerability, "node") + if not github_advisory: + LOGGER.error(f"No node found in {vulnerability!r}") + continue + + name = get_item(github_advisory, "package", "name") + if not name: + LOGGER.error(f"No name found in {github_advisory!r}") + continue + + purl = get_purl(pkg_type=package_type, github_name=name) + if not purl: + continue + + vulnerable_range = get_item(github_advisory, "vulnerableVersionRange") + if not vulnerable_range: + LOGGER.error(f"No affected range found in {github_advisory!r}") + continue + + affected_range = None + try: + affected_range = build_range_from_github_advisory_constraint( + package_type, vulnerable_range + ) + except InvalidVersionRange: + LOGGER.error(f"Could not parse affected range {vulnerable_range!r}") + continue + + if affected_range != NotImplementedError: + affected_packages.append( + AffectedPackage( + package=purl, + affected_version_range=affected_range, + ) + ) + + advisory = get_item(github_advisory, "advisory") + if not advisory: + LOGGER.error(f"No advisory found in {github_advisory!r}") + continue + + references = get_item(advisory, "references") or [] + if references: + references: List[Reference] = list(extract_references(references)) + + summary = get_item(advisory, "summary") + identifiers = get_item(advisory, "identifiers") or [] + for identifier in identifiers: + value = identifier["value"] + identifier_type = identifier["type"] + aliases.add(value) + # attach the GHSA with severity score + if identifier_type == "GHSA": + # Each Node has only one GHSA, hence exit after attaching + # score to this GHSA + for ref in references: + if ref.reference_id == value: + severity = get_item(advisory, "severity") + if severity: + ref.severities = [ + VulnerabilitySeverity( + system=SCORING_SYSTEMS["cvssv3.1_qr"], + value=severity, + ) + ] + + elif identifier_type == "CVE": + pass else: - unaffected_versions.append(version) - return (affected_versions, unaffected_versions) + LOGGER.error(f"Unknown identifier type {identifier_type!r} and value {value!r}") + + date_published = get_item(advisory, "publishedAt") + if date_published: + date_published = dateparser.parse(date_published) + + yield AdvisoryData( + aliases=sorted(list(aliases)), + summary=summary, + references=references, + affected_packages=affected_packages, + date_published=date_published, + ) + + +class GitHubBasicImprover(Improver): + def __init__(self) -> None: + self.version_api_by_purl_type: Mapping[str, VersionAPI] = {} + + @property + def interesting_advisories(self) -> QuerySet: + return Advisory.objects.filter(created_by=GitHubAPIImporter.qualified_name) + + def get_package_versions( + self, package_url: PackageURL, until: Optional[datetime] = None + ) -> List[str]: + """ + Return a list of `valid_versions` for the `package_url` + """ + api_name = get_api_package_name(package_url) + if not api_name: + LOGGER.error(f"Could not get versions for {package_url!r}") + return [] + version_api = self.version_api_by_purl_type.get(package_url.type) + if not version_api: + version_api: VersionAPI = VERSION_API_CLASSES_BY_PACKAGE_TYPE[package_url.type] + self.version_api_by_purl_type[package_url.type] = version_api() + api_object = self.version_api_by_purl_type[package_url.type] + api_object.load_api([api_name]) + self.version_api_by_purl_type[package_url.type] = api_object + return api_object.get(package_name=api_name, until=until).valid_versions + + def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]: + """ + Yield Inferences for the given advisory data + """ + if not advisory_data.affected_packages: + return + try: + purl, affected_version_ranges, _ = AffectedPackage.merge( + advisory_data.affected_packages + ) + except UnMergeablePackageError: + LOGGER.error(f"Cannot merge with different purls {advisory_data.affected_packages!r}") + return iter([]) + + pkg_type = purl.type + pkg_namespace = purl.namespace + pkg_name = purl.name + if purl.type == "golang": + # Problem with the Golang and Go that they provide full path + # FIXME: We need to get the PURL subpath for Go module + version_api_object = self.version_api_by_purl_type.get(purl.type) + if not version_api_object: + version_api_object = GoproxyVersionAPI() + self.version_api_by_purl_type[purl.type] = version_api_object + pkg_name = version_api_object.module_name_by_package_name.get(pkg_name, pkg_name) + + valid_versions = self.get_package_versions( + package_url=purl, until=advisory_data.date_published + ) + for affected_version_range in affected_version_ranges: + aff_vers, unaff_vers = resolve_version_range( + affected_version_range=affected_version_range, + package_versions=valid_versions, + ) + affected_purls = [ + PackageURL(type=pkg_type, namespace=pkg_namespace, name=pkg_name, version=version) + for version in aff_vers + ] + + unaffected_purls = [ + PackageURL(type=pkg_type, namespace=pkg_namespace, name=pkg_name, version=version) + for version in unaff_vers + ] + + affected_packages: List[LegacyAffectedPackage] = nearest_patched_package( + vulnerable_packages=affected_purls, resolved_packages=unaffected_purls + ) + + unique_patched_packages_with_affected_packages = {} + for package in affected_packages: + if package.patched_package not in unique_patched_packages_with_affected_packages: + unique_patched_packages_with_affected_packages[package.patched_package] = [] + unique_patched_packages_with_affected_packages[package.patched_package].append( + package.vulnerable_package + ) + + for ( + fixed_package, + affected_packages, + ) in unique_patched_packages_with_affected_packages.items(): + yield Inference.from_advisory_data( + advisory_data, + confidence=100, # We are getting all valid versions to get this inference + affected_purls=affected_packages, + fixed_purl=fixed_package, + ) + + +def resolve_version_range( + affected_version_range: VersionRange, + package_versions: List[str], + ignorable_versions=WEIRD_IGNORABLE_VERSIONS, +) -> Tuple[List[str], List[str]]: + """ + Given an affected version range and a list of `package_versions`, resolve which versions are in this range + and return a tuple of two lists of `affected_versions` and `unaffected_versions`. + """ + if not affected_version_range: + LOGGER.error(f"affected version range is {affected_version_range!r}") + return [], [] + affected_versions = [] + unaffected_versions = [] + for package_version in package_versions or []: + if package_version in ignorable_versions: + continue + # Remove leading 'v' + if package_version.startswith("v") or package_version.startswith("V"): + package_version = package_version.replace("V", "").replace("v", "") + # Remove whitespace + package_version = package_version.replace(" ", "") + try: + version = affected_version_range.version_class(package_version) + except Exception: + LOGGER.error(f"Could not parse version {package_version!r}") + continue + if version in affected_version_range: + affected_versions.append(package_version) + else: + unaffected_versions.append(package_version) + return affected_versions, unaffected_versions diff --git a/vulnerabilities/importers/nginx.py b/vulnerabilities/importers/nginx.py index aef551c57..093296a23 100644 --- a/vulnerabilities/importers/nginx.py +++ b/vulnerabilities/importers/nginx.py @@ -37,6 +37,7 @@ from vulnerabilities.importer import AffectedPackage from vulnerabilities.importer import Importer from vulnerabilities.importer import Reference +from vulnerabilities.importer import UnMergeablePackageError from vulnerabilities.importer import VulnerabilitySeverity from vulnerabilities.improver import Improver from vulnerabilities.improver import Inference @@ -199,8 +200,10 @@ def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]: purl, affected_version_ranges, fixed_versions = AffectedPackage.merge( advisory_data.affected_packages ) - except KeyError: + except UnMergeablePackageError: + logger.error(f"Cannot merge with different purls {advisory_data.affected_packages!r}") return iter([]) + all_versions = self.version_api.get("nginx/nginx").valid_versions affected_purls = [] for affected_version_range in affected_version_ranges: diff --git a/vulnerabilities/improver.py b/vulnerabilities/improver.py index 27e6a26cd..fa8064fa1 100644 --- a/vulnerabilities/improver.py +++ b/vulnerabilities/improver.py @@ -61,6 +61,20 @@ def __post_init__(self): not versionless_purls ), f"Version-less purls are not supported in an Inference: {versionless_purls}" + def to_dict(self): + """ + Return a dict representation of this Inference + """ + return { + "vulnerability_id": self.vulnerability_id, + "aliases": [alias for alias in self.aliases], + "confidence": self.confidence, + "summary": self.summary, + "affected_purls": [affected_purl.to_dict() for affected_purl in self.affected_purls], + "fixed_purl": self.fixed_purl.to_dict(), + "references": [ref.to_dict() for ref in self.references], + } + @classmethod def from_advisory_data(cls, advisory_data, confidence, fixed_purl, affected_purls=None): """ diff --git a/vulnerabilities/improvers/__init__.py b/vulnerabilities/improvers/__init__.py index fdb48b6c1..ce640c840 100644 --- a/vulnerabilities/improvers/__init__.py +++ b/vulnerabilities/improvers/__init__.py @@ -5,6 +5,7 @@ default.DefaultImprover, importers.nginx.NginxBasicImprover, importers.alpine_linux.AlpineBasicImprover, + importers.github.GitHubBasicImprover, ] IMPROVERS_REGISTRY = {x.qualified_name: x for x in IMPROVERS_REGISTRY} diff --git a/vulnerabilities/package_managers.py b/vulnerabilities/package_managers.py index 6ed487f77..6fbe86d01 100644 --- a/vulnerabilities/package_managers.py +++ b/vulnerabilities/package_managers.py @@ -68,12 +68,15 @@ def get(self, package_name, until=None) -> VersionResponse: for version in self.cache.get(package_name, set()): if until and version.release_date and version.release_date > until: new_versions.add(version.value) - continue - valid_versions.add(version.value) + else: + valid_versions.add(version.value) return VersionResponse(valid_versions=valid_versions, newer_versions=new_versions) async def load_api(self, pkg_set): + """ + Populate the cache with the versions of the packages in pkg_set + """ async with client_session() as session: await asyncio.gather( *[self.fetch(pkg, session) for pkg in pkg_set if pkg not in self.cache] @@ -509,7 +512,10 @@ async def fetch(self, pkg, session): class GoproxyVersionAPI(VersionAPI): package_type = "golang" - module_name_by_package_name = {} + + def __init__(self, cache: MutableMapping[str, Set[Version]] = None): + super().__init__(cache) + self.module_name_by_package_name = {} @staticmethod def trim_url_path(url_path: str) -> Optional[str]: diff --git a/vulnerabilities/package_managers_2.py b/vulnerabilities/package_managers_2.py new file mode 100644 index 000000000..3c207824f --- /dev/null +++ b/vulnerabilities/package_managers_2.py @@ -0,0 +1,361 @@ +import dataclasses +import logging +import traceback +import xml.etree.ElementTree as ET +from datetime import datetime +from typing import List +from typing import MutableMapping +from typing import Optional +from typing import Set +from urllib.parse import urlparse + +import requests +from dateutil import parser as dateparser +from django.utils.dateparse import parse_datetime + +from vulnerabilities.package_managers import VersionResponse + +LOGGER = logging.getLogger(__name__) + + +@dataclasses.dataclass(frozen=True) +class LegacyVersion: + value: str + release_date: Optional[datetime] = None + + +@dataclasses.dataclass +class VersionResponse: + valid_versions: Set[str] = dataclasses.field(default_factory=set) + newer_versions: Set[str] = dataclasses.field(default_factory=set) + + +def get_response(url, type="json"): + resp = requests.get(url=url) + if not resp.status_code == 200: + LOGGER.error(f"Error while fetching {url}: {resp.status_code}") + return None + if type == "read": + return resp.content + if type == "text": + return resp.text + return resp.json() + + +class VersionAPI: + def __init__(self, cache: MutableMapping[str, Set[LegacyVersion]] = None): + self.cache = cache or {} + + def get(self, package_name, until=None) -> VersionResponse: + new_versions = set() + valid_versions = set() + for version in self.cache.get(package_name, set()): + if until and version.release_date and version.release_date > until: + new_versions.add(version.value) + else: + valid_versions.add(version.value) + + return VersionResponse(valid_versions=valid_versions, newer_versions=new_versions) + + def load_api(self, pkg_set): + """ + Populate the cache with the versions of the packages in pkg_set + """ + for pkg in pkg_set: + if pkg in self.cache: + continue + self.fetch(pkg) + + def fetch(self, pkg): + """ + Override this method to fetch the pkg's version in the cache + """ + raise NotImplementedError + + +class PypiVersionAPI(VersionAPI): + + package_type = "pypi" + + def fetch(self, pkg): + url = f"https://pypi.org/pypi/{pkg}/json" + versions = set() + response = get_response(url=url) + + if not response: + self.cache[pkg] = versions + return + + for version, download_items in response["releases"].items() or {}: + if download_items: + latest_download_item = max( + download_items, + key=lambda download_item: dateparser.parse( + download_item["upload_time_iso_8601"] + if "upload_time_iso_8601" in download_item + else LOGGER.error(f"{download_item} has no upload_time_iso_8601") + ), + ) + versions.add( + LegacyVersion( + value=version, + release_date=dateparser.parse(latest_download_item["upload_time_iso_8601"]), + ) + ) + self.cache[pkg] = versions + + +class RubyVersionAPI(VersionAPI): + + package_type = "gem" + + def fetch(self, pkg): + url = f"https://rubygems.org/api/v1/versions/{pkg}.json" + versions = set() + response = get_response(url=url) + if not response: + self.cache[pkg] = versions + return + for release in response: + if release["number"] and release["published_at"]: + release_date = dateparser.parse(release["published_at"]) + versions.add(LegacyVersion(value=release["number"], release_date=release_date)) + else: + LOGGER.error(f"Failed to parse release {release}") + + self.cache[pkg] = versions + + +class MavenVersionAPI(VersionAPI): + + package_type = "maven" + + def fetch(self, pkg) -> None: + artifact_comps = pkg.split(":") + endpoint = self.artifact_url(artifact_comps) + + resp = get_response(url=endpoint, type="read") + + if not resp: + self.cache[pkg] = set() + return + + xml_resp = ET.ElementTree(ET.fromstring(resp.decode("utf-8"))) + self.cache[pkg] = self.extract_versions(xml_resp) + + @staticmethod + def artifact_url(artifact_comps: List[str]) -> str: + base_url = "https://repo1.maven.org/maven2/{}" + try: + group_id, artifact_id = artifact_comps + except ValueError: + if len(artifact_comps) == 1: + group_id = artifact_comps[0] + artifact_id = artifact_comps[0].split(".")[-1] + + elif len(artifact_comps) == 3: + group_id, artifact_id = list(dict.fromkeys(artifact_comps)) + + else: + raise + + group_url = group_id.replace(".", "/") + suffix = group_url + "/" + artifact_id + "/" + "maven-metadata.xml" + endpoint = base_url.format(suffix) + + return endpoint + + @staticmethod + def extract_versions(xml_response: ET.ElementTree) -> Set[LegacyVersion]: + all_versions = set() + for child in xml_response.getroot().iter(): + if child.tag == "version" and child.text: + all_versions.add(LegacyVersion(child.text)) + + return all_versions + + +class NugetVersionAPI(VersionAPI): + + package_type = "nuget" + + def fetch(self, pkg) -> None: + endpoint = self.nuget_url(pkg) + resp = get_response(url=endpoint) + if not resp: + self.cache[pkg] = set() + return + self.cache[pkg] = self.extract_versions(resp) + + @staticmethod + def nuget_url(pkg_name: str) -> str: + pkg_name = pkg_name.lower().strip() + base_url = f"https://api.nuget.org/v3/registration5-semver1/{pkg_name}/index.json" + return base_url + + @staticmethod + def extract_versions(resp: dict) -> Set[LegacyVersion]: + all_versions = set() + for entry_group in resp["items"] or []: + for entry in entry_group["items"] or []: + catalog_entry = entry["catalogEntry"] or {} + version = catalog_entry.get("version") + release_date = dateparser.parse(catalog_entry.get("published")) + if version and release_date: + all_versions.add( + LegacyVersion( + value=version, + release_date=release_date, + ) + ) + + return all_versions + + +class GoproxyVersionAPI(VersionAPI): + + package_type = "golang" + + def __init__(self, cache: MutableMapping[str, Set[LegacyVersion]] = None): + super().__init__(cache) + self.module_name_by_package_name = {} + + @staticmethod + def trim_go_url_path(url_path: str) -> Optional[str]: + """ + Return a trimmed Go `url_path` removing trailing + package references and keeping only the module + references. + + Github advisories for Go are using package names + such as "https://github.com/nats-io/nats-server/v2/server" + (e.g., https://github.com/advisories/GHSA-jp4j-47f9-2vc3 ), + yet goproxy works with module names instead such as + "https://github.com/nats-io/nats-server" (see for details + https://golang.org/ref/mod#goproxy-protocol ). + This functions trims the trailing part(s) of a package URL + and returns the remaining the module name. + For example: + >>> module = "github.com/xx/a" + >>> assert GoproxyVersionAPI.trim_go_url_path("https://github.com/xx/a/b") == module + """ + # some advisories contains this prefix in package name, e.g. https://github.com/advisories/GHSA-7h6j-2268-fhcm + if url_path.startswith("https://pkg.go.dev/"): + url_path = url_path[len("https://pkg.go.dev/") :] + parsed_url_path = urlparse(url_path) + path = parsed_url_path.path + parts = path.split("/") + if len(parts) < 3: + LOGGER.error(f"Not a valid Go URL path {url_path} trim_go_url_path") + return None + else: + joined_path = "/".join(parts[:3]) + return f"{parsed_url_path.netloc}{joined_path}" + + @staticmethod + def escape_path(path: str) -> str: + """ + Return an case-encoded module path or version name. + + This is done by replacing every uppercase letter with an exclamation + mark followed by the corresponding lower-case letter, in order to + avoid ambiguity when serving from case-insensitive file systems. + Refer to https://golang.org/ref/mod#goproxy-protocol. + """ + escaped_path = "" + for c in path: + if c >= "A" and c <= "Z": + # replace uppercase with !lowercase + escaped_path += "!" + chr(ord(c) + ord("a") - ord("A")) + else: + escaped_path += c + return escaped_path + + @staticmethod + def parse_version_info(version_info: str, escaped_pkg: str) -> Optional[LegacyVersion]: + v = version_info.split() + if not v: + return None + value = v[0] + if len(v) > 1: + # get release date from the second part. see https://github.com/golang/go/blob/master/src/cmd/go/internal/modfetch/proxy.go#latest() + release_date = parse_datetime(v[1]) + else: + escaped_ver = GoproxyVersionAPI.escape_path(value) + resp_json = get_response( + url=f"https://proxy.golang.org/{escaped_pkg}/@v/{escaped_ver}.info" + ) + if not resp_json: + traceback.print_exc() + print( + f"error while fetching version info for {escaped_pkg}/{escaped_ver} from goproxy" + ) + release_date = parse_datetime(resp_json.get("Time", "")) if resp_json else None + + return LegacyVersion(value=value, release_date=release_date) + + def fetch(self, pkg: str): + # escape uppercase in module path + escaped_pkg = GoproxyVersionAPI.escape_path(pkg) + trimmed_pkg = pkg + resp_text = None + # resolve module name from package name, see https://go.dev/ref/mod#resolve-pkg-mod + while escaped_pkg is not None: + url = f"https://proxy.golang.org/{escaped_pkg}/@v/list" + resp_text = get_response(url=url, type="text") + if not resp_text: + escaped_pkg = GoproxyVersionAPI.trim_go_url_path(escaped_pkg) + trimmed_pkg = GoproxyVersionAPI.trim_go_url_path(trimmed_pkg) or "" + continue + break + if resp_text is None or escaped_pkg is None or trimmed_pkg is None: + print(f"error while fetching versions for {pkg} from goproxy") + return + self.module_name_by_package_name[pkg] = trimmed_pkg + versions = set() + for version_info in resp_text.split("\n"): + version = GoproxyVersionAPI.parse_version_info(version_info, escaped_pkg) + if version is not None: + versions.add(version) + self.cache[pkg] = versions + + +class ComposerVersionAPI(VersionAPI): + + package_type = "composer" + + def fetch(self, pkg) -> None: + endpoint = self.composer_url(pkg) + if endpoint: + resp = get_response(url=endpoint) + if not resp: + self.cache[pkg] = set() + return + self.cache[pkg] = self.extract_versions(resp, pkg) + + @staticmethod + def composer_url(pkg_name: str) -> Optional[str]: + try: + vendor, name = pkg_name.split("/") + except ValueError: + # TODO Log this + return + return f"https://repo.packagist.org/p/{vendor}/{name}.json" + + @staticmethod + def extract_versions(resp: dict, pkg_name: str) -> Set[LegacyVersion]: + all_versions = set() + for version in resp["packages"][pkg_name]: + if "dev" in version: + continue + + # This if statement ensures, that all_versions contains only released versions + # See https://github.com/composer/composer/blob/44a4429978d1b3c6223277b875762b2930e83e8c/doc/articles/versions.md#tags # nopep8 + # for explanation of removing 'v' + all_versions.add( + LegacyVersion( + value=version.lstrip("v"), + release_date=dateparser.parse(resp["packages"][pkg_name][version]["time"]), + ) + ) + return all_versions diff --git a/vulnerabilities/tests/conftest.py b/vulnerabilities/tests/conftest.py index da9309274..58ad04b38 100644 --- a/vulnerabilities/tests/conftest.py +++ b/vulnerabilities/tests/conftest.py @@ -65,7 +65,6 @@ def no_rmtree(monkeypatch): "test_safety_db.py", "test_gentoo.py", "test_suse.py", - "test_github.py", "test_suse_backports.py", "test_suse_scores.py", "test_ubuntu.py", diff --git a/vulnerabilities/tests/test_affected_package.py b/vulnerabilities/tests/test_affected_package.py new file mode 100644 index 000000000..d18478936 --- /dev/null +++ b/vulnerabilities/tests/test_affected_package.py @@ -0,0 +1,72 @@ +import pytest +from packageurl import PackageURL +from univers.version_constraint import VersionConstraint +from univers.version_range import GemVersionRange +from univers.versions import RubygemsVersion + +from vulnerabilities.importer import AffectedPackage +from vulnerabilities.importer import NoAffectedPackages +from vulnerabilities.importer import UnMergeablePackageError + + +def test_affected_package_merge_fail(): + with pytest.raises(UnMergeablePackageError): + AffectedPackage.merge( + [ + AffectedPackage( + package=PackageURL(type="gem", name="foo"), + fixed_version=RubygemsVersion(string="5.2.8.1"), + affected_version_range=GemVersionRange( + constraints=( + VersionConstraint( + comparator=">=", version=RubygemsVersion(string="5.2.0") + ), + VersionConstraint( + comparator="<=", version=RubygemsVersion(string="5.2.6.2") + ), + ) + ), + ), + AffectedPackage(package=PackageURL(type="npm", name="bar"), fixed_version="1.0.0"), + ] + ) + + +def test_affected_package_merge(): + result = AffectedPackage.merge( + [ + AffectedPackage( + package=PackageURL(type="npm", name="foo"), + fixed_version="1.0.0", + affected_version_range=GemVersionRange( + constraints=( + VersionConstraint(comparator=">=", version=RubygemsVersion(string="5.2.0")), + VersionConstraint( + comparator="<=", version=RubygemsVersion(string="5.2.6.2") + ), + ) + ), + ), + AffectedPackage(package=PackageURL(type="npm", name="foo"), fixed_version="2.0.0"), + ] + ) + expected = ( + PackageURL( + type="npm", namespace=None, name="foo", version=None, qualifiers={}, subpath=None + ), + [ + GemVersionRange( + constraints=( + VersionConstraint(comparator=">=", version=RubygemsVersion(string="5.2.0")), + VersionConstraint(comparator="<=", version=RubygemsVersion(string="5.2.6.2")), + ) + ) + ], + ["1.0.0", "2.0.0"], + ) + assert expected == result + + +def test_affected_package_merge_empty_list(): + with pytest.raises(NoAffectedPackages): + AffectedPackage.merge([]) diff --git a/vulnerabilities/tests/test_data/github_api/composer-expected.json b/vulnerabilities/tests/test_data/github_api/composer-expected.json new file mode 100644 index 000000000..44b6ac33a --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/composer-expected.json @@ -0,0 +1,203 @@ +[ + { + "aliases": [ + "CVE-2022-0832", + "GHSA-6qcc-whgp-pjj2" + ], + "summary": "Cross-site Scripting in Pimcore", + "affected_packages": [ + { + "package": { + "type": "composer", + "namespace": "pimcore", + "name": "pimcore", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:composer/<=10.3.2", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-0832", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/pimcore/pimcore/commit/8ab06bfbb5a05a1b190731d9c7476ec45f5ee878", + "severities": [] + }, + { + "reference_id": "", + "url": "https://huntr.dev/bounties/be450b60-bc8f-4585-96a5-3c4069f1186a", + "severities": [] + }, + { + "reference_id": "GHSA-6qcc-whgp-pjj2", + "url": "https://github.com/advisories/GHSA-6qcc-whgp-pjj2", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + } + ], + "date_published": "2022-03-05T00:00:45+00:00" + }, + { + "aliases": [ + "CVE-2022-0831", + "GHSA-q67f-3jq4-mww2" + ], + "summary": "Cross-site Scripting in Pimcore", + "affected_packages": [ + { + "package": { + "type": "composer", + "namespace": "pimcore", + "name": "pimcore", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:composer/<=10.3.2", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-0831", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/pimcore/pimcore/commit/e786fd44aac46febdbf916ed6c328fbe645d80bf", + "severities": [] + }, + { + "reference_id": "", + "url": "https://huntr.dev/bounties/4152e3a7-27a1-49eb-a6eb-a57506af104f", + "severities": [] + }, + { + "reference_id": "GHSA-q67f-3jq4-mww2", + "url": "https://github.com/advisories/GHSA-q67f-3jq4-mww2", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + } + ], + "date_published": "2022-03-05T00:00:45+00:00" + }, + { + "aliases": [ + "CVE-2022-0895", + "GHSA-x28w-hvwc-mp75" + ], + "summary": "Static Code Injection in Microweber", + "affected_packages": [ + { + "package": { + "type": "composer", + "namespace": "microweber", + "name": "microweber", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:composer/<1.3.0", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-0895", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/microweber/microweber/commit/b2baab6e582b2efe63788d367a2bb61a2fa26470", + "severities": [] + }, + { + "reference_id": "", + "url": "https://huntr.dev/bounties/3c070828-fd00-476c-be33-9c877172363d", + "severities": [] + }, + { + "reference_id": "GHSA-x28w-hvwc-mp75", + "url": "https://github.com/advisories/GHSA-x28w-hvwc-mp75", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ], + "date_published": "2022-03-11T00:02:35+00:00" + }, + { + "aliases": [ + "CVE-2022-0589", + "GHSA-gj26-g5qf-jrh7" + ], + "summary": "Cross-site Scripting in librenms", + "affected_packages": [ + { + "package": { + "type": "composer", + "namespace": "librenms", + "name": "librenms", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:composer/<22.1.0", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-0589", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/librenms/librenms/commit/4c9d4eefd8064a0285f9718ef38f5617d7f9d6fa", + "severities": [] + }, + { + "reference_id": "", + "url": "https://huntr.dev/bounties/d943d95c-076f-441a-ab21-cbf6b15f6768", + "severities": [] + }, + { + "reference_id": "", + "url": "https://notes.netbytesec.com/2022/02/multiple-vulnerabilities-in-librenms.html", + "severities": [] + }, + { + "reference_id": "GHSA-gj26-g5qf-jrh7", + "url": "https://github.com/advisories/GHSA-gj26-g5qf-jrh7", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + } + ], + "date_published": "2022-02-16T00:01:51+00:00" + } +] \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/composer.json b/vulnerabilities/tests/test_data/github_api/composer.json new file mode 100644 index 000000000..72e6bc701 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/composer.json @@ -0,0 +1,163 @@ +{ + "data": { + "securityVulnerabilities": { + "edges": [ + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-6qcc-whgp-pjj2" + }, + { + "type": "CVE", + "value": "CVE-2022-0832" + } + ], + "summary": "Cross-site Scripting in Pimcore", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-0832" + }, + { + "url": "https://github.com/pimcore/pimcore/commit/8ab06bfbb5a05a1b190731d9c7476ec45f5ee878" + }, + { + "url": "https://huntr.dev/bounties/be450b60-bc8f-4585-96a5-3c4069f1186a" + }, + { + "url": "https://github.com/advisories/GHSA-6qcc-whgp-pjj2" + } + ], + "severity": "MODERATE", + "publishedAt": "2022-03-05T00:00:45Z" + }, + "package": { + "name": "pimcore/pimcore" + }, + "vulnerableVersionRange": "<= 10.3.2" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-q67f-3jq4-mww2" + }, + { + "type": "CVE", + "value": "CVE-2022-0831" + } + ], + "summary": "Cross-site Scripting in Pimcore", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-0831" + }, + { + "url": "https://github.com/pimcore/pimcore/commit/e786fd44aac46febdbf916ed6c328fbe645d80bf" + }, + { + "url": "https://huntr.dev/bounties/4152e3a7-27a1-49eb-a6eb-a57506af104f" + }, + { + "url": "https://github.com/advisories/GHSA-q67f-3jq4-mww2" + } + ], + "severity": "MODERATE", + "publishedAt": "2022-03-05T00:00:45Z" + }, + "package": { + "name": "pimcore/pimcore" + }, + "vulnerableVersionRange": "<= 10.3.2" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-x28w-hvwc-mp75" + }, + { + "type": "CVE", + "value": "CVE-2022-0895" + } + ], + "summary": "Static Code Injection in Microweber", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-0895" + }, + { + "url": "https://github.com/microweber/microweber/commit/b2baab6e582b2efe63788d367a2bb61a2fa26470" + }, + { + "url": "https://huntr.dev/bounties/3c070828-fd00-476c-be33-9c877172363d" + }, + { + "url": "https://github.com/advisories/GHSA-x28w-hvwc-mp75" + } + ], + "severity": "HIGH", + "publishedAt": "2022-03-11T00:02:35Z" + }, + "package": { + "name": "microweber/microweber" + }, + "vulnerableVersionRange": "< 1.3" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-gj26-g5qf-jrh7" + }, + { + "type": "CVE", + "value": "CVE-2022-0589" + } + ], + "summary": "Cross-site Scripting in librenms", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-0589" + }, + { + "url": "https://github.com/librenms/librenms/commit/4c9d4eefd8064a0285f9718ef38f5617d7f9d6fa" + }, + { + "url": "https://huntr.dev/bounties/d943d95c-076f-441a-ab21-cbf6b15f6768" + }, + { + "url": "https://notes.netbytesec.com/2022/02/multiple-vulnerabilities-in-librenms.html" + }, + { + "url": "https://github.com/advisories/GHSA-gj26-g5qf-jrh7" + } + ], + "severity": "MODERATE", + "publishedAt": "2022-02-16T00:01:51Z" + }, + "package": { + "name": "librenms/librenms" + }, + "vulnerableVersionRange": "< 22.1.0" + } + } + ], + "pageInfo": { + "hasNextPage": true, + "endCursor": "Y3Vyc29yOnYyOpK5MjAyMi0wMi0xN1QwNDoyMzozOSswNTozMM1W1g==" + } + } + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/gem-expected.json b/vulnerabilities/tests/test_data/github_api/gem-expected.json new file mode 100644 index 000000000..55604d2dd --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/gem-expected.json @@ -0,0 +1,302 @@ +[ + { + "aliases": [ + "CVE-2009-4492", + "GHSA-6mq2-37j5-w6r6" + ], + "summary": "Moderate severity vulnerability that affects webrick", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "webrick", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<=1.3.1", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2009-4492", + "severities": [] + }, + { + "reference_id": "GHSA-6mq2-37j5-w6r6", + "url": "https://github.com/advisories/GHSA-6mq2-37j5-w6r6", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + }, + { + "reference_id": "", + "url": "http://secunia.com/advisories/37949", + "severities": [] + }, + { + "reference_id": "", + "url": "http://securitytracker.com/id?1023429", + "severities": [] + }, + { + "reference_id": "", + "url": "http://www.redhat.com/support/errata/RHSA-2011-0908.html", + "severities": [] + }, + { + "reference_id": "", + "url": "http://www.redhat.com/support/errata/RHSA-2011-0909.html", + "severities": [] + }, + { + "reference_id": "", + "url": "http://www.ruby-lang.org/en/news/2010/01/10/webrick-escape-sequence-injection", + "severities": [] + }, + { + "reference_id": "", + "url": "http://www.securityfocus.com/archive/1/508830/100/0/threaded", + "severities": [] + }, + { + "reference_id": "", + "url": "http://www.securityfocus.com/bid/37710", + "severities": [] + }, + { + "reference_id": "", + "url": "http://www.ush.it/team/ush/hack_httpd_escape/adv.txt", + "severities": [] + }, + { + "reference_id": "", + "url": "http://www.vupen.com/english/advisories/2010/0089", + "severities": [] + } + ], + "date_published": "2017-10-24T18:33:38+00:00" + }, + { + "aliases": [ + "CVE-2022-21831", + "GHSA-w749-p3v6-hccq" + ], + "summary": "Possible code injection vulnerability in Rails / Active Storage", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/>=7.0.0|<=7.0.2.2", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21831", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e", + "severities": [] + }, + { + "reference_id": "", + "url": "https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI", + "severities": [] + }, + { + "reference_id": "", + "url": "https://rubysec.com/advisories/CVE-2022-21831/", + "severities": [] + }, + { + "reference_id": "GHSA-w749-p3v6-hccq", + "url": "https://github.com/advisories/GHSA-w749-p3v6-hccq", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ], + "date_published": "2022-03-08T21:25:54+00:00" + }, + { + "aliases": [ + "CVE-2022-21831", + "GHSA-w749-p3v6-hccq" + ], + "summary": "Possible code injection vulnerability in Rails / Active Storage", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/>=6.1.0|<=6.1.4.6", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21831", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e", + "severities": [] + }, + { + "reference_id": "", + "url": "https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI", + "severities": [] + }, + { + "reference_id": "", + "url": "https://rubysec.com/advisories/CVE-2022-21831/", + "severities": [] + }, + { + "reference_id": "GHSA-w749-p3v6-hccq", + "url": "https://github.com/advisories/GHSA-w749-p3v6-hccq", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ], + "date_published": "2022-03-08T21:25:54+00:00" + }, + { + "aliases": [ + "CVE-2022-21831", + "GHSA-w749-p3v6-hccq" + ], + "summary": "Possible code injection vulnerability in Rails / Active Storage", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/>=6.0.0|<=6.0.4.6", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21831", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e", + "severities": [] + }, + { + "reference_id": "", + "url": "https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI", + "severities": [] + }, + { + "reference_id": "", + "url": "https://rubysec.com/advisories/CVE-2022-21831/", + "severities": [] + }, + { + "reference_id": "GHSA-w749-p3v6-hccq", + "url": "https://github.com/advisories/GHSA-w749-p3v6-hccq", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ], + "date_published": "2022-03-08T21:25:54+00:00" + }, + { + "aliases": [ + "CVE-2022-21831", + "GHSA-w749-p3v6-hccq" + ], + "summary": "Possible code injection vulnerability in Rails / Active Storage", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/>=5.2.0|<=5.2.6.2", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21831", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e", + "severities": [] + }, + { + "reference_id": "", + "url": "https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI", + "severities": [] + }, + { + "reference_id": "", + "url": "https://rubysec.com/advisories/CVE-2022-21831/", + "severities": [] + }, + { + "reference_id": "GHSA-w749-p3v6-hccq", + "url": "https://github.com/advisories/GHSA-w749-p3v6-hccq", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ], + "date_published": "2022-03-08T21:25:54+00:00" + } +] \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/gem.json b/vulnerabilities/tests/test_data/github_api/gem.json new file mode 100644 index 000000000..db91c7eda --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/gem.json @@ -0,0 +1,230 @@ +{ + "data": { + "securityVulnerabilities": { + "edges": [ + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-6mq2-37j5-w6r6" + }, + { + "type": "CVE", + "value": "CVE-2009-4492" + } + ], + "summary": "Moderate severity vulnerability that affects webrick", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2009-4492" + }, + { + "url": "https://github.com/advisories/GHSA-6mq2-37j5-w6r6" + }, + { + "url": "http://secunia.com/advisories/37949" + }, + { + "url": "http://securitytracker.com/id?1023429" + }, + { + "url": "http://www.redhat.com/support/errata/RHSA-2011-0908.html" + }, + { + "url": "http://www.redhat.com/support/errata/RHSA-2011-0909.html" + }, + { + "url": "http://www.ruby-lang.org/en/news/2010/01/10/webrick-escape-sequence-injection" + }, + { + "url": "http://www.securityfocus.com/archive/1/508830/100/0/threaded" + }, + { + "url": "http://www.securityfocus.com/bid/37710" + }, + { + "url": "http://www.ush.it/team/ush/hack_httpd_escape/adv.txt" + }, + { + "url": "http://www.vupen.com/english/advisories/2010/0089" + } + ], + "severity": "MODERATE", + "publishedAt": "2017-10-24T18:33:38Z" + }, + "package": { + "name": "webrick" + }, + "vulnerableVersionRange": "<= 1.3.1" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-w749-p3v6-hccq" + }, + { + "type": "CVE", + "value": "CVE-2022-21831" + } + ], + "summary": "Possible code injection vulnerability in Rails / Active Storage", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21831" + }, + { + "url": "https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e" + }, + { + "url": "https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI" + }, + { + "url": "https://rubysec.com/advisories/CVE-2022-21831/" + }, + { + "url": "https://github.com/advisories/GHSA-w749-p3v6-hccq" + } + ], + "severity": "HIGH", + "publishedAt": "2022-03-08T21:25:54Z" + }, + "package": { + "name": "activestorage" + }, + "vulnerableVersionRange": ">= 7.0.0, <= 7.0.2.2" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-w749-p3v6-hccq" + }, + { + "type": "CVE", + "value": "CVE-2022-21831" + } + ], + "summary": "Possible code injection vulnerability in Rails / Active Storage", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21831" + }, + { + "url": "https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e" + }, + { + "url": "https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI" + }, + { + "url": "https://rubysec.com/advisories/CVE-2022-21831/" + }, + { + "url": "https://github.com/advisories/GHSA-w749-p3v6-hccq" + } + ], + "severity": "HIGH", + "publishedAt": "2022-03-08T21:25:54Z" + }, + "package": { + "name": "activestorage" + }, + "vulnerableVersionRange": ">= 6.1.0, <= 6.1.4.6" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-w749-p3v6-hccq" + }, + { + "type": "CVE", + "value": "CVE-2022-21831" + } + ], + "summary": "Possible code injection vulnerability in Rails / Active Storage", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21831" + }, + { + "url": "https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e" + }, + { + "url": "https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI" + }, + { + "url": "https://rubysec.com/advisories/CVE-2022-21831/" + }, + { + "url": "https://github.com/advisories/GHSA-w749-p3v6-hccq" + } + ], + "severity": "HIGH", + "publishedAt": "2022-03-08T21:25:54Z" + }, + "package": { + "name": "activestorage" + }, + "vulnerableVersionRange": ">= 6.0.0, <= 6.0.4.6" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-w749-p3v6-hccq" + }, + { + "type": "CVE", + "value": "CVE-2022-21831" + } + ], + "summary": "Possible code injection vulnerability in Rails / Active Storage", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21831" + }, + { + "url": "https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e" + }, + { + "url": "https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI" + }, + { + "url": "https://rubysec.com/advisories/CVE-2022-21831/" + }, + { + "url": "https://github.com/advisories/GHSA-w749-p3v6-hccq" + } + ], + "severity": "HIGH", + "publishedAt": "2022-03-08T21:25:54Z" + }, + "package": { + "name": "activestorage" + }, + "vulnerableVersionRange": ">= 5.2.0, <= 5.2.6.2" + } + } + ], + "pageInfo": { + "hasNextPage": true, + "endCursor": "Y3Vyc29yOnYyOpK5MjAyMS0wOS0xNFQwMDozNzowNyswNTozMM1NJA==" + } + } + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/golang-expected.json b/vulnerabilities/tests/test_data/github_api/golang-expected.json new file mode 100644 index 000000000..74d1c066f --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/golang-expected.json @@ -0,0 +1,179 @@ +[ + { + "aliases": [ + "CVE-2014-9356", + "GHSA-vj3f-3286-r4pf" + ], + "summary": "Path Traversal in Docker", + "affected_packages": [ + { + "package": { + "type": "golang", + "namespace": null, + "name": "github.com/moby/moby", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:golang/<1.3.3", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2014-9356", + "severities": [] + }, + { + "reference_id": "", + "url": "https://access.redhat.com/security/cve/cve-2014-9356", + "severities": [] + }, + { + "reference_id": "", + "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1172761", + "severities": [] + }, + { + "reference_id": "", + "url": "https://groups.google.com/forum/#%21msg/docker-user/nFAz-B-n4Bw/0wr3wvLsnUwJ", + "severities": [] + }, + { + "reference_id": "", + "url": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-9356", + "severities": [] + }, + { + "reference_id": "", + "url": "http://www.securityfocus.com/archive/1/archive/1/534215/100/0/threaded", + "severities": [] + }, + { + "reference_id": "GHSA-vj3f-3286-r4pf", + "url": "https://github.com/advisories/GHSA-vj3f-3286-r4pf", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ], + "date_published": "2021-05-18T21:09:17+00:00" + }, + { + "aliases": [ + "CVE-2014-9356", + "GHSA-vj3f-3286-r4pf" + ], + "summary": "Path Traversal in Docker", + "affected_packages": [ + { + "package": { + "type": "golang", + "namespace": null, + "name": "github.com/docker/docker", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:golang/<1.3.3", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2014-9356", + "severities": [] + }, + { + "reference_id": "", + "url": "https://access.redhat.com/security/cve/cve-2014-9356", + "severities": [] + }, + { + "reference_id": "", + "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1172761", + "severities": [] + }, + { + "reference_id": "", + "url": "https://groups.google.com/forum/#%21msg/docker-user/nFAz-B-n4Bw/0wr3wvLsnUwJ", + "severities": [] + }, + { + "reference_id": "", + "url": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-9356", + "severities": [] + }, + { + "reference_id": "", + "url": "http://www.securityfocus.com/archive/1/archive/1/534215/100/0/threaded", + "severities": [] + }, + { + "reference_id": "GHSA-vj3f-3286-r4pf", + "url": "https://github.com/advisories/GHSA-vj3f-3286-r4pf", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ], + "date_published": "2021-05-18T21:09:17+00:00" + }, + { + "aliases": [ + "CVE-2021-39183", + "GHSA-2hfj-cxw7-g45p" + ], + "summary": "Unsafe inline XSS in pasting DOM element into chat", + "affected_packages": [ + { + "package": { + "type": "golang", + "namespace": null, + "name": "github.com/owncast/owncast", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:golang/<0.0.9", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "GHSA-2hfj-cxw7-g45p", + "url": "https://github.com/owncast/owncast/security/advisories/GHSA-2hfj-cxw7-g45p", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + }, + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-39183", + "severities": [] + }, + { + "reference_id": "GHSA-2hfj-cxw7-g45p", + "url": "https://github.com/advisories/GHSA-2hfj-cxw7-g45p", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ], + "date_published": "2021-12-14T21:48:16+00:00" + } +] \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/golang.json b/vulnerabilities/tests/test_data/github_api/golang.json new file mode 100644 index 000000000..03d9005d1 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/golang.json @@ -0,0 +1,138 @@ +{ + "data": { + "securityVulnerabilities": { + "edges": [ + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-vj3f-3286-r4pf" + }, + { + "type": "CVE", + "value": "CVE-2014-9356" + } + ], + "summary": "Path Traversal in Docker", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2014-9356" + }, + { + "url": "https://access.redhat.com/security/cve/cve-2014-9356" + }, + { + "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1172761" + }, + { + "url": "https://groups.google.com/forum/#%21msg/docker-user/nFAz-B-n4Bw/0wr3wvLsnUwJ" + }, + { + "url": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-9356" + }, + { + "url": "http://www.securityfocus.com/archive/1/archive/1/534215/100/0/threaded" + }, + { + "url": "https://github.com/advisories/GHSA-vj3f-3286-r4pf" + } + ], + "severity": "HIGH", + "publishedAt": "2021-05-18T21:09:17Z" + }, + "package": { + "name": "github.com/moby/moby" + }, + "vulnerableVersionRange": "< 1.3.3" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-vj3f-3286-r4pf" + }, + { + "type": "CVE", + "value": "CVE-2014-9356" + } + ], + "summary": "Path Traversal in Docker", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2014-9356" + }, + { + "url": "https://access.redhat.com/security/cve/cve-2014-9356" + }, + { + "url": "https://bugzilla.redhat.com/show_bug.cgi?id=1172761" + }, + { + "url": "https://groups.google.com/forum/#%21msg/docker-user/nFAz-B-n4Bw/0wr3wvLsnUwJ" + }, + { + "url": "https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-9356" + }, + { + "url": "http://www.securityfocus.com/archive/1/archive/1/534215/100/0/threaded" + }, + { + "url": "https://github.com/advisories/GHSA-vj3f-3286-r4pf" + } + ], + "severity": "HIGH", + "publishedAt": "2021-05-18T21:09:17Z" + }, + "package": { + "name": "github.com/docker/docker" + }, + "vulnerableVersionRange": "< 1.3.3" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-2hfj-cxw7-g45p" + }, + { + "type": "CVE", + "value": "CVE-2021-39183" + } + ], + "summary": "Unsafe inline XSS in pasting DOM element into chat", + "references": [ + { + "url": "https://github.com/owncast/owncast/security/advisories/GHSA-2hfj-cxw7-g45p" + }, + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-39183" + }, + { + "url": "https://github.com/advisories/GHSA-2hfj-cxw7-g45p" + } + ], + "severity": "HIGH", + "publishedAt": "2021-12-14T21:48:16Z" + }, + "package": { + "name": "github.com/owncast/owncast" + }, + "vulnerableVersionRange": "< 0.0.9" + } + } + ], + "pageInfo": { + "hasNextPage": true, + "endCursor": "Y3Vyc29yOnYyOpK5MjAyMS0xMi0xNVQwMzoxNzoxMSswNTozMM1R6g==" + } + } + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/inference-expected.json b/vulnerabilities/tests/test_data/github_api/inference-expected.json new file mode 100644 index 000000000..fafbc1499 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/inference-expected.json @@ -0,0 +1,424 @@ +[ + { + "vulnerability_id": null, + "aliases": [ + "CVE-2022-21831", + "GHSA-w749-p3v6-hccq" + ], + "confidence": 100, + "summary": "Possible code injection vulnerability in Rails / Active Storage", + "affected_purls": [ + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.0", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.1.rc1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.1.1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.2.rc1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.2", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.2.1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.3.rc1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.3", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.4.rc1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.4", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.4.1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.4.2", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.4.3", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.4.4", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.4.5", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.4.6", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.5", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.6", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.6.1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.6.2", + "qualifiers": null, + "subpath": null + } + ], + "fixed_purl": { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "5.2.6.3", + "qualifiers": null, + "subpath": null + }, + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21831", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e", + "severities": [] + }, + { + "reference_id": "", + "url": "https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI", + "severities": [] + }, + { + "reference_id": "", + "url": "https://rubysec.com/advisories/CVE-2022-21831/", + "severities": [] + }, + { + "reference_id": "GHSA-w749-p3v6-hccq", + "url": "https://github.com/advisories/GHSA-w749-p3v6-hccq", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ] + }, + { + "vulnerability_id": null, + "aliases": [ + "CVE-2022-21831", + "GHSA-w749-p3v6-hccq" + ], + "confidence": 100, + "summary": "Possible code injection vulnerability in Rails / Active Storage", + "affected_purls": [ + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.2.rc1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.2.rc2", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.2", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.2.1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.2.2", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.3.rc1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.3", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.3.1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.3.2", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.3.3", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.3.4", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.3.5", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.3.6", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.3.7", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.4", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.4.1", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.4.2", + "qualifiers": null, + "subpath": null + }, + { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.4.3", + "qualifiers": null, + "subpath": null + } + ], + "fixed_purl": { + "type": "gem", + "namespace": null, + "name": "activestorage", + "version": "6.0.4.4", + "qualifiers": null, + "subpath": null + }, + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21831", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e", + "severities": [] + }, + { + "reference_id": "", + "url": "https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI", + "severities": [] + }, + { + "reference_id": "", + "url": "https://rubysec.com/advisories/CVE-2022-21831/", + "severities": [] + }, + { + "reference_id": "GHSA-w749-p3v6-hccq", + "url": "https://github.com/advisories/GHSA-w749-p3v6-hccq", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ] + } +] \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/maven-expected.json b/vulnerabilities/tests/test_data/github_api/maven-expected.json new file mode 100644 index 000000000..2ad169b4f --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/maven-expected.json @@ -0,0 +1,172 @@ +[ + { + "aliases": [ + "CVE-2019-0199", + "GHSA-qcxh-w3j9-58qr" + ], + "summary": "Denial of Service in Tomcat", + "affected_packages": [ + { + "package": { + "type": "maven", + "namespace": "org.apache.tomcat.embed", + "name": "tomcat-embed-core", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:maven/>=8.0.0|<8.5.38", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "GHSA-qcxh-w3j9-58qr", + "url": "https://github.com/advisories/GHSA-qcxh-w3j9-58qr", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + } + ], + "date_published": "2021-05-24T18:12:20+00:00" + }, + { + "aliases": [ + "CVE-2019-0199", + "GHSA-qcxh-w3j9-58qr" + ], + "summary": "Denial of Service in Tomcat", + "affected_packages": [ + { + "package": { + "type": "maven", + "namespace": "org.apache.tomcat.embed", + "name": "tomcat-embed-core", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:maven/>=9.0.0|<9.0.16", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "GHSA-qcxh-w3j9-58qr", + "url": "https://github.com/advisories/GHSA-qcxh-w3j9-58qr", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ], + "date_published": "2021-05-24T18:12:20+00:00" + }, + { + "aliases": [ + "CVE-2020-1938", + "GHSA-c9hw-wf7x-jp9j" + ], + "summary": "Improper Input Validation in Tomcat", + "affected_packages": [ + { + "package": { + "type": "maven", + "namespace": "org.apache.tomcat.embed", + "name": "tomcat-embed-core", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:maven/>=7.0.0|<7.0.100", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "GHSA-c9hw-wf7x-jp9j", + "url": "https://github.com/advisories/GHSA-c9hw-wf7x-jp9j", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "LOW" + } + ] + } + ], + "date_published": "2021-05-24T18:12:20+00:00" + }, + { + "aliases": [ + "CVE-2020-1938", + "GHSA-c9hw-wf7x-jp9j" + ], + "summary": "Improper Input Validation in Tomcat", + "affected_packages": [ + { + "package": { + "type": "maven", + "namespace": "org.apache.tomcat.embed", + "name": "tomcat-embed-core", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:maven/>=8.0.0|<8.5.51", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "GHSA-c9hw-wf7x-jp9j", + "url": "https://github.com/advisories/GHSA-c9hw-wf7x-jp9j", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + } + ], + "date_published": "2021-05-24T18:12:20+00:00" + }, + { + "aliases": [ + "CVE-2020-1938", + "GHSA-c9hw-wf7x-jp9j" + ], + "summary": "Improper Input Validation in Tomcat", + "affected_packages": [ + { + "package": { + "type": "maven", + "namespace": "org.apache.tomcat.embed", + "name": "tomcat-embed-core", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:maven/>=9.0.0|<9.0.31", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "GHSA-c9hw-wf7x-jp9j", + "url": "https://github.com/advisories/GHSA-c9hw-wf7x-jp9j", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "LOW" + } + ] + } + ], + "date_published": "2021-05-24T18:12:20+00:00" + } +] \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/response.json b/vulnerabilities/tests/test_data/github_api/maven.json similarity index 99% rename from vulnerabilities/tests/test_data/github_api/response.json rename to vulnerabilities/tests/test_data/github_api/maven.json index d890394fe..147962df4 100644 --- a/vulnerabilities/tests/test_data/github_api/response.json +++ b/vulnerabilities/tests/test_data/github_api/maven.json @@ -1,4 +1,4 @@ -{"MAVEN":[{ +{ "data": { "securityVulnerabilities": { "edges": [ @@ -149,5 +149,4 @@ } } } -}] -} \ No newline at end of file +} diff --git a/vulnerabilities/tests/test_data/github_api/nuget-expected.json b/vulnerabilities/tests/test_data/github_api/nuget-expected.json new file mode 100644 index 000000000..fdea6fd2e --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/nuget-expected.json @@ -0,0 +1,178 @@ +[ + { + "aliases": [ + "CVE-2021-46703", + "GHSA-ph3v-2hq5-5qfq" + ], + "summary": "Code injection in RazorEngine", + "affected_packages": [ + { + "package": { + "type": "nuget", + "namespace": null, + "name": "RazorEngine", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:nuget/<=4.5.1-alpha001", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-46703", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/Antaris/RazorEngine/issues/585", + "severities": [] + }, + { + "reference_id": "GHSA-ph3v-2hq5-5qfq", + "url": "https://github.com/advisories/GHSA-ph3v-2hq5-5qfq", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + } + ], + "date_published": "2022-03-07T00:00:41+00:00" + }, + { + "aliases": [ + "CVE-2022-23395", + "GHSA-gcx5-3p5f-f8vp" + ], + "summary": "Prototype Pollution in jquery.cookie", + "affected_packages": [ + { + "package": { + "type": "nuget", + "namespace": null, + "name": "jquery.cookie", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:nuget/<=1.4.1", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-23395", + "severities": [] + }, + { + "reference_id": "", + "url": "https://snyk.io/test/npm/jquery.cookie/1.4.1?tab=issues", + "severities": [] + }, + { + "reference_id": "GHSA-gcx5-3p5f-f8vp", + "url": "https://github.com/advisories/GHSA-gcx5-3p5f-f8vp", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + } + ], + "date_published": "2022-03-03T00:00:51+00:00" + }, + { + "aliases": [ + "CVE-2022-0609", + "GHSA-vv6j-ww6x-54gx" + ], + "summary": "Use after free in Animation", + "affected_packages": [ + { + "package": { + "type": "nuget", + "namespace": null, + "name": "CefSharp.Wpf.NETCore", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:nuget/<=98.1.190", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "GHSA-vv6j-ww6x-54gx", + "url": "https://github.com/cefsharp/CefSharp/security/advisories/GHSA-vv6j-ww6x-54gx", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + }, + { + "reference_id": "GHSA-vv6j-ww6x-54gx", + "url": "https://github.com/advisories/GHSA-vv6j-ww6x-54gx", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "HIGH" + } + ] + } + ], + "date_published": "2022-02-22T21:51:19+00:00" + }, + { + "aliases": [ + "CVE-2017-0256", + "GHSA-j8f4-2w4p-mhjc" + ], + "summary": "Moderate severity vulnerability that affects Microsoft.AspNetCore.Mvc", + "affected_packages": [ + { + "package": { + "type": "nuget", + "namespace": null, + "name": "Microsoft.AspNetCore.Mvc.Razor.Host", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:nuget/>=1.1.0|<1.1.3", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2017-0256", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/aspnet/Announcements/issues/239", + "severities": [] + }, + { + "reference_id": "GHSA-j8f4-2w4p-mhjc", + "url": "https://github.com/advisories/GHSA-j8f4-2w4p-mhjc", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + } + ], + "date_published": "2018-10-16T19:57:48+00:00" + } +] \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/nuget.json b/vulnerabilities/tests/test_data/github_api/nuget.json new file mode 100644 index 000000000..4d5d60354 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/nuget.json @@ -0,0 +1,145 @@ +{ + "data": { + "securityVulnerabilities": { + "edges": [ + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-ph3v-2hq5-5qfq" + }, + { + "type": "CVE", + "value": "CVE-2021-46703" + } + ], + "summary": "Code injection in RazorEngine", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-46703" + }, + { + "url": "https://github.com/Antaris/RazorEngine/issues/585" + }, + { + "url": "https://github.com/advisories/GHSA-ph3v-2hq5-5qfq" + } + ], + "severity": "MODERATE", + "publishedAt": "2022-03-07T00:00:41Z" + }, + "package": { + "name": "RazorEngine" + }, + "vulnerableVersionRange": "<= 4.5.1-alpha001" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-gcx5-3p5f-f8vp" + }, + { + "type": "CVE", + "value": "CVE-2022-23395" + } + ], + "summary": "Prototype Pollution in jquery.cookie", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-23395" + }, + { + "url": "https://snyk.io/test/npm/jquery.cookie/1.4.1?tab=issues" + }, + { + "url": "https://github.com/advisories/GHSA-gcx5-3p5f-f8vp" + } + ], + "severity": "MODERATE", + "publishedAt": "2022-03-03T00:00:51Z" + }, + "package": { + "name": "jquery.cookie" + }, + "vulnerableVersionRange": "<= 1.4.1" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-vv6j-ww6x-54gx" + }, + { + "type": "CVE", + "value": "CVE-2022-0609" + } + ], + "summary": "Use after free in Animation", + "references": [ + { + "url": "https://github.com/cefsharp/CefSharp/security/advisories/GHSA-vv6j-ww6x-54gx" + }, + { + "url": "https://github.com/advisories/GHSA-vv6j-ww6x-54gx" + } + ], + "severity": "HIGH", + "publishedAt": "2022-02-22T21:51:19Z" + }, + "package": { + "name": "CefSharp.Wpf.NETCore" + }, + "vulnerableVersionRange": "<= 98.1.190" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-j8f4-2w4p-mhjc" + }, + { + "type": "CVE", + "value": "CVE-2017-0256" + } + ], + "summary": "Moderate severity vulnerability that affects Microsoft.AspNetCore.Mvc", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2017-0256" + }, + { + "url": "https://github.com/aspnet/Announcements/issues/239" + }, + { + "url": "https://github.com/advisories/GHSA-j8f4-2w4p-mhjc" + } + ], + "severity": "MODERATE", + "publishedAt": "2018-10-16T19:57:48Z" + }, + "package": { + "name": "Microsoft.AspNetCore.Mvc.Razor.Host" + }, + "vulnerableVersionRange": ">= 1.1.0, < 1.1.3" + } + } + ], + "pageInfo": { + "hasNextPage": true, + "endCursor": "Y3Vyc29yOnYyOpK5MjAyMS0wNC0wNVQyMToyNzo0MiswNTozMM0Y8A==" + } + } + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/pypi-expected.json b/vulnerabilities/tests/test_data/github_api/pypi-expected.json new file mode 100644 index 000000000..05d898bb6 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/pypi-expected.json @@ -0,0 +1,217 @@ +[ + { + "aliases": [ + "GHSA-4fx9-vc88-q2xc" + ], + "summary": "Infinite loop in Pillow", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": null, + "name": "pillow", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:pypi/<9.0.0", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://github.com/python-pillow/Pillow/commit/baae9ec4b67c68e3adaf1208cf54e8de5e38a6fd", + "severities": [] + }, + { + "reference_id": "", + "url": "https://pillow.readthedocs.io/en/stable/releasenotes/9.0.0.html#ensure-jpegimageplugin-stops-at-the-end-of-a-truncated-file", + "severities": [] + }, + { + "reference_id": "GHSA-4fx9-vc88-q2xc", + "url": "https://github.com/advisories/GHSA-4fx9-vc88-q2xc", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "LOW" + } + ] + } + ], + "date_published": "2022-03-11T23:39:27+00:00" + }, + { + "aliases": [ + "CVE-2022-22817", + "GHSA-8vj2-vxx3-667w" + ], + "summary": "Arbitrary expression injection in Pillow", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": null, + "name": "pillow", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:pypi/<9.0.1", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-22817", + "severities": [] + }, + { + "reference_id": "", + "url": "https://pillow.readthedocs.io/en/stable/releasenotes/9.0.0.html#restrict-builtins-available-to-imagemath-eval", + "severities": [] + }, + { + "reference_id": "", + "url": "https://lists.debian.org/debian-lts-announce/2022/01/msg00018.html", + "severities": [] + }, + { + "reference_id": "", + "url": "https://www.debian.org/security/2022/dsa-5053", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/python-pillow/Pillow/commit/8531b01d6cdf0b70f256f93092caa2a5d91afc11", + "severities": [] + }, + { + "reference_id": "", + "url": "https://pillow.readthedocs.io/en/stable/releasenotes/9.0.1.html#security", + "severities": [] + }, + { + "reference_id": "GHSA-8vj2-vxx3-667w", + "url": "https://github.com/advisories/GHSA-8vj2-vxx3-667w", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "CRITICAL" + } + ] + } + ], + "date_published": "2022-01-12T20:07:33+00:00" + }, + { + "aliases": [ + "CVE-2022-24303", + "GHSA-9j59-75qj-795w" + ], + "summary": "Path traversal in Pillow", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": null, + "name": "pillow", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:pypi/<9.0.1", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-24303", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/python-pillow/Pillow/commit/427221ef5f19157001bf8b1ad7cfe0b905ca8c26", + "severities": [] + }, + { + "reference_id": "", + "url": "https://pillow.readthedocs.io/en/stable/releasenotes/9.0.1.html#security", + "severities": [] + }, + { + "reference_id": "GHSA-9j59-75qj-795w", + "url": "https://github.com/advisories/GHSA-9j59-75qj-795w", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + } + ], + "date_published": "2022-03-11T23:10:32+00:00" + }, + { + "aliases": [ + "CVE-2022-23578", + "GHSA-8r7c-3cm2-3h8f" + ], + "summary": "Memory leak in Tensorflow", + "affected_packages": [ + { + "package": { + "type": "pypi", + "namespace": null, + "name": "tensorflow", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:pypi/<2.5.3", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "GHSA-8r7c-3cm2-3h8f", + "url": "https://github.com/tensorflow/tensorflow/security/advisories/GHSA-8r7c-3cm2-3h8f", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/tensorflow/tensorflow/commit/c79ccba517dbb1a0ccb9b01ee3bd2a63748b60dd", + "severities": [] + }, + { + "reference_id": "", + "url": "https://github.com/tensorflow/tensorflow/blob/a1320ec1eac186da1d03f033109191f715b2b130/tensorflow/core/common_runtime/immutable_executor_state.cc#L84-L262", + "severities": [] + }, + { + "reference_id": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-23578", + "severities": [] + }, + { + "reference_id": "GHSA-8r7c-3cm2-3h8f", + "url": "https://github.com/advisories/GHSA-8r7c-3cm2-3h8f", + "severities": [ + { + "system": "cvssv3.1_qr", + "value": "MODERATE" + } + ] + } + ], + "date_published": "2022-02-10T00:33:13+00:00" + } +] \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/pypi.json b/vulnerabilities/tests/test_data/github_api/pypi.json new file mode 100644 index 000000000..2f63948e2 --- /dev/null +++ b/vulnerabilities/tests/test_data/github_api/pypi.json @@ -0,0 +1,165 @@ +{ + "data": { + "securityVulnerabilities": { + "edges": [ + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-4fx9-vc88-q2xc" + } + ], + "summary": "Infinite loop in Pillow", + "references": [ + { + "url": "https://github.com/python-pillow/Pillow/commit/baae9ec4b67c68e3adaf1208cf54e8de5e38a6fd" + }, + { + "url": "https://pillow.readthedocs.io/en/stable/releasenotes/9.0.0.html#ensure-jpegimageplugin-stops-at-the-end-of-a-truncated-file" + }, + { + "url": "https://github.com/advisories/GHSA-4fx9-vc88-q2xc" + } + ], + "severity": "LOW", + "publishedAt": "2022-03-11T23:39:27Z" + }, + "package": { + "name": "Pillow" + }, + "vulnerableVersionRange": "< 9.0.0" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-8vj2-vxx3-667w" + }, + { + "type": "CVE", + "value": "CVE-2022-22817" + } + ], + "summary": "Arbitrary expression injection in Pillow", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-22817" + }, + { + "url": "https://pillow.readthedocs.io/en/stable/releasenotes/9.0.0.html#restrict-builtins-available-to-imagemath-eval" + }, + { + "url": "https://lists.debian.org/debian-lts-announce/2022/01/msg00018.html" + }, + { + "url": "https://www.debian.org/security/2022/dsa-5053" + }, + { + "url": "https://github.com/python-pillow/Pillow/commit/8531b01d6cdf0b70f256f93092caa2a5d91afc11" + }, + { + "url": "https://pillow.readthedocs.io/en/stable/releasenotes/9.0.1.html#security" + }, + { + "url": "https://github.com/advisories/GHSA-8vj2-vxx3-667w" + } + ], + "severity": "CRITICAL", + "publishedAt": "2022-01-12T20:07:33Z" + }, + "package": { + "name": "Pillow" + }, + "vulnerableVersionRange": "< 9.0.1" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-9j59-75qj-795w" + }, + { + "type": "CVE", + "value": "CVE-2022-24303" + } + ], + "summary": "Path traversal in Pillow", + "references": [ + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-24303" + }, + { + "url": "https://github.com/python-pillow/Pillow/commit/427221ef5f19157001bf8b1ad7cfe0b905ca8c26" + }, + { + "url": "https://pillow.readthedocs.io/en/stable/releasenotes/9.0.1.html#security" + }, + { + "url": "https://github.com/advisories/GHSA-9j59-75qj-795w" + } + ], + "severity": "MODERATE", + "publishedAt": "2022-03-11T23:10:32Z" + }, + "package": { + "name": "Pillow" + }, + "vulnerableVersionRange": "< 9.0.1" + } + }, + { + "node": { + "advisory": { + "identifiers": [ + { + "type": "GHSA", + "value": "GHSA-8r7c-3cm2-3h8f" + }, + { + "type": "CVE", + "value": "CVE-2022-23578" + } + ], + "summary": "Memory leak in Tensorflow", + "references": [ + { + "url": "https://github.com/tensorflow/tensorflow/security/advisories/GHSA-8r7c-3cm2-3h8f" + }, + { + "url": "https://github.com/tensorflow/tensorflow/commit/c79ccba517dbb1a0ccb9b01ee3bd2a63748b60dd" + }, + { + "url": "https://github.com/tensorflow/tensorflow/blob/a1320ec1eac186da1d03f033109191f715b2b130/tensorflow/core/common_runtime/immutable_executor_state.cc#L84-L262" + }, + { + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-23578" + }, + { + "url": "https://github.com/advisories/GHSA-8r7c-3cm2-3h8f" + } + ], + "severity": "MODERATE", + "publishedAt": "2022-02-10T00:33:13Z" + }, + "package": { + "name": "tensorflow" + }, + "vulnerableVersionRange": "< 2.5.3" + } + } + ], + "pageInfo": { + "hasNextPage": true, + "endCursor": "Y3Vyc29yOnYyOpK5MjAyMi0wMi0wNVQwMTo0NTowNCswNTozMM1V5g==" + } + } + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/github_api/release_response.json b/vulnerabilities/tests/test_data/github_api/release_response.json deleted file mode 100644 index a5f1d6111..000000000 --- a/vulnerabilities/tests/test_data/github_api/release_response.json +++ /dev/null @@ -1,76 +0,0 @@ -[ - { - "url": "https://api.github.com/repos/nexB/vulnerablecode/releases/32748782", - "assets_url": "https://api.github.com/repos/nexB/vulnerablecode/releases/32748782/assets", - "upload_url": "https://uploads.github.com/repos/nexB/vulnerablecode/releases/32748782/assets{?name,label}", - "html_url": "https://github.com/nexB/vulnerablecode/releases/tag/v20.10", - "id": 32748782, - "author": { - "login": "pombredanne", - "id": 675997, - "node_id": "MDQ6VXNlcjY3NTk5Nw==", - "avatar_url": "https://avatars.githubusercontent.com/u/675997?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/pombredanne", - "html_url": "https://github.com/pombredanne", - "followers_url": "https://api.github.com/users/pombredanne/followers", - "following_url": "https://api.github.com/users/pombredanne/following{/other_user}", - "gists_url": "https://api.github.com/users/pombredanne/gists{/gist_id}", - "starred_url": "https://api.github.com/users/pombredanne/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/pombredanne/subscriptions", - "organizations_url": "https://api.github.com/users/pombredanne/orgs", - "repos_url": "https://api.github.com/users/pombredanne/repos", - "events_url": "https://api.github.com/users/pombredanne/events{/privacy}", - "received_events_url": "https://api.github.com/users/pombredanne/received_events", - "type": "User", - "site_admin": false - }, - "node_id": "MDc6UmVsZWFzZTMyNzQ4Nzgy", - "tag_name": "v20.10", - "target_commitish": "main", - "name": "v20.10", - "draft": false, - "prerelease": false, - "created_at": "2020-09-28T12:31:16Z", - "published_at": "2020-10-19T10:46:17Z", - "assets": [ - { - "url": "https://api.github.com/repos/nexB/vulnerablecode/releases/assets/27230021", - "id": 27230021, - "node_id": "MDEyOlJlbGVhc2VBc3NldDI3MjMwMDIx", - "name": "vulnerablecode-2020-10-19.json.xz", - "label": null, - "uploader": { - "login": "pombredanne", - "id": 675997, - "node_id": "MDQ6VXNlcjY3NTk5Nw==", - "avatar_url": "https://avatars.githubusercontent.com/u/675997?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/pombredanne", - "html_url": "https://github.com/pombredanne", - "followers_url": "https://api.github.com/users/pombredanne/followers", - "following_url": "https://api.github.com/users/pombredanne/following{/other_user}", - "gists_url": "https://api.github.com/users/pombredanne/gists{/gist_id}", - "starred_url": "https://api.github.com/users/pombredanne/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/pombredanne/subscriptions", - "organizations_url": "https://api.github.com/users/pombredanne/orgs", - "repos_url": "https://api.github.com/users/pombredanne/repos", - "events_url": "https://api.github.com/users/pombredanne/events{/privacy}", - "received_events_url": "https://api.github.com/users/pombredanne/received_events", - "type": "User", - "site_admin": false - }, - "content_type": "application/x-xz", - "state": "uploaded", - "size": 13603356, - "download_count": 20, - "created_at": "2020-10-20T09:40:08Z", - "updated_at": "2020-10-20T09:40:25Z", - "browser_download_url": "https://github.com/nexB/vulnerablecode/releases/download/v20.10/vulnerablecode-2020-10-19.json.xz" - } - ], - "tarball_url": "https://api.github.com/repos/nexB/vulnerablecode/tarball/v20.10", - "zipball_url": "https://api.github.com/repos/nexB/vulnerablecode/zipball/v20.10", - "body": "This release comes with the new calver versioning scheme and an initial data dump.\r\n\r\nTo load the JSON data attached here:\r\n- extract it with `unxz vulnerablecode-2020-10-19.json.xz`\r\n- run `DJANGO_DEV=1 python manage.py loaddata vulnerablecode-2020-10-19.json`\r\n\r\nThe data import is not optimized yet and takes a long time." - } -] diff --git a/vulnerabilities/tests/test_data/package_manager_data/nuget-data.json b/vulnerabilities/tests/test_data/package_manager_data/nuget-data.json new file mode 100644 index 000000000..d73cdfddf --- /dev/null +++ b/vulnerabilities/tests/test_data/package_manager_data/nuget-data.json @@ -0,0 +1,626 @@ +{ + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json", + "@type": [ + "catalog:CatalogRoot", + "PackageRegistration", + "catalog:Permalink" + ], + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "count": 2, + "items": [ + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json#page/2.1.0/4.1.5-beta1", + "@type": "catalog:CatalogPage", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "count": 64, + "items": [ + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/2.1.0.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.2.1.0.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott, Ben Dornis", + "description": "A templating engine built upon Microsoft's Razor parsing technology. RazorEngine allows you to use Razor syntax to build robust templates.", + "iconUrl": "", + "id": "RazorEngine", + "language": "en-US", + "licenseExpression": "", + "licenseUrl": "http://razorengine.codeplex.com/license", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/2.1.0/razorengine.2.1.0.nupkg", + "projectUrl": "http://razorengine.codeplex.com/", + "published": "2011-01-22T13:34:08.55+00:00", + "requireLicenseAcceptance": false, + "summary": "", + "tags": [ + "razor", + "razorengine", + "templating" + ], + "title": "RazorEngine", + "version": "2.1.0" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/2.1.0/razorengine.2.1.0.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.0.0.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.0.0.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott,Ben Dornis", + "description": "A templating engine built upon Microsoft's Razor parsing technology. RazorEngine allows you to use Razor syntax to build robust templates.", + "iconUrl": "", + "id": "RazorEngine", + "language": "en-US", + "licenseExpression": "", + "licenseUrl": "http://razorengine.codeplex.com/license", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.0.0/razorengine.3.0.0.nupkg", + "projectUrl": "http://razorengine.codeplex.com/", + "published": "2011-11-24T00:26:02.527+00:00", + "requireLicenseAcceptance": false, + "summary": "", + "tags": [ + "razor", + "razorengine", + "templating" + ], + "title": "RazorEngine v3.0.0beta", + "version": "3.0.0" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.0.0/razorengine.3.0.0.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.0.3.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.0.3.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott,Ben Dornis", + "description": "A templating engine built upon Microsoft's Razor parsing technology. RazorEngine allows you to use Razor syntax to build robust templates.", + "iconUrl": "", + "id": "RazorEngine", + "language": "en-US", + "licenseExpression": "", + "licenseUrl": "http://razorengine.codeplex.com/license", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.0.3/razorengine.3.0.3.nupkg", + "projectUrl": "http://razorengine.codeplex.com/", + "published": "2011-11-27T13:50:02.063+00:00", + "requireLicenseAcceptance": false, + "summary": "", + "tags": [ + "razor", + "razorengine", + "templating" + ], + "title": "RazorEngine v3.0.3beta", + "version": "3.0.3" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.0.3/razorengine.3.0.3.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.0.4.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.0.4.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott,Ben Dornis", + "description": "A templating engine built upon Microsoft's Razor parsing technology. RazorEngine allows you to use Razor syntax to build robust templates.", + "iconUrl": "", + "id": "RazorEngine", + "language": "en-US", + "licenseExpression": "", + "licenseUrl": "http://razorengine.codeplex.com/license", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.0.4/razorengine.3.0.4.nupkg", + "projectUrl": "http://razorengine.codeplex.com/", + "published": "2011-12-12T10:18:33.38+00:00", + "requireLicenseAcceptance": false, + "summary": "", + "tags": [ + "razor", + "razorengine", + "templating" + ], + "title": "RazorEngine v3.0.4beta", + "version": "3.0.4" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.0.4/razorengine.3.0.4.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.0.5.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.0.5.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott,Ben Dornis", + "description": "A templating engine built upon Microsoft's Razor parsing technology. RazorEngine allows you to use Razor syntax to build robust templates.", + "iconUrl": "", + "id": "RazorEngine", + "language": "en-US", + "licenseExpression": "", + "licenseUrl": "http://razorengine.codeplex.com/license", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.0.5/razorengine.3.0.5.nupkg", + "projectUrl": "http://razorengine.codeplex.com/", + "published": "2011-12-12T12:00:25.947+00:00", + "requireLicenseAcceptance": false, + "summary": "", + "tags": [ + "razor", + "razorengine", + "templating" + ], + "title": "RazorEngine v3.0.5beta", + "version": "3.0.5" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.0.5/razorengine.3.0.5.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.0.6.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.0.6.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott,Ben Dornis", + "description": "A templating engine built upon Microsoft's Razor parsing technology. RazorEngine allows you to use Razor syntax to build robust templates.", + "iconUrl": "", + "id": "RazorEngine", + "language": "en-US", + "licenseExpression": "", + "licenseUrl": "http://razorengine.codeplex.com/license", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.0.6/razorengine.3.0.6.nupkg", + "projectUrl": "http://razorengine.codeplex.com/", + "published": "2012-01-02T21:10:43.403+00:00", + "requireLicenseAcceptance": false, + "summary": "", + "tags": [ + "razor", + "razorengine", + "templating" + ], + "title": "RazorEngine v3.0.6beta", + "version": "3.0.6" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.0.6/razorengine.3.0.6.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.4.0.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.4.0.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott,Ben Dornis", + "dependencyGroups": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.4.0.json#dependencygroup", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.4.0.json#dependencygroup/microsoft.aspnet.razor", + "@type": "PackageDependency", + "id": "Microsoft.AspNet.Razor", + "range": "[3.0.0, )", + "registration": "https://api.nuget.org/v3/registration5-semver1/microsoft.aspnet.razor/index.json" + } + ] + } + ], + "description": "Simple templating using Razor syntax.", + "iconUrl": "", + "id": "RazorEngine", + "language": "", + "licenseExpression": "", + "licenseUrl": "", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.4.0/razorengine.3.4.0.nupkg", + "projectUrl": "", + "published": "2013-10-20T13:32:30.837+00:00", + "requireLicenseAcceptance": false, + "summary": "", + "tags": [ + "" + ], + "title": "RazorEngine", + "version": "3.4.0" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.4.0/razorengine.3.4.0.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.4.1.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.4.1.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott,Ben Dornis", + "dependencyGroups": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.4.1.json#dependencygroup", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.4.1.json#dependencygroup/microsoft.aspnet.razor", + "@type": "PackageDependency", + "id": "Microsoft.AspNet.Razor", + "range": "[3.0.0, )", + "registration": "https://api.nuget.org/v3/registration5-semver1/microsoft.aspnet.razor/index.json" + } + ] + } + ], + "description": "Simple templating using Razor syntax.", + "iconUrl": "", + "id": "RazorEngine", + "language": "", + "licenseExpression": "", + "licenseUrl": "", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.4.1/razorengine.3.4.1.nupkg", + "projectUrl": "https://github.com/Antaris/RazorEngine/wiki", + "published": "2014-01-17T09:17:43.68+00:00", + "requireLicenseAcceptance": false, + "summary": "", + "tags": [ + "" + ], + "title": "RazorEngine", + "version": "3.4.1" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.4.1/razorengine.3.4.1.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.5.0-beta2.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0-beta2.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott, Ben Dornis, Matthias Dittrich", + "dependencyGroups": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0-beta2.json#dependencygroup/.netframework4.0", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0-beta2.json#dependencygroup/.netframework4.0/microsoft.aspnet.razor", + "@type": "PackageDependency", + "id": "Microsoft.AspNet.Razor", + "range": "[2.0.30506, 2.0.30506]", + "registration": "https://api.nuget.org/v3/registration5-semver1/microsoft.aspnet.razor/index.json" + } + ], + "targetFramework": ".NETFramework4.0" + }, + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0-beta2.json#dependencygroup/.netframework4.5", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0-beta2.json#dependencygroup/.netframework4.5/microsoft.aspnet.razor", + "@type": "PackageDependency", + "id": "Microsoft.AspNet.Razor", + "range": "[3.0.0, )", + "registration": "https://api.nuget.org/v3/registration5-semver1/microsoft.aspnet.razor/index.json" + } + ], + "targetFramework": ".NETFramework4.5" + } + ], + "description": "RazorEngine - A Templating Engine based on the Razor parser.", + "iconUrl": "", + "id": "RazorEngine", + "language": "", + "licenseExpression": "", + "licenseUrl": "https://github.com/Antaris/RazorEngine/blob/master/doc/LICENSE.md", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.5.0-beta2/razorengine.3.5.0-beta2.nupkg", + "projectUrl": "https://github.com/Antaris/RazorEngine", + "published": "2015-01-01T14:09:28.71+00:00", + "requireLicenseAcceptance": false, + "summary": "Simple templating using Razor syntax.", + "tags": [ + "C#", + "razor", + "template", + "engine", + "programming" + ], + "title": "RazorEngine", + "version": "3.5.0-beta2" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.5.0-beta2/razorengine.3.5.0-beta2.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.5.0-beta3.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0-beta3.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott, Ben Dornis, Matthias Dittrich", + "dependencyGroups": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0-beta3.json#dependencygroup/.netframework4.0", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0-beta3.json#dependencygroup/.netframework4.0/microsoft.aspnet.razor", + "@type": "PackageDependency", + "id": "Microsoft.AspNet.Razor", + "range": "[2.0.30506, 2.0.30506]", + "registration": "https://api.nuget.org/v3/registration5-semver1/microsoft.aspnet.razor/index.json" + } + ], + "targetFramework": ".NETFramework4.0" + }, + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0-beta3.json#dependencygroup/.netframework4.5", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0-beta3.json#dependencygroup/.netframework4.5/microsoft.aspnet.razor", + "@type": "PackageDependency", + "id": "Microsoft.AspNet.Razor", + "range": "[3.0.0, )", + "registration": "https://api.nuget.org/v3/registration5-semver1/microsoft.aspnet.razor/index.json" + } + ], + "targetFramework": ".NETFramework4.5" + } + ], + "description": "RazorEngine - A Templating Engine based on the Razor parser.", + "iconUrl": "", + "id": "RazorEngine", + "language": "", + "licenseExpression": "", + "licenseUrl": "https://github.com/Antaris/RazorEngine/blob/master/doc/LICENSE.md", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.5.0-beta3/razorengine.3.5.0-beta3.nupkg", + "projectUrl": "https://github.com/Antaris/RazorEngine", + "published": "2015-01-06T17:39:25.147+00:00", + "requireLicenseAcceptance": false, + "summary": "Simple templating using Razor syntax.", + "tags": [ + "C#", + "razor", + "template", + "engine", + "programming" + ], + "title": "RazorEngine", + "version": "3.5.0-beta3" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.5.0-beta3/razorengine.3.5.0-beta3.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.5.0.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott, Ben Dornis, Matthias Dittrich", + "dependencyGroups": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0.json#dependencygroup/.netframework4.0", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0.json#dependencygroup/.netframework4.0/microsoft.aspnet.razor", + "@type": "PackageDependency", + "id": "Microsoft.AspNet.Razor", + "range": "[2.0.30506, 2.0.30506]", + "registration": "https://api.nuget.org/v3/registration5-semver1/microsoft.aspnet.razor/index.json" + } + ], + "targetFramework": ".NETFramework4.0" + }, + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0.json#dependencygroup/.netframework4.5", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.0.json#dependencygroup/.netframework4.5/microsoft.aspnet.razor", + "@type": "PackageDependency", + "id": "Microsoft.AspNet.Razor", + "range": "[3.0.0, )", + "registration": "https://api.nuget.org/v3/registration5-semver1/microsoft.aspnet.razor/index.json" + } + ], + "targetFramework": ".NETFramework4.5" + } + ], + "description": "RazorEngine - A Templating Engine based on the Razor parser.", + "iconUrl": "", + "id": "RazorEngine", + "language": "", + "licenseExpression": "", + "licenseUrl": "https://github.com/Antaris/RazorEngine/blob/master/doc/LICENSE.md", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.5.0/razorengine.3.5.0.nupkg", + "projectUrl": "https://github.com/Antaris/RazorEngine", + "published": "2015-01-14T02:01:58.853+00:00", + "requireLicenseAcceptance": false, + "summary": "Simple templating using Razor syntax.", + "tags": [ + "C#", + "razor", + "template", + "engine", + "programming" + ], + "title": "RazorEngine", + "version": "3.5.0" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.5.0/razorengine.3.5.0.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + }, + { + "@id": "https://api.nuget.org/v3/registration5-semver1/razorengine/3.5.1.json", + "@type": "Package", + "commitId": "63ff8119-d8af-49a0-9906-28cba30dd479", + "commitTimeStamp": "2022-03-11T23:18:21.9196828+00:00", + "catalogEntry": { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.1.json", + "@type": "PackageDetails", + "authors": "Matthew Abbott, Ben Dornis, Matthias Dittrich", + "dependencyGroups": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.1.json#dependencygroup/.netframework4.0", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.1.json#dependencygroup/.netframework4.0/microsoft.aspnet.razor", + "@type": "PackageDependency", + "id": "Microsoft.AspNet.Razor", + "range": "[2.0.30506, 2.0.30506]", + "registration": "https://api.nuget.org/v3/registration5-semver1/microsoft.aspnet.razor/index.json" + } + ], + "targetFramework": ".NETFramework4.0" + }, + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.1.json#dependencygroup/.netframework4.5", + "@type": "PackageDependencyGroup", + "dependencies": [ + { + "@id": "https://api.nuget.org/v3/catalog0/data/2022.03.11.23.17.27/razorengine.3.5.1.json#dependencygroup/.netframework4.5/microsoft.aspnet.razor", + "@type": "PackageDependency", + "id": "Microsoft.AspNet.Razor", + "range": "[3.0.0, )", + "registration": "https://api.nuget.org/v3/registration5-semver1/microsoft.aspnet.razor/index.json" + } + ], + "targetFramework": ".NETFramework4.5" + } + ], + "description": "RazorEngine - A Templating Engine based on the Razor parser.", + "iconUrl": "", + "id": "RazorEngine", + "language": "", + "licenseExpression": "", + "licenseUrl": "https://github.com/Antaris/RazorEngine/blob/master/doc/LICENSE.md", + "listed": true, + "minClientVersion": "", + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.5.1/razorengine.3.5.1.nupkg", + "projectUrl": "https://github.com/Antaris/RazorEngine", + "published": "2015-01-23T01:05:44.447+00:00", + "requireLicenseAcceptance": false, + "summary": "Simple templating using Razor syntax.", + "tags": [ + "C#", + "razor", + "template", + "engine", + "programming" + ], + "title": "RazorEngine", + "version": "3.5.1" + }, + "packageContent": "https://api.nuget.org/v3-flatcontainer/razorengine/3.5.1/razorengine.3.5.1.nupkg", + "registration": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json" + } + ], + "parent": "https://api.nuget.org/v3/registration5-semver1/razorengine/index.json", + "lower": "2.1.0", + "upper": "4.1.5-beta1" + } + ], + "@context": { + "@vocab": "http://schema.nuget.org/schema#", + "catalog": "http://schema.nuget.org/catalog#", + "xsd": "http://www.w3.org/2001/XMLSchema#", + "items": { + "@id": "catalog:item", + "@container": "@set" + }, + "commitTimeStamp": { + "@id": "catalog:commitTimeStamp", + "@type": "xsd:dateTime" + }, + "commitId": { + "@id": "catalog:commitId" + }, + "count": { + "@id": "catalog:count" + }, + "parent": { + "@id": "catalog:parent", + "@type": "@id" + }, + "tags": { + "@id": "tag", + "@container": "@set" + }, + "reasons": { + "@container": "@set" + }, + "packageTargetFrameworks": { + "@id": "packageTargetFramework", + "@container": "@set" + }, + "dependencyGroups": { + "@id": "dependencyGroup", + "@container": "@set" + }, + "dependencies": { + "@id": "dependency", + "@container": "@set" + }, + "packageContent": { + "@type": "@id" + }, + "published": { + "@type": "xsd:dateTime" + }, + "registration": { + "@type": "@id" + } + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/package_manager_data/pypi.json b/vulnerabilities/tests/test_data/package_manager_data/pypi.json new file mode 100644 index 000000000..77a6cf498 --- /dev/null +++ b/vulnerabilities/tests/test_data/package_manager_data/pypi.json @@ -0,0 +1,560 @@ +{ + "releases": { + "1.0.1": [], + "1.0.2": [], + "1.0.3": [], + "1.0.4": [], + "1.1": [], + "1.1.1": [], + "1.1.2": [], + "1.1.3": [ + { + "comment_text": "", + "digests": { + "md5": "52848c23dbc120fe0b2a8e7189b20306", + "sha256": "0e5034cf8046ba77c62e95a45d776d2c59998b26f181ceaf5cec516115e3f85a" + }, + "downloads": -1, + "filename": "Django-1.1.3.tar.gz", + "has_sig": false, + "md5_digest": "52848c23dbc120fe0b2a8e7189b20306", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 5748608, + "upload_time": "2010-12-23T05:14:23", + "upload_time_iso_8601": "2010-12-23T05:14:23.509436Z", + "url": "https://files.pythonhosted.org/packages/8f/1f/74aa91b56dea5847b62e11ce6737db82c6446561bddc20ca80fa5df025cc/Django-1.1.3.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.1.4": [ + { + "comment_text": "", + "digests": { + "md5": "e818668acc4de944f85e494ac80f1e7d", + "sha256": "1f9d48a741f98951e65818e167c84c407d1c322efcfd4cb419384773ea793dee" + }, + "downloads": -1, + "filename": "Django-1.1.4.tar.gz", + "has_sig": false, + "md5_digest": "e818668acc4de944f85e494ac80f1e7d", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 5750441, + "upload_time": "2011-02-09T04:13:07", + "upload_time_iso_8601": "2011-02-09T04:13:07.000075Z", + "url": "https://files.pythonhosted.org/packages/00/01/c29275c88671d5e4089388c54ecbd72ed64f8d472067f765e52f767d472a/Django-1.1.4.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10": [ + { + "comment_text": "", + "digests": { + "md5": "36e17cd1a0255258e1dec1bbb8808040", + "sha256": "9c60f4a801bf7c26bd6824c1062550c12c373344116703461c18cc258f8c9320" + }, + "downloads": -1, + "filename": "Django-1.10-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "36e17cd1a0255258e1dec1bbb8808040", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6795167, + "upload_time": "2016-08-01T18:32:07", + "upload_time_iso_8601": "2016-08-01T18:32:07.351674Z", + "url": "https://files.pythonhosted.org/packages/4b/4c/059f68d8f029f7054e4e6bb0b1ed2fde7f28d07a3727325727d5a95ae1b8/Django-1.10-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "939e4d989b93a4e12e4ec5d98fcdb4f5", + "sha256": "46b868d68e5fd69dd9e05a0a7900df91786097e30b2aa6f065dd7fa3b22f7005" + }, + "downloads": -1, + "filename": "Django-1.10.tar.gz", + "has_sig": true, + "md5_digest": "939e4d989b93a4e12e4ec5d98fcdb4f5", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7691063, + "upload_time": "2016-08-01T18:32:16", + "upload_time_iso_8601": "2016-08-01T18:32:16.280614Z", + "url": "https://files.pythonhosted.org/packages/18/5c/3cd8989b2226c55a1faf66f1a110e76cba6e6ca5d9dd15fb469fb636f378/Django-1.10.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10.1": [ + { + "comment_text": "", + "digests": { + "md5": "6b50546050424bf01fd9687de3096855", + "sha256": "3d689905cd0635bbb33b87f9a5df7ca70a3db206faae4ec58cda5e7f5f47050d" + }, + "downloads": -1, + "filename": "Django-1.10.1-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "6b50546050424bf01fd9687de3096855", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6796229, + "upload_time": "2016-09-01T23:17:41", + "upload_time_iso_8601": "2016-09-01T23:17:41.185068Z", + "url": "https://files.pythonhosted.org/packages/6c/cf/d6ab0edb891865ef86b3e3d7290c162f57c363cf880099bbe94229806f56/Django-1.10.1-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "037d07e126eecc15a5fbf5221dd4081b", + "sha256": "d6e6c5b25cb67f46afd7c82f536529b11981183423dad8932e15bce93d1a24f3" + }, + "downloads": -1, + "filename": "Django-1.10.1.tar.gz", + "has_sig": true, + "md5_digest": "037d07e126eecc15a5fbf5221dd4081b", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7700057, + "upload_time": "2016-09-01T23:18:18", + "upload_time_iso_8601": "2016-09-01T23:18:18.672706Z", + "url": "https://files.pythonhosted.org/packages/0a/9e/e76cca958089cd0317ab46cb91f0ed36274900e48829c949b2e33d2a4469/Django-1.10.1.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10.2": [ + { + "comment_text": "", + "digests": { + "md5": "0b29f13cc5907dcf6f06649ce77be7c2", + "sha256": "4d48ab8e84a7c8b2bc4b2f4f199bc3a8bfcc9cbdbc29e355ac5c44a501d73a1a" + }, + "downloads": -1, + "filename": "Django-1.10.2-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "0b29f13cc5907dcf6f06649ce77be7c2", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6832895, + "upload_time": "2016-10-01T20:05:18", + "upload_time_iso_8601": "2016-10-01T20:05:18.594291Z", + "url": "https://files.pythonhosted.org/packages/8a/09/46f790104abca7eb93786139d3adde9366b1afd59a77b583a1f310dc8cbd/Django-1.10.2-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "5342e77374b2acd2eafa86d2bb68f8c9", + "sha256": "e127f12a0bfb34843b6e8c82f91e26fff6445a7ca91d222c0794174cf97cbce1" + }, + "downloads": -1, + "filename": "Django-1.10.2.tar.gz", + "has_sig": true, + "md5_digest": "5342e77374b2acd2eafa86d2bb68f8c9", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7724987, + "upload_time": "2016-10-01T20:05:31", + "upload_time_iso_8601": "2016-10-01T20:05:31.330942Z", + "url": "https://files.pythonhosted.org/packages/57/9e/59444485f092b6ed4f1931e7d2e13b67fdab967c041d02f58a0d1dab8c23/Django-1.10.2.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10.3": [ + { + "comment_text": "", + "digests": { + "md5": "dd66eaec09d7a3810c40b01c53535b37", + "sha256": "94426cc28d8721fbf13c333053f08d32427671a4ca7986f7030fc82bdf9c88c1" + }, + "downloads": -1, + "filename": "Django-1.10.3-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "dd66eaec09d7a3810c40b01c53535b37", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6833335, + "upload_time": "2016-11-01T13:56:54", + "upload_time_iso_8601": "2016-11-01T13:56:54.139706Z", + "url": "https://files.pythonhosted.org/packages/0e/ab/16abddb9ab7ee46a26e04a0c8ba1f02b9412a77927dec699c1af6d0070f8/Django-1.10.3-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "70e4e0e6b2b38e782436e4eb7eb6ff39", + "sha256": "6f92f08dee8a1bd7680e098a91bf5acd08b5cdfe74137f695b60fd79f4478c30" + }, + "downloads": -1, + "filename": "Django-1.10.3.tar.gz", + "has_sig": true, + "md5_digest": "70e4e0e6b2b38e782436e4eb7eb6ff39", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7733727, + "upload_time": "2016-11-01T13:57:16", + "upload_time_iso_8601": "2016-11-01T13:57:16.055061Z", + "url": "https://files.pythonhosted.org/packages/4d/6b/cf3edad0526851d1fd6dd56c9cc94f2be090489c39d9666ca4ad980312e2/Django-1.10.3.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10.4": [ + { + "comment_text": "", + "digests": { + "md5": "28f2a0607bb52dac9c1b168b374de1cd", + "sha256": "a8e1a552205cda15023c39ecf17f7e525e96c5b0142e7879e8bd0c445351f2cc" + }, + "downloads": -1, + "filename": "Django-1.10.4-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "28f2a0607bb52dac9c1b168b374de1cd", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6833471, + "upload_time": "2016-12-01T23:46:26", + "upload_time_iso_8601": "2016-12-01T23:46:26.502027Z", + "url": "https://files.pythonhosted.org/packages/71/37/581a00bbc4571526ce88ef517c0c02ca7575ac2ae8a3671161d2aa14b740/Django-1.10.4-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "65aa2a1bd3b3f08b16a4cd368472d520", + "sha256": "fff7f062e510d812badde7cfc57745b7779edb4d209b2bc5ea8d954c22305c2b" + }, + "downloads": -1, + "filename": "Django-1.10.4.tar.gz", + "has_sig": true, + "md5_digest": "65aa2a1bd3b3f08b16a4cd368472d520", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7735213, + "upload_time": "2016-12-01T23:46:50", + "upload_time_iso_8601": "2016-12-01T23:46:50.215935Z", + "url": "https://files.pythonhosted.org/packages/3b/14/6c1e7508b1342afde8e80f50a55d6b305c0755c702f741db6094924f7499/Django-1.10.4.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10.5": [ + { + "comment_text": "", + "digests": { + "md5": "6892778eea81f14acd58d883f10f3d9f", + "sha256": "4541a60834f28f308ee7b6e96400feca905fb0de473eb9dad6847e98a36d86d4" + }, + "downloads": -1, + "filename": "Django-1.10.5-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "6892778eea81f14acd58d883f10f3d9f", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6833796, + "upload_time": "2017-01-04T19:22:17", + "upload_time_iso_8601": "2017-01-04T19:22:17.889078Z", + "url": "https://files.pythonhosted.org/packages/45/60/faa28a1d17f879f9dbef28f249e4e9a8dd1d29ae78409516b4b8b6c3ebab/Django-1.10.5-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "3fce02f1e6461fec21f1f15ea7489924", + "sha256": "0db89374b691b9c8b057632a6cd64b18d08db2f4d63b4d4af6024267ab965f8b" + }, + "downloads": -1, + "filename": "Django-1.10.5.tar.gz", + "has_sig": true, + "md5_digest": "3fce02f1e6461fec21f1f15ea7489924", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7734715, + "upload_time": "2017-01-04T19:23:00", + "upload_time_iso_8601": "2017-01-04T19:23:00.596664Z", + "url": "https://files.pythonhosted.org/packages/c3/c2/6096bf5d0caa4e3d5b985ac72e3a0c795e37fa7407d6c85460b2a105b467/Django-1.10.5.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10.6": [ + { + "comment_text": "", + "digests": { + "md5": "31a63e4c21a4e12d5ebbafc137523e40", + "sha256": "2cfb83859bfaa10e2bd586340bead27c69fdcaa21fa683a008cc712482c26726" + }, + "downloads": -1, + "filename": "Django-1.10.6-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "31a63e4c21a4e12d5ebbafc137523e40", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6833948, + "upload_time": "2017-03-01T13:37:27", + "upload_time_iso_8601": "2017-03-01T13:37:27.613779Z", + "url": "https://files.pythonhosted.org/packages/b9/bb/723f78e6f6aea78590331eba4e42b8a09c33ce154204a942525a91101d0b/Django-1.10.6-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "aaf0e61104bca75f2dea179d666537cf", + "sha256": "7a6ebe254ab126510da143628d019ca8d6da2de49d7682bf046c03713a3c2c61" + }, + "downloads": -1, + "filename": "Django-1.10.6.tar.gz", + "has_sig": true, + "md5_digest": "aaf0e61104bca75f2dea179d666537cf", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7734864, + "upload_time": "2017-03-01T13:37:40", + "upload_time_iso_8601": "2017-03-01T13:37:40.243134Z", + "url": "https://files.pythonhosted.org/packages/1d/07/fb81c7ed26abbfadd84185be80b5b949219948c4bfd7c30c5c1436d5fd7d/Django-1.10.6.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10.7": [ + { + "comment_text": "", + "digests": { + "md5": "ed23475695d32c176e12b6e2a1fbe1aa", + "sha256": "e68fd450154ad7ee2c88472bb812350490232462adc6e3c6bcb544abe5212134" + }, + "downloads": -1, + "filename": "Django-1.10.7-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "ed23475695d32c176e12b6e2a1fbe1aa", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6834495, + "upload_time": "2017-04-04T14:27:40", + "upload_time_iso_8601": "2017-04-04T14:27:40.297406Z", + "url": "https://files.pythonhosted.org/packages/e5/e7/bdcc0837a2e7ccb1a37be9e5e6e6da642cec5fe9fc1f9ac37dd397c91f74/Django-1.10.7-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "693dfeabad62c561cb205900d32c2a98", + "sha256": "593d779dbc2350a245c4f76d26bdcad58a39895e87304fe6d725bbdf84b5b0b8" + }, + "downloads": -1, + "filename": "Django-1.10.7.tar.gz", + "has_sig": true, + "md5_digest": "693dfeabad62c561cb205900d32c2a98", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7737654, + "upload_time": "2017-04-04T14:27:54", + "upload_time_iso_8601": "2017-04-04T14:27:54.235551Z", + "url": "https://files.pythonhosted.org/packages/15/b4/d4bb7313e02386bd23a60e1eb5670321313fb67289c6f36ec43bce747aff/Django-1.10.7.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10.8": [ + { + "comment_text": "", + "digests": { + "md5": "76640241d7aa59c87a5095fbebc7e2a1", + "sha256": "ffdc7e938391ae3c2ee8ff82e0b4444e4e6bb15c99d00770285233d42aaf33d6" + }, + "downloads": -1, + "filename": "Django-1.10.8-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "76640241d7aa59c87a5095fbebc7e2a1", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6834486, + "upload_time": "2017-09-05T15:31:48", + "upload_time_iso_8601": "2017-09-05T15:31:48.077227Z", + "url": "https://files.pythonhosted.org/packages/bb/9f/2c20639ac635a83123ddffd91ba15001cb0d04e74fbb08f31fb57e490dab/Django-1.10.8-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "d140e63b9f704ab375d052c40f9d8e76", + "sha256": "d4ef83bd326573c00972cb9429beb396d210341a636e4b816fc9b3f505c498bb" + }, + "downloads": -1, + "filename": "Django-1.10.8.tar.gz", + "has_sig": true, + "md5_digest": "d140e63b9f704ab375d052c40f9d8e76", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7739226, + "upload_time": "2017-09-05T15:31:58", + "upload_time_iso_8601": "2017-09-05T15:31:58.221021Z", + "url": "https://files.pythonhosted.org/packages/09/17/13a0cd29f603a4a51b06f7cdc9466fd7bfc48aa20ae2aa80f79d3ad9ba7d/Django-1.10.8.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10a1": [ + { + "comment_text": "", + "digests": { + "md5": "2e29930f401031ecf33e36bfa4739245", + "sha256": "1a8b6ad1f8fabbbd2e1ef8fb54dfe5f9a0b4908c642cccee20d58cfd7c0a3f7e" + }, + "downloads": -1, + "filename": "Django-1.10a1-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "2e29930f401031ecf33e36bfa4739245", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6600566, + "upload_time": "2016-05-20T12:16:44", + "upload_time_iso_8601": "2016-05-20T12:16:44.951411Z", + "url": "https://files.pythonhosted.org/packages/02/50/b210dc6206e9c61a25a05acb76fc2f09101d9031ff0fb4d137f746e2e419/Django-1.10a1-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "25f3210c15f3bf9bb7e4c33adfbcb952", + "sha256": "a53bbf8be7be60a9479295ab2bef375c1e25ae777d00ff0fea5ac2e347aa5c76" + }, + "downloads": -1, + "filename": "Django-1.10a1.tar.gz", + "has_sig": true, + "md5_digest": "25f3210c15f3bf9bb7e4c33adfbcb952", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7543001, + "upload_time": "2016-05-20T12:24:59", + "upload_time_iso_8601": "2016-05-20T12:24:59.952686Z", + "url": "https://files.pythonhosted.org/packages/ae/98/3b27e0a3c53ec0d6727eb19da46eed5705fa9250ed28ec0d1df48778c401/Django-1.10a1.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10b1": [ + { + "comment_text": "", + "digests": { + "md5": "907c0f7f7b6ac716e46312015dfa9e1a", + "sha256": "3dee9e77e12d3edc30aed96e5522632d8ded656845c4e3e804dab8c60a937478" + }, + "downloads": -1, + "filename": "Django-1.10b1-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "907c0f7f7b6ac716e46312015dfa9e1a", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6641407, + "upload_time": "2016-06-22T01:15:05", + "upload_time_iso_8601": "2016-06-22T01:15:05.240779Z", + "url": "https://files.pythonhosted.org/packages/e6/8c/142b08d2dc89aec2b74ad1d37f943ec50c73d4afce12ea6c0c568403ab22/Django-1.10b1-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "c2f10b2d1453adc68e37132c3304317a", + "sha256": "d8ef9aef259d68a452d5ae1a6f60793e8c10c609dbbe9e7412d47ac21e6d4245" + }, + "downloads": -1, + "filename": "Django-1.10b1.tar.gz", + "has_sig": true, + "md5_digest": "c2f10b2d1453adc68e37132c3304317a", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7600640, + "upload_time": "2016-06-22T01:15:17", + "upload_time_iso_8601": "2016-06-22T01:15:17.267637Z", + "url": "https://files.pythonhosted.org/packages/02/be/d10613977c37674ca3b7f6db7105fac9a104b7765ede4a2f1445fabc2873/Django-1.10b1.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "1.10rc1": [ + { + "comment_text": "", + "digests": { + "md5": "c951d6a11587d5e9f2b5e0a5cca96915", + "sha256": "ccb60ae7804bc451e42b39e6863fc916de8c1fd9a681426e4d9fc9a1abf8bd44" + }, + "downloads": -1, + "filename": "Django-1.10rc1-py2.py3-none-any.whl", + "has_sig": true, + "md5_digest": "c951d6a11587d5e9f2b5e0a5cca96915", + "packagetype": "bdist_wheel", + "python_version": "py2.py3", + "requires_python": null, + "size": 6785247, + "upload_time": "2016-07-18T18:04:51", + "upload_time_iso_8601": "2016-07-18T18:04:51.589122Z", + "url": "https://files.pythonhosted.org/packages/79/a2/988b57157526dcbdf78501c68ba6409b7863381a6cc6bc06424e07e134a2/Django-1.10rc1-py2.py3-none-any.whl", + "yanked": false, + "yanked_reason": null + }, + { + "comment_text": "", + "digests": { + "md5": "daf478a2459e54ba28e5ec600d669960", + "sha256": "26d08f62284d838598bc45671af6e6dba880d54fff3c14aa6aa78ba5519aeac0" + }, + "downloads": -1, + "filename": "Django-1.10rc1.tar.gz", + "has_sig": true, + "md5_digest": "daf478a2459e54ba28e5ec600d669960", + "packagetype": "sdist", + "python_version": "source", + "requires_python": null, + "size": 7687388, + "upload_time": "2016-07-18T18:05:05", + "upload_time_iso_8601": "2016-07-18T18:05:05.503584Z", + "url": "https://files.pythonhosted.org/packages/32/0b/a54e4d4922545b0deb6808d4af0bb78010c0ca4d3109608ce6675f4f0ea1/Django-1.10rc1.tar.gz", + "yanked": false, + "yanked_reason": null + } + ], + "vulnerabilities": [] + } +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_github.py b/vulnerabilities/tests/test_github.py index 8dcd6c79a..e82bd1a1f 100644 --- a/vulnerabilities/tests/test_github.py +++ b/vulnerabilities/tests/test_github.py @@ -22,288 +22,315 @@ import json import os -from unittest import TestCase -from unittest.mock import MagicMock -from unittest.mock import call -from unittest.mock import patch +from datetime import datetime +from unittest import mock +import pytest +import pytz from packageurl import PackageURL +from univers.version_constraint import VersionConstraint +from univers.version_range import GemVersionRange +from univers.versions import RubygemsVersion -from vulnerabilities.helpers import AffectedPackage -from vulnerabilities.importer import Advisory +from vulnerabilities.importer import AdvisoryData +from vulnerabilities.importer import AffectedPackage from vulnerabilities.importer import Reference from vulnerabilities.importer import VulnerabilitySeverity from vulnerabilities.importers.github import GitHubAPIImporter +from vulnerabilities.importers.github import GitHubBasicImprover from vulnerabilities.importers.github import GitHubTokenError -from vulnerabilities.importers.github import query -from vulnerabilities.package_managers import ComposerVersionAPI -from vulnerabilities.package_managers import MavenVersionAPI -from vulnerabilities.package_managers import NugetVersionAPI -from vulnerabilities.package_managers import Version +from vulnerabilities.importers.github import process_response +from vulnerabilities.importers.github import resolve_version_range +from vulnerabilities.package_managers import Version as PackageVersion from vulnerabilities.severity_systems import ScoringSystem BASE_DIR = os.path.dirname(os.path.abspath(__file__)) -TEST_DATA = os.path.join(BASE_DIR, "test_data") +TEST_DATA = os.path.join(BASE_DIR, "test_data", "github_api") -class TestGitHubAPIImporter(TestCase): - @classmethod - def setUpClass(cls): - data_source_cfg = { - "endpoint": "https://api.example.com/graphql", - "ecosystems": ["MAVEN"], - } - with patch.dict(os.environ, {"GH_TOKEN": "abc"}): - cls.data_src = GitHubAPIImporter(1, config=data_source_cfg) +@pytest.mark.parametrize("pkg_type", ["maven", "nuget", "gem", "golang", "composer", "pypi"]) +def test_process_response_github_importer(pkg_type, regen=False): + response_file = os.path.join(TEST_DATA, f"{pkg_type}.json") + expected_file = os.path.join(TEST_DATA, f"{pkg_type}-expected.json") + with open(response_file) as f: + response = json.load(f) - def tearDown(self): - setattr(self.data_src, "version_api", None) + result = [data.to_dict() for data in process_response(resp=response, package_type=pkg_type)] - def test_categorize_versions(self): - eg_version_range = ">= 3.3.0, < 3.3.5" - eg_versions = ["3.3.6", "3.3.0", "3.3.4", "3.2.0"] + if regen: + with open(expected_file, "w") as f: + json.dump(result, f, indent=2) + expected = result + else: + with open(expected_file) as f: + expected = json.load(f) - aff_vers, safe_vers = self.data_src.categorize_versions( - "pypi", eg_version_range, eg_versions - ) - exp_safe_vers = ["3.3.6", "3.2.0"] - exp_aff_vers = ["3.3.0", "3.3.4"] - - assert aff_vers == exp_aff_vers - assert safe_vers == exp_safe_vers + assert result == expected - def test_fetch_withinvalidtoken(self): - class MockErrorResponse(MagicMock): - @staticmethod - def json(): - return {"message": "Bad credentials"} - # This test checks whether `fetch` raises an error when there is an Authentication - # failure. - exp_headers = {"Authorization": "token abc"} - first_query = {"query": query % ("MAVEN", "")} - mock = MockErrorResponse() - with patch("vulnerabilities.importers.github.requests.post", new=mock): - self.assertRaises(GitHubTokenError, self.data_src.fetch) - mock.assert_called_with( - self.data_src.config.endpoint, headers=exp_headers, json=first_query +def test_resolve_version_range(): + assert (["1.0.0", "2.0.0"], ["10.0.0"]) == resolve_version_range( + GemVersionRange( + constraints=( + VersionConstraint(comparator="<", version=RubygemsVersion(string="9.0.0")), ) - - def test_fetch_withvalidtoken(self): - class MockCorrectResponse(MagicMock): - has_next_page = False - # This owes an explanation. The intent of having - # has_next_page is to obtain different MockCorrectResponse objects - # the first one should have `has_next_page = True` and other should - # have `has_next_page = False`. This is required to test whether - # GitHubAPIImporter.fetch stops as expected. - - def json(self): - self.has_next_page = not self.has_next_page - return { - "data": { - "securityVulnerabilities": { - "pageInfo": { - "endCursor": "page=2", - "hasNextPage": self.has_next_page, - } - } - } - } - - exp_headers = {"Authorization": "token abc"} - first_query = {"query": query % ("MAVEN", "")} - second_query = {"query": query % ("MAVEN", 'after: "page=2"')} - mock = MockCorrectResponse() - with patch("vulnerabilities.importers.github.requests.post", new=mock): - resp = self.data_src.fetch() - - call_1 = call(self.data_src.config.endpoint, headers=exp_headers, json=first_query) - call_2 = call(self.data_src.config.endpoint, headers=exp_headers, json=second_query) - - assert mock.call_args_list[0] == call_1 - assert mock.call_args_list[1] == call_2 - - def test_set_version_api(self): - - with patch("vulnerabilities.importers.github.GitHubAPIImporter.set_api"): - with patch("vulnerabilities.importers.github.GitHubAPIImporter.collect_packages"): - assert getattr(self.data_src, "version_api", None) is None - - self.data_src.set_version_api("MAVEN") - assert isinstance(self.data_src.version_api, MavenVersionAPI) - - self.data_src.set_version_api("NUGET") - assert isinstance(self.data_src.version_api, NugetVersionAPI) - - self.data_src.set_version_api("COMPOSER") - assert isinstance(self.data_src.version_api, ComposerVersionAPI) - - def test_process_name(self): - - expected_1 = ("org.apache", "kafka") - result_1 = self.data_src.process_name("MAVEN", "org.apache:kafka") - assert result_1 == expected_1 - - expected_2 = (None, "WindowS.nUget.ExIsts") - result_2 = self.data_src.process_name("NUGET", "WindowS.nUget.ExIsts") - assert result_2 == expected_2 - - expected_3 = ("psf", "black") - result_3 = self.data_src.process_name("COMPOSER", "psf/black") - assert result_3 == expected_3 - - expected_4 = None - result_4 = self.data_src.process_name("SAMPLE", "sample?example=True") - assert result_4 == expected_4 - - def test_process_response(self): - - with open(os.path.join(TEST_DATA, "github_api", "response.json")) as f: - resp = json.load(f) - self.data_src.advisories = resp - - expected_advisories = [ - Advisory( - summary="Denial of Service in Tomcat", - references=[ - Reference( - reference_id="GHSA-qcxh-w3j9-58qr", - url="https://github.com/advisories/GHSA-qcxh-w3j9-58qr", - severities=[ - VulnerabilitySeverity( - system=ScoringSystem( - identifier="cvssv3.1_qr", - name="CVSSv3.1 Qualitative Severity Rating", - url="https://www.first.org/cvss/specification-document#Qualitative-Severity-Rating-Scale", - notes="A textual interpretation of severity. Has values like HIGH, MODERATE etc", # nopep8 - ), - value="MODERATE", - ) - ], + ), + [ + "1.0.0", + "2.0.0", + "10.0.0", + ], + ) + + +def test_resolve_version_range_failure(caplog): + assert ([], []) == resolve_version_range( + None, + [ + PackageVersion(value="1.0.0"), + PackageVersion(value="2.0.0"), + PackageVersion(value="10.0.0"), + ], + ) + assert "affected version range is" in caplog.text + + +def test_process_response_with_empty_vulnaribilities(caplog): + list(process_response({"data": {"securityVulnerabilities": {"edges": []}}}, "maven")) + assert "No vulnerabilities found for package_type: 'maven'" in caplog.text + + +def test_process_response_with_empty_vulnaribilities(caplog): + list( + process_response( + {"data": {"securityVulnerabilities": {"edges": [{"node": {}}, None]}}}, "maven" + ) + ) + assert "No node found" in caplog.text + + +def test_github_importer_with_missing_credentials(): + with pytest.raises(GitHubTokenError) as e: + with mock.patch.dict(os.environ, {}, clear=True): + importer = GitHubAPIImporter() + importer.advisory_data() + + +@mock.patch("vulnerabilities.importers.github.get_response") +def test_github_importer_with_missing_credentials(mock_response): + mock_response.return_value = {"message": "Bad credentials"} + with pytest.raises(GitHubTokenError) as e: + with mock.patch.dict(os.environ, {"GH_TOKEN": "BAD"}, clear=True): + importer = GitHubAPIImporter() + importer.advisory_data() + + +def valid_versions(): + return [ + "5.2.4.1", + "6.1.4.3", + "6.0.2", + "5.2.1", + "6.0.3", + "7.0.2", + "6.1.4.6", + "5.2.0.beta2", + "6.0.0.beta3", + "5.2.0.beta1", + "5.2.4.4", + "5.2.0", + "6.1.3", + "6.0.0", + "5.2.3.rc1", + "6.0.3.5", + "5.2.6.2", + "6.1.0.rc1", + "5.2.7", + "6.1.2.1", + "7.0.0.rc3", + "6.0.4.7", + "5.2.1.rc1", + "7.0.2.1", + "6.1.4.4", + "5.2.5", + "5.2.4.5", + "7.0.2.2", + "6.0.3.7", + "6.0.4.2", + "6.0.2.2", + "5.2.2.1", + "6.1.4", + "7.0.0.rc2", + "6.0.0.beta2", + "5.2.1.1", + "6.1.4.5", + "6.0.3.1", + "6.0.4.1", + "6.0.2.1", + "5.2.6.1", + "5.2.6.3", + "6.1.5", + "6.0.3.3", + "6.0.3.2", + "5.2.2.rc1", + "6.0.1", + "7.0.0.alpha1", + "5.2.6", + "6.1.3.2", + "6.0.4.6", + "6.1.0.rc2", + "5.2.4.3", + "7.0.1", + "7.0.2.3", + "6.0.4", + "7.0.0.rc1", + "6.1.2", + "5.2.4.6", + "5.2.3", + "6.1.4.2", + "6.0.3.6", + "6.0.4.4", + "7.0.0", + "6.0.4.3", + "6.0.0.rc2", + "5.2.4.rc1", + "0.1", + "6.1.0", + "6.0.1.rc1", + "5.2.4.2", + "6.0.0.beta1", + "5.2.4", + "6.0.4.5", + "6.1.3.1", + "7.0.0.alpha2", + "6.1.1", + "6.0.0.rc1", + "5.2.0.rc2", + "6.1.4.1", + "6.1.4.7", + "5.2.2", + "6.0.2.rc1", + "5.2.0.rc1", + "6.0.3.4", + "6.0.3.rc1", + "6.0.2.rc2", + ] + + +@mock.patch("vulnerabilities.importers.github.GitHubBasicImprover.get_package_versions") +def test_github_improver(mock_response, regen=False): + advisory_data = AdvisoryData( + aliases=["CVE-2022-21831", "GHSA-w749-p3v6-hccq"], + summary="Possible code injection vulnerability in Rails / Active Storage", + affected_packages=[ + AffectedPackage( + package=PackageURL( + type="gem", + namespace=None, + name="activestorage", + version=None, + qualifiers={}, + subpath=None, + ), + affected_version_range=GemVersionRange( + constraints=( + VersionConstraint(comparator=">=", version=RubygemsVersion(string="5.2.0")), + VersionConstraint( + comparator="<=", version=RubygemsVersion(string="5.2.6.2") + ), + VersionConstraint(comparator=">=", version=RubygemsVersion(string="6.0.1")), + VersionConstraint( + comparator="<=", version=RubygemsVersion(string="6.0.4.3") + ), ) - ], - vulnerability_id="CVE-2019-0199", + ), + fixed_version=None, + ) + ], + references=[ + Reference( + reference_id="", + url="https://nvd.nist.gov/vuln/detail/CVE-2022-21831", + severities=[], ), - Advisory( - summary="Denial of Service in Tomcat", - affected_packages=[ - AffectedPackage( - vulnerable_package=PackageURL( - type="maven", - namespace="org.apache.tomcat.embed", - name="tomcat-embed-core", - version="9.0.2", - qualifiers={}, - subpath=None, - ) - ) - ], - references=[ - Reference( - reference_id="GHSA-qcxh-w3j9-58qr", - url="https://github.com/advisories/GHSA-qcxh-w3j9-58qr", - severities=[ - VulnerabilitySeverity( - system=ScoringSystem( - identifier="cvssv3.1_qr", - name="CVSSv3.1 Qualitative Severity Rating", - url="https://www.first.org/cvss/specification-document#Qualitative-Severity-Rating-Scale", - notes="A textual interpretation of severity. Has values like HIGH, MODERATE etc", # nopep8 - ), - value="HIGH", - ) - ], - ) - ], - vulnerability_id="CVE-2019-0199", + Reference( + reference_id="", + url="https://github.com/rails/rails/commit/0a72f7d670e9aa77a0bb8584cb1411ddabb7546e", + severities=[], ), - Advisory( - summary="Improper Input Validation in Tomcat", - references=[ - Reference( - reference_id="GHSA-c9hw-wf7x-jp9j", - url="https://github.com/advisories/GHSA-c9hw-wf7x-jp9j", - severities=[ - VulnerabilitySeverity( - system=ScoringSystem( - identifier="cvssv3.1_qr", - name="CVSSv3.1 Qualitative Severity Rating", - url="https://www.first.org/cvss/specification-document#Qualitative-Severity-Rating-Scale", - notes="A textual interpretation of severity. Has values like HIGH, MODERATE etc", # nopep8 - ), - value="LOW", - ) - ], - ) - ], - vulnerability_id="CVE-2020-1938", + Reference( + reference_id="", + url="https://groups.google.com/g/rubyonrails-security/c/n-p-W1yxatI", + severities=[], ), - Advisory( - summary="Improper Input Validation in Tomcat", - references=[ - Reference( - reference_id="GHSA-c9hw-wf7x-jp9j", - url="https://github.com/advisories/GHSA-c9hw-wf7x-jp9j", - severities=[ - VulnerabilitySeverity( - system=ScoringSystem( - identifier="cvssv3.1_qr", - name="CVSSv3.1 Qualitative Severity Rating", - url="https://www.first.org/cvss/specification-document#Qualitative-Severity-Rating-Scale", - notes="A textual interpretation of severity. Has values like HIGH, MODERATE etc", # nopep8 - ), - value="MODERATE", - ) - ], - ) - ], - vulnerability_id="CVE-2020-1938", + Reference( + reference_id="", + url="https://rubysec.com/advisories/CVE-2022-21831/", + severities=[], ), - Advisory( - summary="Improper Input Validation in Tomcat", - affected_packages=[ - AffectedPackage( - vulnerable_package=PackageURL( - type="maven", - namespace="org.apache.tomcat.embed", - name="tomcat-embed-core", - version="9.0.2", - ) + Reference( + reference_id="GHSA-w749-p3v6-hccq", + url="https://github.com/advisories/GHSA-w749-p3v6-hccq", + severities=[ + VulnerabilitySeverity( + system=ScoringSystem( + identifier="cvssv3.1_qr", + name="CVSSv3.1 Qualitative Severity Rating", + url="https://www.first.org/cvss/specification-document#Qualitative-Severity-Rating-Scale", + notes="A textual interpretation of severity. Has values like HIGH, MEDIUM etc", + ), + value="HIGH", ) ], - references=[ - Reference( - reference_id="GHSA-c9hw-wf7x-jp9j", - url="https://github.com/advisories/GHSA-c9hw-wf7x-jp9j", - severities=[ - VulnerabilitySeverity( - system=ScoringSystem( - identifier="cvssv3.1_qr", - name="CVSSv3.1 Qualitative Severity Rating", - url="https://www.first.org/cvss/specification-document#Qualitative-Severity-Rating-Scale", - notes="A textual interpretation of severity. Has values like HIGH, MODERATE etc", # nopep8 - ), - value="LOW", - ) - ], - ) - ], - vulnerability_id="CVE-2020-1938", ), - ] - - mock_version_api = MavenVersionAPI( - cache={ - "org.apache.tomcat.embed:tomcat-embed-core": {Version("1.2.0"), Version("9.0.2")} - } - ) - with patch( - "vulnerabilities.importers.github.MavenVersionAPI", return_value=mock_version_api - ): - with patch("vulnerabilities.importers.github.GitHubAPIImporter.set_api"): - found_advisories = self.data_src.process_response() - - found_advisories = list(map(Advisory.normalized, found_advisories)) - expected_advisories = list(map(Advisory.normalized, expected_advisories)) - assert sorted(found_advisories) == sorted(expected_advisories) + ], + date_published=datetime.now(), + ) + mock_response.return_value = list(valid_versions()) + improver = GitHubBasicImprover() + expected_file = os.path.join(TEST_DATA, f"inference-expected.json") + + result = [data.to_dict() for data in improver.get_inferences(advisory_data=advisory_data)] + + if regen: + with open(expected_file, "w") as f: + json.dump(result, f, indent=2) + expected = result + else: + with open(expected_file) as f: + expected = json.load(f) + + assert result == expected + + +@mock.patch("vulnerabilities.package_managers_2.get_response") +def test_get_package_versions(mock_response): + with open(os.path.join(BASE_DIR, "test_data", "package_manager_data", "pypi.json"), "r") as f: + mock_response.return_value = json.load(f) + improver = GitHubBasicImprover() + valid_versions = { + "1.1.3", + "1.1.4", + "1.10", + "1.10.1", + "1.10.2", + "1.10.3", + "1.10.4", + "1.10.5", + "1.10.6", + "1.10.7", + "1.10.8", + "1.10a1", + "1.10b1", + "1.10rc1", + } + assert ( + improver.get_package_versions(package_url=PackageURL(type="pypi", name="django")) + == valid_versions + ) + mock_response.return_value = None + assert not improver.get_package_versions(package_url=PackageURL(type="gem", name="foo")) + assert not improver.get_package_versions(package_url=PackageURL(type="pypi", name="foo")) + assert "django" in improver.version_api_by_purl_type["pypi"].cache + assert "foo" in improver.version_api_by_purl_type["gem"].cache + assert "foo" in improver.version_api_by_purl_type["pypi"].cache diff --git a/vulnerabilities/tests/test_helpers.py b/vulnerabilities/tests/test_helpers.py index 87076b969..79e2cb371 100644 --- a/vulnerabilities/tests/test_helpers.py +++ b/vulnerabilities/tests/test_helpers.py @@ -19,11 +19,51 @@ # for any legal advice. # VulnerableCode is a free software tool from nexB Inc. and others. # Visit https://github.com/nexB/vulnerablecode/ for support and download. +from unittest import result -from unittest import TestCase -from unittest.mock import MagicMock -from unittest.mock import patch +from packageurl import PackageURL +from vulnerabilities.helpers import AffectedPackage as LegacyAffectedPackage +from vulnerabilities.helpers import nearest_patched_package -class TestHelpers(TestCase): - ... + +def test_nearest_patched_package(): + + result = nearest_patched_package( + vulnerable_packages=[ + PackageURL(type="npm", name="foo", version="2.0.0"), + PackageURL(type="npm", name="foo", version="2.0.1"), + PackageURL(type="npm", name="foo", version="1.9.8"), + ], + resolved_packages=[ + PackageURL(type="npm", name="foo", version="2.0.2"), + PackageURL(type="npm", name="foo", version="1.9.9"), + ], + ) + + assert [ + LegacyAffectedPackage( + vulnerable_package=PackageURL( + type="npm", namespace=None, name="foo", version="1.9.8", qualifiers={}, subpath=None + ), + patched_package=PackageURL( + type="npm", namespace=None, name="foo", version="1.9.9", qualifiers={}, subpath=None + ), + ), + LegacyAffectedPackage( + vulnerable_package=PackageURL( + type="npm", namespace=None, name="foo", version="2.0.0", qualifiers={}, subpath=None + ), + patched_package=PackageURL( + type="npm", namespace=None, name="foo", version="2.0.2", qualifiers={}, subpath=None + ), + ), + LegacyAffectedPackage( + vulnerable_package=PackageURL( + type="npm", namespace=None, name="foo", version="2.0.1", qualifiers={}, subpath=None + ), + patched_package=PackageURL( + type="npm", namespace=None, name="foo", version="2.0.2", qualifiers={}, subpath=None + ), + ), + ] == result diff --git a/vulnerabilities/tests/test_package_managers_2.py b/vulnerabilities/tests/test_package_managers_2.py new file mode 100644 index 000000000..27cb7acb6 --- /dev/null +++ b/vulnerabilities/tests/test_package_managers_2.py @@ -0,0 +1,71 @@ +import json +import os +from datetime import datetime + +import pytest +import pytz + +from vulnerabilities.package_managers_2 import GoproxyVersionAPI +from vulnerabilities.package_managers_2 import LegacyVersion +from vulnerabilities.package_managers_2 import NugetVersionAPI + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +TEST_DATA = os.path.join(BASE_DIR, "test_data", "package_manager_data") + + +@pytest.mark.parametrize( + "url_path", ["https://pkg.go.dev/https://github.com/xx/a/b", "https://github.com/xx/a/b"] +) +def test_trim_go_url_path(url_path): + assert GoproxyVersionAPI.trim_go_url_path(url_path) == "github.com/xx/a" + + +def test_trim_go_url_path_failure(caplog): + url_path = "https://github.com" + assert GoproxyVersionAPI.trim_go_url_path(url_path) == None + assert "Not a valid Go URL path" in caplog.text + + +def test_nuget_extract_version(): + with open(os.path.join(TEST_DATA, "nuget-data.json"), "r") as f: + resp = json.load(f) + assert NugetVersionAPI.extract_versions(resp) == { + LegacyVersion( + value="3.0.3", release_date=datetime(2011, 11, 27, 13, 50, 2, 63000, tzinfo=pytz.UTC) + ), + LegacyVersion( + value="3.0.5", release_date=datetime(2011, 12, 12, 12, 0, 25, 947000, tzinfo=pytz.UTC) + ), + LegacyVersion( + value="2.1.0", release_date=datetime(2011, 1, 22, 13, 34, 8, 550000, tzinfo=pytz.UTC) + ), + LegacyVersion( + value="3.0.0", release_date=datetime(2011, 11, 24, 0, 26, 2, 527000, tzinfo=pytz.UTC) + ), + LegacyVersion( + value="3.0.4", release_date=datetime(2011, 12, 12, 10, 18, 33, 380000, tzinfo=pytz.UTC) + ), + LegacyVersion( + value="3.0.6", release_date=datetime(2012, 1, 2, 21, 10, 43, 403000, tzinfo=pytz.UTC) + ), + LegacyVersion( + value="3.4.0", release_date=datetime(2013, 10, 20, 13, 32, 30, 837000, tzinfo=pytz.UTC) + ), + LegacyVersion( + value="3.4.1", release_date=datetime(2014, 1, 17, 9, 17, 43, 680000, tzinfo=pytz.UTC) + ), + LegacyVersion( + value="3.5.0-beta3", + release_date=datetime(2015, 1, 6, 17, 39, 25, 147000, tzinfo=pytz.UTC), + ), + LegacyVersion( + value="3.5.0-beta2", + release_date=datetime(2015, 1, 1, 14, 9, 28, 710000, tzinfo=pytz.UTC), + ), + LegacyVersion( + value="3.5.0", release_date=datetime(2015, 1, 14, 2, 1, 58, 853000, tzinfo=pytz.UTC) + ), + LegacyVersion( + value="3.5.1", release_date=datetime(2015, 1, 23, 1, 5, 44, 447000, tzinfo=pytz.UTC) + ), + }