diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index 0b6e87bea..09c64f34c 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -106,7 +106,7 @@ def to_advisory(self, data): fixed_packages.extend( [ PackageURL(type="apache", name="httpd", version=version) - for version in self.version_api.get("apache/httpd") + for version in self.version_api.get("apache/httpd")["valid"] if MavenVersion(version) in version_range ] ) @@ -115,7 +115,7 @@ def to_advisory(self, data): affected_packages.extend( [ PackageURL(type="apache", name="httpd", version=version) - for version in self.version_api.get("apache/httpd") + for version in self.version_api.get("apache/httpd")["valid"] if MavenVersion(version) in version_range ] ) diff --git a/vulnerabilities/importers/apache_kafka.py b/vulnerabilities/importers/apache_kafka.py index 105f327f0..d0e8f7d7e 100644 --- a/vulnerabilities/importers/apache_kafka.py +++ b/vulnerabilities/importers/apache_kafka.py @@ -72,7 +72,7 @@ def to_advisory(self, advisory_page): fixed_packages = [ PackageURL(type="apache", name="kafka", version=version) - for version in self.version_api.get("apache/kafka") + for version in self.version_api.get("apache/kafka")["valid"] if any( [ MavenVersion(version) in version_range @@ -83,7 +83,7 @@ def to_advisory(self, advisory_page): affected_packages = [ PackageURL(type="apache", name="kafka", version=version) - for version in self.version_api.get("apache/kafka") + for version in self.version_api.get("apache/kafka")["valid"] if any( [ MavenVersion(version) in version_range diff --git a/vulnerabilities/importers/apache_tomcat.py b/vulnerabilities/importers/apache_tomcat.py index 42a728840..3326a3bf9 100644 --- a/vulnerabilities/importers/apache_tomcat.py +++ b/vulnerabilities/importers/apache_tomcat.py @@ -62,7 +62,9 @@ def updated_advisories(self): return self.batch_advisories(advisories) def fetch_pages(self): - tomcat_major_versions = {i[0] for i in self.version_api.get("org.apache.tomcat:tomcat")} + tomcat_major_versions = { + i[0] for i in self.version_api.get("org.apache.tomcat:tomcat")["valid"] + } for version in tomcat_major_versions: page_url = self.base_url.format(version) if create_etag(self, page_url, "ETag"): @@ -102,7 +104,7 @@ def to_advisories(self, apache_tomcat_advisory_html): PackageURL( type="maven", namespace="apache", name="tomcat", version=version ) - for version in self.version_api.get("org.apache.tomcat:tomcat") + for version in self.version_api.get("org.apache.tomcat:tomcat")["valid"] if MavenVersion(version) in version_range ] ) diff --git a/vulnerabilities/importers/nginx.py b/vulnerabilities/importers/nginx.py index fb8d82404..13a572f3e 100644 --- a/vulnerabilities/importers/nginx.py +++ b/vulnerabilities/importers/nginx.py @@ -171,7 +171,9 @@ def extract_vuln_pkgs(self, vuln_info): ) ) - valid_versions = find_valid_versions(self.version_api.get("nginx/nginx"), version_ranges) + valid_versions = find_valid_versions( + self.version_api.get("nginx/nginx")["valid"], version_ranges + ) qualifiers = {} if windows_only: qualifiers["os"] = "windows" diff --git a/vulnerabilities/importers/npm.py b/vulnerabilities/importers/npm.py index eb28869d8..e66f9e5d4 100644 --- a/vulnerabilities/importers/npm.py +++ b/vulnerabilities/importers/npm.py @@ -88,7 +88,7 @@ def process_file(self, file) -> List[Advisory]: publish_date = parse(record["updated_at"]) publish_date.replace(tzinfo=pytz.UTC) - all_versions = self.versions.get(package_name, until=publish_date) + all_versions = self.versions.get(package_name, until=publish_date)["valid"] aff_range = record.get("vulnerable_versions") if not aff_range: aff_range = "" diff --git a/vulnerabilities/importers/project_kb_msr2019.py b/vulnerabilities/importers/project_kb_msr2019.py index 8b97eccf1..897a0c4ff 100644 --- a/vulnerabilities/importers/project_kb_msr2019.py +++ b/vulnerabilities/importers/project_kb_msr2019.py @@ -22,12 +22,10 @@ import csv import dataclasses -import re import urllib.request # Reading CSV file from a url using `requests` is bit too complicated. # Use `urllib.request` for that purpose. -from packageurl import PackageURL from vulnerabilities.data_source import Advisory diff --git a/vulnerabilities/importers/ruby.py b/vulnerabilities/importers/ruby.py index a801a5f81..0ae3ba7be 100644 --- a/vulnerabilities/importers/ruby.py +++ b/vulnerabilities/importers/ruby.py @@ -23,6 +23,8 @@ import asyncio from typing import Set from typing import List +from dateutil.parser import parse +from pytz import UTC from packageurl import PackageURL from univers.version_specifier import VersionSpecifier @@ -90,6 +92,7 @@ def process_file(self, path) -> List[Advisory]: else: return + publish_time = parse(record["date"]).replace(tzinfo=UTC) safe_version_ranges = record.get("patched_versions", []) # this case happens when the advisory contain only 'patched_versions' field # and it has value None(i.e it is empty :( ). @@ -100,7 +103,10 @@ def process_file(self, path) -> List[Advisory]: if not getattr(self, "pkg_manager_api", None): self.pkg_manager_api = RubyVersionAPI() - all_vers = self.pkg_manager_api.get(package_name) + all_vers = self.pkg_manager_api.get(package_name, until=publish_time)["valid"] + print( + f"Ignored {len(self.pkg_manager_api.get(package_name,until=publish_time)['new'])} versions" + ) safe_versions, affected_versions = self.categorize_versions(all_vers, safe_version_ranges) impacted_purls = [ diff --git a/vulnerabilities/importers/safety_db.py b/vulnerabilities/importers/safety_db.py index 97ea5ae64..38956f708 100755 --- a/vulnerabilities/importers/safety_db.py +++ b/vulnerabilities/importers/safety_db.py @@ -111,7 +111,7 @@ def updated_advisories(self) -> Set[Advisory]: logger.error(e) continue - all_package_versions = self.versions.get(package_name) + all_package_versions = self.versions.get(package_name)["valid"] if not len(all_package_versions): # PyPi does not have data about this package, we skip these continue diff --git a/vulnerabilities/importers/suse_backports.py b/vulnerabilities/importers/suse_backports.py index 6ad43ee08..c80d25969 100644 --- a/vulnerabilities/importers/suse_backports.py +++ b/vulnerabilities/importers/suse_backports.py @@ -1,95 +1,95 @@ -# # Copyright (c) 2017 nexB Inc. and others. All rights reserved. -# # http://nexb.com and https://github.com/nexB/vulnerablecode/ -# # The VulnerableCode software is licensed under the Apache License version 2.0. -# # Data generated with VulnerableCode require an acknowledgment. -# # -# # You may not use this software except in compliance with the License. -# # You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0 -# # Unless required by applicable law or agreed to in writing, software distributed -# # under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -# # CONDITIONS OF ANY KIND, either express or implied. See the License for the -# # specific language governing permissions and limitations under the License. -# # -# # When you publish or redistribute any data created with VulnerableCode or any VulnerableCode -# # derivative work, you must accompany this data with the following acknowledgment: -# # -# # Generated with VulnerableCode and provided on an "AS IS" BASIS, WITHOUT WARRANTIES -# # OR CONDITIONS OF ANY KIND, either express or implied. No content created from -# # VulnerableCode should be considered or used as legal advice. Consult an Attorney -# # for any legal advice. -# # VulnerableCode is a free software code scanning tool from nexB Inc. and others. -# # Visit https://github.com/nexB/vulnerablecode/ for support and download. -# import dataclasses +# Copyright (c) 2017 nexB Inc. and others. All rights reserved. +# http://nexb.com and https://github.com/nexB/vulnerablecode/ +# The VulnerableCode software is licensed under the Apache License version 2.0. +# Data generated with VulnerableCode require an acknowledgment. +# +# You may not use this software except in compliance with the License. +# You may obtain a copy of the License at: http://apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software distributed +# under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +# CONDITIONS OF ANY KIND, either express or implied. See the License for the +# specific language governing permissions and limitations under the License. +# +# When you publish or redistribute any data created with VulnerableCode or any VulnerableCode +# derivative work, you must accompany this data with the following acknowledgment: +# +# Generated with VulnerableCode and provided on an "AS IS" BASIS, WITHOUT WARRANTIES +# OR CONDITIONS OF ANY KIND, either express or implied. No content created from +# VulnerableCode should be considered or used as legal advice. Consult an Attorney +# for any legal advice. +# VulnerableCode is a free software code scanning tool from nexB Inc. and others. +# Visit https://github.com/nexB/vulnerablecode/ for support and download. +import dataclasses -# import requests -# import saneyaml -# from bs4 import BeautifulSoup -# from packageurl import PackageURL +import requests +import saneyaml +from bs4 import BeautifulSoup +from packageurl import PackageURL -# from vulnerabilities.data_source import Advisory -# from vulnerabilities.data_source import DataSource -# from vulnerabilities.data_source import DataSourceConfiguration -# from vulnerabilities.helpers import create_etag +from vulnerabilities.data_source import Advisory +from vulnerabilities.data_source import DataSource +from vulnerabilities.data_source import DataSourceConfiguration +from vulnerabilities.helpers import create_etag -# @dataclasses.dataclass -# class SUSEBackportsConfiguration(DataSourceConfiguration): -# url: str -# etags: dict +@dataclasses.dataclass +class SUSEBackportsConfiguration(DataSourceConfiguration): + url: str + etags: dict -# class SUSEBackportsDataSource(DataSource): +class SUSEBackportsDataSource(DataSource): -# CONFIG_CLASS = SUSEBackportsConfiguration + CONFIG_CLASS = SUSEBackportsConfiguration -# @staticmethod -# def get_all_urls_of_backports(url): -# r = requests.get(url) -# soup = BeautifulSoup(r.content, "lxml") -# for a_tag in soup.find_all("a", href=True): -# if a_tag["href"].endswith(".yaml") and a_tag["href"].startswith("backports"): -# yield url + a_tag["href"] + @staticmethod + def get_all_urls_of_backports(url): + r = requests.get(url) + soup = BeautifulSoup(r.content, "lxml") + for a_tag in soup.find_all("a", href=True): + if a_tag["href"].endswith(".yaml") and a_tag["href"].startswith("backports"): + yield url + a_tag["href"] -# def updated_advisories(self): -# advisories = [] -# all_urls = self.get_all_urls_of_backports(self.config.url) -# for url in all_urls: -# if not create_etag(data_src=self, url=url, etag_key="ETag"): -# continue -# advisories.extend(self.process_file(self._fetch_yaml(url))) -# return self.batch_advisories(advisories) + def updated_advisories(self): + advisories = [] + all_urls = self.get_all_urls_of_backports(self.config.url) + for url in all_urls: + if not create_etag(data_src=self, url=url, etag_key="ETag"): + continue + advisories.extend(self.process_file(self._fetch_yaml(url))) + return self.batch_advisories(advisories) -# def _fetch_yaml(self, url): + def _fetch_yaml(self, url): -# try: -# resp = requests.get(url) -# resp.raise_for_status() -# return saneyaml.load(resp.content) + try: + resp = requests.get(url) + resp.raise_for_status() + return saneyaml.load(resp.content) -# except requests.HTTPError: -# return {} + except requests.HTTPError: + return {} -# @staticmethod -# def process_file(yaml_file): -# advisories = [] -# try: -# for pkg in yaml_file[0]["packages"]: -# for version in yaml_file[0]["packages"][pkg]["fixed"]: -# for vuln in yaml_file[0]["packages"][pkg]["fixed"][version]: -# # yaml_file specific data can be added -# purl = [ -# PackageURL(name=pkg, type="rpm", version=version, namespace="opensuse") -# ] -# advisories.append( -# Advisory( -# vulnerability_id=vuln, -# resolved_package_urls=purl, -# summary="", -# impacted_package_urls=[], -# ) -# ) -# except TypeError: -# # could've used pass -# return advisories + @staticmethod + def process_file(yaml_file): + advisories = [] + try: + for pkg in yaml_file[0]["packages"]: + for version in yaml_file[0]["packages"][pkg]["fixed"]: + for vuln in yaml_file[0]["packages"][pkg]["fixed"][version]: + # yaml_file specific data can be added + purl = [ + PackageURL(name=pkg, type="rpm", version=version, namespace="opensuse") + ] + advisories.append( + Advisory( + vulnerability_id=vuln, + resolved_package_urls=purl, + summary="", + impacted_package_urls=[], + ) + ) + except TypeError: + # could've used pass + return advisories -# return advisories + return advisories diff --git a/vulnerabilities/importers/ubuntu.py b/vulnerabilities/importers/ubuntu.py index dadc4e15d..4124317c0 100644 --- a/vulnerabilities/importers/ubuntu.py +++ b/vulnerabilities/importers/ubuntu.py @@ -25,19 +25,11 @@ import bz2 import dataclasses import logging -from typing import Iterable -from typing import List -from typing import Mapping -from typing import Set import xml.etree.ElementTree as ET - -from aiohttp import ClientSession -from aiohttp.client_exceptions import ClientResponseError import requests from vulnerabilities.data_source import OvalDataSource, DataSourceConfiguration from vulnerabilities.package_managers import LaunchpadVersionAPI -from vulnerabilities.helpers import create_etag logger = logging.getLogger(__name__) diff --git a/vulnerabilities/package_managers.py b/vulnerabilities/package_managers.py index 7ce7d7c34..5b9735435 100644 --- a/vulnerabilities/package_managers.py +++ b/vulnerabilities/package_managers.py @@ -21,20 +21,24 @@ # Visit https://github.com/nexB/vulnerablecode/ for support and download. import asyncio -from collections import namedtuple +import dataclasses import pytz from bs4 import BeautifulSoup from dateutil import parser from json import JSONDecodeError from typing import Mapping from typing import Set +from datetime import datetime from aiohttp import ClientSession from aiohttp.client_exceptions import ClientResponseError from aiohttp.client_exceptions import ServerDisconnectedError -Version = namedtuple("Version", field_names=["value", "release_date"]) +@dataclasses.dataclass(frozen=True) +class Version: + value: str + release_date: datetime = None class VersionAPI: @@ -303,8 +307,8 @@ def extract_versions(soup: BeautifulSoup) -> Set[Version]: pre_tag = soup.find("pre") prev_tag = None versions = set() - for atag in pre_tag: - if atag.name == "a" and atag["href"] != "../": + for i, atag in enumerate(pre_tag): + if atag.name == "a" and i != 0: prev_tag = atag elif prev_tag: text_groups = atag.split() diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index 384696646..47dcbef7e 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -31,6 +31,7 @@ from vulnerabilities.data_source import Advisory from vulnerabilities.data_source import VulnerabilitySeverity from vulnerabilities.package_managers import GitHubTagsAPI +from vulnerabilities.package_managers import Version from vulnerabilities.severity_systems import scoring_systems from vulnerabilities.importers.apache_httpd import ApacheHTTPDDataSource from vulnerabilities.helpers import AffectedPackage @@ -44,7 +45,7 @@ class TestApacheHTTPDDataSource(TestCase): def setUpClass(cls): data_source_cfg = {"etags": {}} cls.data_src = ApacheHTTPDDataSource(1, config=data_source_cfg) - known_versions = ["1.3.2", "1.3.1", "1.3.0"] + known_versions = [Version("1.3.2"), Version("1.3.1"), Version("1.3.0")] cls.data_src.version_api = GitHubTagsAPI(cache={"apache/httpd": known_versions}) with open(TEST_DATA) as f: cls.data = json.load(f) diff --git a/vulnerabilities/tests/test_apache_kafka.py b/vulnerabilities/tests/test_apache_kafka.py index a2435b425..46869ed6c 100644 --- a/vulnerabilities/tests/test_apache_kafka.py +++ b/vulnerabilities/tests/test_apache_kafka.py @@ -29,6 +29,7 @@ from vulnerabilities.data_source import Advisory from vulnerabilities.data_source import Reference from vulnerabilities.package_managers import GitHubTagsAPI +from vulnerabilities.package_managers import Version from vulnerabilities.importers.apache_kafka import ApacheKafkaDataSource from vulnerabilities.importers.apache_kafka import to_version_ranges from vulnerabilities.helpers import AffectedPackage @@ -63,7 +64,9 @@ def test_to_version_ranges(self): def test_to_advisory(self): data_source = ApacheKafkaDataSource(batch_size=1) - data_source.version_api = GitHubTagsAPI(cache={"apache/kafka": ["2.1.2", "0.10.2.2"]}) + data_source.version_api = GitHubTagsAPI( + cache={"apache/kafka": [Version("2.1.2"), Version("0.10.2.2")]} + ) expected_advisories = [ Advisory( summary="In Apache Kafka versions between 0.11.0.0 and 2.1.0, it is possible to manually\n craft a Produce request which bypasses transaction/idempotent ACL validation.\n Only authenticated clients with Write permission on the respective topics are\n able to exploit this vulnerability. Users should upgrade to 2.1.1 or later\n where this vulnerability has been fixed.", diff --git a/vulnerabilities/tests/test_apache_tomcat.py b/vulnerabilities/tests/test_apache_tomcat.py index 290ca3652..d7419dedc 100644 --- a/vulnerabilities/tests/test_apache_tomcat.py +++ b/vulnerabilities/tests/test_apache_tomcat.py @@ -21,7 +21,6 @@ # Visit https://github.com/nexB/vulnerablecode/ for support and download. import os -from unittest.mock import MagicMock from unittest.mock import patch from unittest import TestCase @@ -31,6 +30,8 @@ from vulnerabilities.data_source import Reference from vulnerabilities.importers.apache_tomcat import ApacheTomcatDataSource from vulnerabilities.helpers import AffectedPackage +from vulnerabilities.package_managers import Version +from vulnerabilities.package_managers import MavenVersionAPI BASE_DIR = os.path.dirname(os.path.abspath(__file__)) TEST_DATA = os.path.join(BASE_DIR, "test_data", "apache_tomcat", "security-9.html") @@ -40,11 +41,19 @@ class TestApacheTomcatDataSource(TestCase): @classmethod def setUpClass(cls): data_source_cfg = {"etags": {}} - mock_api = {"org.apache.tomcat:tomcat": ["9.0.0.M1", "9.0.0.M2", "8.0.0.M1", "6.0.0M2"]} + mock_api = MavenVersionAPI( + cache={ + "org.apache.tomcat:tomcat": [ + Version("9.0.0.M1"), + Version("9.0.0.M2"), + Version("8.0.0.M1"), + Version("6.0.0M2"), + ] + } + ) with patch("vulnerabilities.importers.apache_tomcat.MavenVersionAPI"): with patch("vulnerabilities.importers.apache_tomcat.asyncio"): cls.data_src = ApacheTomcatDataSource(1, config=data_source_cfg) - cls.data_src.version_api = mock_api def test_to_advisories(self): diff --git a/vulnerabilities/tests/test_data/github_api/response.json b/vulnerabilities/tests/test_data/github_api/response.json index a1fb7e9ea..d890394fe 100644 --- a/vulnerabilities/tests/test_data/github_api/response.json +++ b/vulnerabilities/tests/test_data/github_api/response.json @@ -21,7 +21,8 @@ "url":"https://github.com/advisories/GHSA-qcxh-w3j9-58qr" } ], - "severity": "MODERATE" + "severity": "MODERATE", + "publishedAt": "2021-05-24T18:12:20Z" }, "package": { "name": "org.apache.tomcat.embed:tomcat-embed-core" @@ -48,7 +49,8 @@ "url":"https://github.com/advisories/GHSA-qcxh-w3j9-58qr" } ], - "severity": "HIGH" + "severity": "HIGH", + "publishedAt": "2021-05-24T18:12:20Z" }, "package": { "name": "org.apache.tomcat.embed:tomcat-embed-core" @@ -75,7 +77,8 @@ "url":"https://github.com/advisories/GHSA-c9hw-wf7x-jp9j" } ], - "severity": "LOW" + "severity": "LOW", + "publishedAt": "2021-05-24T18:12:20Z" }, "package": { "name": "org.apache.tomcat.embed:tomcat-embed-core" @@ -102,7 +105,8 @@ "url":"https://github.com/advisories/GHSA-c9hw-wf7x-jp9j" } ], - "severity": "MODERATE" + "severity": "MODERATE", + "publishedAt": "2021-05-24T18:12:20Z" }, "package": { "name": "org.apache.tomcat.embed:tomcat-embed-core" @@ -129,7 +133,8 @@ "url":"https://github.com/advisories/GHSA-c9hw-wf7x-jp9j" } ], - "severity": "LOW" + "severity": "LOW", + "publishedAt": "2021-05-24T18:12:20Z" }, "package": { "name": "org.apache.tomcat.embed:tomcat-embed-core" diff --git a/vulnerabilities/tests/test_data/maven_api/easygcm.html b/vulnerabilities/tests/test_data/maven_api/easygcm.html new file mode 100644 index 000000000..280faf6a2 --- /dev/null +++ b/vulnerabilities/tests/test_data/maven_api/easygcm.html @@ -0,0 +1,31 @@ + + + + Central Repository: eu/inloop/easygcm + + + + + +
+

eu/inloop/easygcm

+
+
+
+
../
+1.2.2/                                            2014-12-22 10:29         -      
+1.2.3/                                            2014-12-22 10:53         -      
+1.3.0/                                            2015-03-12 15:20         -      
+maven-metadata.xml                                2015-03-12 15:22       385      
+maven-metadata.xml.md5                            2015-03-12 15:22        32      
+maven-metadata.xml.sha1                           2015-03-12 15:22        40      
+		
+
+
+ + + \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/npm.zip b/vulnerabilities/tests/test_data/npm.zip index 37d86e7f1..fdb957a32 100644 Binary files a/vulnerabilities/tests/test_data/npm.zip and b/vulnerabilities/tests/test_data/npm.zip differ diff --git a/vulnerabilities/tests/test_elixir_security.py b/vulnerabilities/tests/test_elixir_security.py index 774a2209f..68022b17c 100644 --- a/vulnerabilities/tests/test_elixir_security.py +++ b/vulnerabilities/tests/test_elixir_security.py @@ -30,6 +30,7 @@ from vulnerabilities.data_source import Reference from vulnerabilities.importers.elixir_security import ElixirSecurityDataSource from vulnerabilities.package_managers import HexVersionAPI +from vulnerabilities.package_managers import Version from vulnerabilities.helpers import AffectedPackage BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -45,17 +46,17 @@ def setUpClass(cls): cls.data_src.pkg_manager_api = HexVersionAPI( { "coherence": [ - "0.5.2", - "0.5.1", - "0.5.0", - "0.4.0", - "0.3.1", - "0.3.0", - "0.2.0", - "0.1.3", - "0.1.2", - "0.1.1", - "0.1.0", + Version("0.5.2"), + Version("0.5.1"), + Version("0.5.0"), + Version("0.4.0"), + Version("0.3.1"), + Version("0.3.0"), + Version("0.2.0"), + Version("0.1.3"), + Version("0.1.2"), + Version("0.1.1"), + Version("0.1.0"), ] } ) diff --git a/vulnerabilities/tests/test_github.py b/vulnerabilities/tests/test_github.py index 3306b75a5..c186711aa 100644 --- a/vulnerabilities/tests/test_github.py +++ b/vulnerabilities/tests/test_github.py @@ -26,17 +26,14 @@ from unittest.mock import patch from unittest.mock import MagicMock from unittest.mock import call -import xml.etree.ElementTree as ET -from collections import OrderedDict -from requests.models import Response from packageurl import PackageURL from vulnerabilities.data_source import Advisory from vulnerabilities.data_source import Reference from vulnerabilities.data_source import VulnerabilitySeverity from vulnerabilities.importers.github import GitHubAPIDataSource -from vulnerabilities.package_managers import MavenVersionAPI +from vulnerabilities.package_managers import MavenVersionAPI, Version from vulnerabilities.package_managers import NugetVersionAPI from vulnerabilities.package_managers import ComposerVersionAPI from vulnerabilities.severity_systems import ScoringSystem @@ -295,12 +292,14 @@ def test_process_response(self): ), ] - mock_version_api = MagicMock() - mock_version_api.package_type = "maven" - mock_version_api.get = lambda x: {"1.2.0", "9.0.2"} + 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 - ): # nopep8 + ): with patch("vulnerabilities.importers.github.GitHubAPIDataSource.set_api"): found_advisories = self.data_src.process_response() diff --git a/vulnerabilities/tests/test_istio.py b/vulnerabilities/tests/test_istio.py index f17dc64ed..986830edc 100644 --- a/vulnerabilities/tests/test_istio.py +++ b/vulnerabilities/tests/test_istio.py @@ -29,6 +29,7 @@ from vulnerabilities.data_source import Advisory, Reference from vulnerabilities.importers.istio import IstioDataSource from vulnerabilities.package_managers import GitHubTagsAPI +from vulnerabilities.package_managers import Version from vulnerabilities.helpers import AffectedPackage BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -44,16 +45,16 @@ def setUpClass(cls): cls.data_src.version_api = GitHubTagsAPI( { "istio/istio": [ - "1.0.0", - "1.1.0", - "1.1.1", - "1.1.17", - "1.2.1", - "1.2.7", - "1.3.0", - "1.3.1", - "1.3.2", - "1.9.1", + Version(value="1.0.0"), + Version(value="1.1.0"), + Version(value="1.1.1"), + Version(value="1.1.17"), + Version(value="1.2.1"), + Version(value="1.2.7"), + Version(value="1.3.0"), + Version(value="1.3.1"), + Version(value="1.3.2"), + Version(value="1.9.1"), ] } ) diff --git a/vulnerabilities/tests/test_nginx.py b/vulnerabilities/tests/test_nginx.py index 38551a46b..a62e009f7 100644 --- a/vulnerabilities/tests/test_nginx.py +++ b/vulnerabilities/tests/test_nginx.py @@ -27,9 +27,9 @@ from packageurl import PackageURL from vulnerabilities.data_source import Advisory -from vulnerabilities.data_source import Reference from vulnerabilities.importers.nginx import NginxDataSource from vulnerabilities.package_managers import GitHubTagsAPI +from vulnerabilities.package_managers import Version from vulnerabilities.helpers import AffectedPackage @@ -45,11 +45,17 @@ def setUpClass(cls): data_source_cfg = {"etags": {}} cls.data_src = NginxDataSource(1, config=data_source_cfg) cls.data_src.version_api = GitHubTagsAPI( - cache={"nginx/nginx": {"1.2.3", "1.7.0", "1.3.9", "0.7.52"}} + cache={ + "nginx/nginx": { + Version("1.2.3"), + Version("1.7.0"), + Version("1.3.9"), + Version("0.7.52"), + } + } ) def test_to_advisories(self): - # expected_advisories = [Advisory(summary='An error log data are not sanitized', vulnerability_id='CVE-2009-4487', affected_packages=[], references=[]), Advisory(summary='Directory traversal vulnerability', vulnerability_id='CVE-2009-3898', affected_packages=[AffectedPackage(vulnerable_package=PackageURL(type='generic', namespace=None, name='nginx', version='0.7.52', qualifiers={}, subpath=None), patched_package=None)], references=[]), Advisory(summary='Stack-based buffer overflow with specially crafted request', vulnerability_id='CVE-2013-2028', affected_packages=[AffectedPackage(vulnerable_package=PackageURL(type='generic', namespace=None, name='nginx', version='1.3.9', qualifiers={}, subpath=None), patched_package=PackageURL(type='generic', namespace=None, name='nginx', version='1.7.0', qualifiers={}, subpath=None))], references=[]), Advisory(summary='The renegotiation vulnerability in SSL protocol', vulnerability_id='CVE-2009-3555', affected_packages=[AffectedPackage(vulnerable_package=PackageURL(type='generic', namespace=None, name='nginx', version='0.7.52', qualifiers={}, subpath=None), patched_package=None)], references=[]), Advisory(summary='Vulnerabilities with Windows directory aliases', vulnerability_id='CVE-2011-4963', affected_packages=[AffectedPackage(vulnerable_package=PackageURL(type='generic', namespace=None, name='nginx', version='0.7.52', qualifiers={'os': 'windows'}, subpath=None), patched_package=PackageURL(type='generic', namespace=None, name='nginx', version='1.2.3', qualifiers={}, subpath=None)), AffectedPackage(vulnerable_package=PackageURL(type='generic', namespace=None, name='nginx', version='1.2.3', qualifiers={'os': 'windows'}, subpath=None), patched_package=PackageURL(type='generic', namespace=None, name='nginx', version='1.3.9', qualifiers={}, subpath=None))], references=[]), Advisory(summary='Vulnerabilities with invalid UTF-8 sequence on Windows', vulnerability_id='CVE-2010-2266', affected_packages=[AffectedPackage(vulnerable_package=PackageURL(type='generic', namespace=None, name='nginx', version='0.7.52', qualifiers={'os': 'windows'}, subpath=None), patched_package=None)], references=[])] expected_advisories = [ Advisory( summary="An error log data are not sanitized", diff --git a/vulnerabilities/tests/test_npm.py b/vulnerabilities/tests/test_npm.py index 953cb1d96..6deab261d 100644 --- a/vulnerabilities/tests/test_npm.py +++ b/vulnerabilities/tests/test_npm.py @@ -20,7 +20,6 @@ # for any legal advice. # VulnerableCode is a free software code scanning tool from nexB Inc. and others. # Visit https://github.com/nexB/vulnerablecode/ for support and download. -import json import os import shutil import tempfile @@ -32,6 +31,7 @@ from vulnerabilities import models from vulnerabilities.import_runner import ImportRunner from vulnerabilities.package_managers import NpmVersionAPI +from vulnerabilities.package_managers import Version from vulnerabilities.importers.npm import categorize_versions BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -40,9 +40,15 @@ MOCK_VERSION_API = NpmVersionAPI( cache={ - "jquery": {"3.4.0", "3.8.0"}, - "kerberos": {"0.5.8", "1.2.0"}, - "@hapi/subtext": {"3.7.0", "4.1.1", "6.1.3", "7.0.0", "7.0.5"}, + "jquery": {Version("3.4.0"), Version("3.8.0")}, + "kerberos": {Version("0.5.8"), Version("1.2.0")}, + "@hapi/subtext": { + Version("3.7.0"), + Version("4.1.1"), + Version("6.1.3"), + Version("7.0.0"), + Version("7.0.5"), + }, } ) @@ -67,7 +73,7 @@ def setUpClass(cls) -> None: data_source="NpmDataSource", data_source_cfg={ "repository_url": "https://example.git", - "working_directory": os.path.join(cls.tempdir, "npm_test"), + "working_directory": os.path.join(cls.tempdir, "npm/npm_test"), "create_working_directory": False, "remove_working_directory": False, }, diff --git a/vulnerabilities/tests/test_package_managers.py b/vulnerabilities/tests/test_package_managers.py index a74a46907..5a33f3526 100644 --- a/vulnerabilities/tests/test_package_managers.py +++ b/vulnerabilities/tests/test_package_managers.py @@ -21,17 +21,20 @@ # Visit https://github.com/nexB/vulnerablecode/ for support and download. import asyncio +from datetime import datetime +from bs4 import BeautifulSoup +from dateutil.tz import tzlocal +from pytz import UTC import os import json from unittest import TestCase -from unittest.mock import patch -from unittest.mock import MagicMock, AsyncMock +from unittest.mock import AsyncMock import xml.etree.ElementTree as ET -from aiohttp import test_utils from vulnerabilities.package_managers import ComposerVersionAPI from vulnerabilities.package_managers import MavenVersionAPI from vulnerabilities.package_managers import NugetVersionAPI +from vulnerabilities.package_managers import Version BASE_DIR = os.path.dirname(os.path.abspath(__file__)) TEST_DATA = os.path.join(BASE_DIR, "test_data") @@ -62,72 +65,270 @@ def setUpClass(cls): cls.response = json.load(f) cls.expected_versions = { - "9.5.3", - "8.7.30", - "9.3.1", - "9.5.1", - "9.5.11", - "9.5.6", - "8.7.18", - "8.7.15", - "9.4.0", - "9.5.7", - "8.7.21", - "9.5.12", - "9.5.14", - "8.7.27", - "8.7.17", - "8.7.9", - "10.4.3", - "10.0.0", - "10.1.0", - "9.5.13", - "9.5.5", - "8.7.22", - "8.7.10", - "8.7.24", - "8.7.13", - "8.7.14", - "8.7.19", - "9.5.17", - "9.3.2", - "9.5.15", - "8.7.8", - "9.3.3", - "8.7.32", - "10.4.0", - "10.4.1", - "9.5.18", - "9.1.0", - "9.5.19", - "9.5.2", - "8.7.26", - "8.7.20", - "10.2.0", - "8.7.31", - "8.7.11", - "9.2.1", - "8.7.25", - "9.5.10", - "10.2.2", - "10.4.2", - "9.5.9", - "9.2.0", - "9.3.0", - "9.5.16", - "10.3.0", - "8.7.7", - "10.4.4", - "8.7.12", - "8.7.29", - "10.2.1", - "9.5.8", - "9.5.4", - "9.5.0", - "8.7.28", - "8.7.23", - "9.0.0", - "8.7.16", + Version( + value="8.7.10", + release_date=datetime(2018, 2, 6, 10, 46, 2, tzinfo=tzlocal()), + ), + Version( + value="8.7.11", + release_date=datetime(2018, 3, 13, 12, 44, 45, tzinfo=tzlocal()), + ), + Version( + value="8.7.12", + release_date=datetime(2018, 3, 22, 11, 35, 42, tzinfo=tzlocal()), + ), + Version( + value="8.7.13", + release_date=datetime(2018, 4, 17, 8, 15, 46, tzinfo=tzlocal()), + ), + Version( + value="8.7.14", + release_date=datetime(2018, 5, 22, 13, 51, 9, tzinfo=tzlocal()), + ), + Version( + value="8.7.15", + release_date=datetime(2018, 5, 23, 11, 31, 21, tzinfo=tzlocal()), + ), + Version( + value="8.7.16", + release_date=datetime(2018, 6, 11, 17, 18, 14, tzinfo=tzlocal()), + ), + Version( + value="8.7.17", + release_date=datetime(2018, 7, 12, 11, 29, 19, tzinfo=tzlocal()), + ), + Version( + value="8.7.18", + release_date=datetime(2018, 7, 31, 8, 15, 29, tzinfo=tzlocal()), + ), + Version( + value="8.7.19", + release_date=datetime(2018, 8, 21, 7, 23, 21, tzinfo=tzlocal()), + ), + Version( + value="8.7.21", + release_date=datetime(2018, 12, 11, 12, 40, 12, tzinfo=tzlocal()), + ), + Version( + value="8.7.20", + release_date=datetime(2018, 10, 30, 10, 39, 51, tzinfo=tzlocal()), + ), + Version( + value="8.7.22", + release_date=datetime(2018, 12, 14, 7, 43, 50, tzinfo=tzlocal()), + ), + Version( + value="8.7.23", + release_date=datetime(2019, 1, 22, 10, 10, 2, tzinfo=tzlocal()), + ), + Version( + value="8.7.24", + release_date=datetime(2019, 1, 22, 15, 25, 55, tzinfo=tzlocal()), + ), + Version( + value="8.7.25", + release_date=datetime(2019, 5, 7, 10, 5, 55, tzinfo=tzlocal()), + ), + Version( + value="8.7.26", + release_date=datetime(2019, 5, 15, 11, 24, 12, tzinfo=tzlocal()), + ), + Version( + value="8.7.27", + release_date=datetime(2019, 6, 25, 8, 24, 21, tzinfo=tzlocal()), + ), + Version( + value="8.7.28", + release_date=datetime(2019, 10, 15, 7, 21, 52, tzinfo=tzlocal()), + ), + Version( + value="8.7.29", + release_date=datetime(2019, 10, 30, 21, 0, 45, tzinfo=tzlocal()), + ), + Version( + value="8.7.30", + release_date=datetime(2019, 12, 17, 10, 49, 17, tzinfo=tzlocal()), + ), + Version( + value="8.7.31", + release_date=datetime(2020, 2, 17, 23, 29, 16, tzinfo=tzlocal()), + ), + Version( + value="8.7.7", + release_date=datetime(2017, 9, 19, 14, 22, 53, tzinfo=tzlocal()), + ), + Version( + value="8.7.32", + release_date=datetime(2020, 3, 31, 8, 33, 3, tzinfo=tzlocal()), + ), + Version( + value="8.7.8", + release_date=datetime(2017, 10, 10, 16, 8, 44, tzinfo=tzlocal()), + ), + Version( + value="8.7.9", + release_date=datetime(2017, 12, 12, 16, 9, 50, tzinfo=tzlocal()), + ), + Version( + value="9.0.0", + release_date=datetime(2017, 12, 12, 16, 48, 22, tzinfo=tzlocal()), + ), + Version( + value="9.1.0", + release_date=datetime(2018, 1, 30, 15, 31, 12, tzinfo=tzlocal()), + ), + Version( + value="9.2.0", + release_date=datetime(2018, 4, 9, 20, 51, 35, tzinfo=tzlocal()), + ), + Version( + value="9.2.1", + release_date=datetime(2018, 5, 22, 13, 47, 11, tzinfo=tzlocal()), + ), + Version( + value="9.3.0", + release_date=datetime(2018, 6, 11, 17, 14, 33, tzinfo=tzlocal()), + ), + Version( + value="9.3.1", + release_date=datetime(2018, 7, 12, 11, 33, 12, tzinfo=tzlocal()), + ), + Version( + value="9.3.2", + release_date=datetime(2018, 7, 12, 15, 51, 49, tzinfo=tzlocal()), + ), + Version( + value="9.3.3", + release_date=datetime(2018, 7, 31, 8, 20, 17, tzinfo=tzlocal()), + ), + Version( + value="9.5.0", + release_date=datetime(2018, 10, 2, 8, 10, 33, tzinfo=tzlocal()), + ), + Version( + value="9.4.0", + release_date=datetime(2018, 9, 4, 12, 8, 20, tzinfo=tzlocal()), + ), + Version( + value="9.5.1", + release_date=datetime(2018, 10, 30, 10, 45, 30, tzinfo=tzlocal()), + ), + Version( + value="9.5.10", + release_date=datetime(2019, 10, 15, 7, 29, 55, tzinfo=tzlocal()), + ), + Version( + value="9.5.11", + release_date=datetime(2019, 10, 30, 20, 46, 49, tzinfo=tzlocal()), + ), + Version( + value="9.5.12", + release_date=datetime(2019, 12, 17, 10, 53, 45, tzinfo=tzlocal()), + ), + Version( + value="9.5.13", + release_date=datetime(2019, 12, 17, 14, 17, 37, tzinfo=tzlocal()), + ), + Version( + value="9.5.14", + release_date=datetime(2020, 2, 17, 23, 37, 2, tzinfo=tzlocal()), + ), + Version( + value="9.5.15", + release_date=datetime(2020, 3, 31, 8, 40, 25, tzinfo=tzlocal()), + ), + Version( + value="9.5.16", + release_date=datetime(2020, 4, 28, 9, 22, 14, tzinfo=tzlocal()), + ), + Version( + value="9.5.17", + release_date=datetime(2020, 5, 12, 10, 36, tzinfo=tzlocal()), + ), + Version( + value="9.5.18", + release_date=datetime(2020, 5, 19, 13, 10, 50, tzinfo=tzlocal()), + ), + Version( + value="9.5.2", + release_date=datetime(2018, 12, 11, 12, 42, 55, tzinfo=tzlocal()), + ), + Version( + value="9.5.19", + release_date=datetime(2020, 6, 9, 8, 44, 34, tzinfo=tzlocal()), + ), + Version( + value="9.5.3", + release_date=datetime(2018, 12, 14, 7, 28, 48, tzinfo=tzlocal()), + ), + Version( + value="9.5.4", + release_date=datetime(2019, 1, 22, 10, 12, 4, tzinfo=tzlocal()), + ), + Version( + value="9.5.5", + release_date=datetime(2019, 3, 4, 20, 25, 8, tzinfo=tzlocal()), + ), + Version( + value="9.5.6", + release_date=datetime(2019, 5, 7, 10, 16, 30, tzinfo=tzlocal()), + ), + Version( + value="9.5.7", + release_date=datetime(2019, 5, 15, 11, 41, 51, tzinfo=tzlocal()), + ), + Version( + value="9.5.8", + release_date=datetime(2019, 6, 25, 8, 28, 51, tzinfo=tzlocal()), + ), + Version( + value="9.5.9", + release_date=datetime(2019, 8, 20, 9, 33, 35, tzinfo=tzlocal()), + ), + Version( + value="10.0.0", + release_date=datetime(2019, 7, 23, 7, 6, 3, tzinfo=tzlocal()), + ), + Version( + value="10.1.0", + release_date=datetime(2019, 10, 1, 8, 18, 18, tzinfo=tzlocal()), + ), + Version( + value="10.2.0", + release_date=datetime(2019, 12, 3, 11, 16, 26, tzinfo=tzlocal()), + ), + Version( + value="10.2.1", + release_date=datetime(2019, 12, 17, 11, 0, tzinfo=tzlocal()), + ), + Version( + value="10.2.2", + release_date=datetime(2019, 12, 17, 11, 36, 14, tzinfo=tzlocal()), + ), + Version( + value="10.3.0", + release_date=datetime(2020, 2, 25, 12, 50, 9, tzinfo=tzlocal()), + ), + Version( + value="10.4.0", + release_date=datetime(2020, 4, 21, 8, 0, 15, tzinfo=tzlocal()), + ), + Version( + value="10.4.1", + release_date=datetime(2020, 4, 28, 9, 7, 54, tzinfo=tzlocal()), + ), + Version( + value="10.4.2", + release_date=datetime(2020, 5, 12, 10, 41, 40, tzinfo=tzlocal()), + ), + Version( + value="10.4.4", + release_date=datetime(2020, 6, 9, 8, 56, 30, tzinfo=tzlocal()), + ), + Version( + value="10.4.3", + release_date=datetime(2020, 5, 19, 13, 16, 31, tzinfo=tzlocal()), + ), } def test_composer_url(self): @@ -142,45 +343,50 @@ def test_extract_versions(self): def test_fetch(self): - assert self.version_api.get("typo3/cms-core") == set() + assert self.version_api.get("typo3/cms-core") == {"valid": set(), "new": set()} client_session = MockClientSession(self.response) asyncio.run(self.version_api.fetch("typo3/cms-core", client_session)) - assert self.version_api.get("typo3/cms-core") == self.expected_versions + assert self.version_api.cache["typo3/cms-core"] == self.expected_versions class TestMavenVersionAPI(TestCase): @classmethod def setUpClass(cls): cls.version_api = MavenVersionAPI() - with open(os.path.join(TEST_DATA, "maven_api", "maven-metadata.xml")) as f: - cls.response = ET.parse(f) - - with open(os.path.join(TEST_DATA, "maven_api", "maven-metadata.xml"), "rb") as f: - cls.content = f.read() + with open(os.path.join(TEST_DATA, "maven_api", "easygcm.html"), "rb") as f: + data = f.read() + cls.response = BeautifulSoup(data) + cls.content = data def test_artifact_url(self): - eg_comps1 = ["org.apache", "kafka"] - eg_comps2 = ["apple.msft.windows.mac.oss", "exfat-ntfs"] + eg_pkg1 = "org.apache:kafka" + eg_pkg2 = "apple.msft.windows.mac.oss:exfat-ntfs" - url1 = self.version_api.artifact_url(eg_comps1) - url2 = self.version_api.artifact_url(eg_comps2) + url1 = self.version_api.artifact_url(eg_pkg1) + url2 = self.version_api.artifact_url(eg_pkg2) - assert "https://repo1.maven.org/maven2/org/apache/kafka/maven-metadata.xml" == url1 - assert ( - "https://repo1.maven.org/maven2" - "/apple/msft/windows/mac/oss/exfat-ntfs/maven-metadata.xml" == url2 - ) + assert "https://repo1.maven.org/maven2/org/apache/kafka/" == url1 + assert "https://repo1.maven.org/maven2/apple/msft/windows/mac/oss/exfat-ntfs/" == url2 def test_extract_versions(self): - expected_versions = {"1.2.2", "1.2.3", "1.3.0"} + expected_versions = { + Version(value="1.3.0", release_date=datetime(2015, 3, 12, 15, 20, tzinfo=UTC)), + Version(value="1.2.3", release_date=datetime(2014, 12, 22, 10, 53, tzinfo=UTC)), + Version(value="1.2.2", release_date=datetime(2014, 12, 22, 10, 29, tzinfo=UTC)), + } assert expected_versions == self.version_api.extract_versions(self.response) def test_fetch(self): - assert self.version_api.get("org.apache:kafka") == set() - expected = {"1.2.3", "1.3.0", "1.2.2"} + assert self.version_api.get("org.apache:kafka") == {"new": set(), "valid": set()} + expected = { + Version(value="1.2.2", release_date=datetime(2014, 12, 22, 10, 29, tzinfo=UTC)), + Version(value="1.3.0", release_date=datetime(2015, 3, 12, 15, 20, tzinfo=UTC)), + Version(value="1.2.3", release_date=datetime(2014, 12, 22, 10, 53, tzinfo=UTC)), + } + client_session = MockClientSession(self.content) asyncio.run(self.version_api.fetch("org.apache:kafka", client_session)) - assert self.version_api.get("org.apache:kafka") == expected + assert self.version_api.cache["org.apache:kafka"] == expected class TestNugetVersionAPI(TestCase): @@ -191,20 +397,62 @@ def setUpClass(cls): cls.response = json.load(f) cls.expected_versions = { - "0.23.0", - "0.24.0", - "1.0.0", - "1.0.1", - "1.0.2", - "2.0.0", - "2.0.0-preview01", - "2.6.0", - "2.1.0", - "2.2.0", - "2.3.0", - "2.4.0", - "2.5.0", - "2.7.0", + Version( + value="1.0.0", + release_date=datetime(2018, 9, 13, 8, 16, 0, 420000, tzinfo=tzlocal()), + ), + Version( + value="1.0.1", + release_date=datetime(2020, 1, 17, 15, 31, 41, 857000, tzinfo=tzlocal()), + ), + Version( + value="1.0.2", + release_date=datetime(2020, 4, 21, 12, 24, 53, 877000, tzinfo=tzlocal()), + ), + Version( + value="2.0.0-preview01", + release_date=datetime(2018, 1, 9, 17, 12, 20, 440000, tzinfo=tzlocal()), + ), + Version( + value="2.0.0", + release_date=datetime(2018, 9, 27, 13, 33, 15, 370000, tzinfo=tzlocal()), + ), + Version( + value="2.1.0", + release_date=datetime(2018, 10, 16, 6, 59, 44, 680000, tzinfo=tzlocal()), + ), + Version( + value="2.2.0", + release_date=datetime(2018, 11, 23, 8, 13, 8, 3000, tzinfo=tzlocal()), + ), + Version( + value="2.3.0", + release_date=datetime(2019, 6, 27, 14, 27, 31, 613000, tzinfo=tzlocal()), + ), + Version( + value="2.4.0", + release_date=datetime(2020, 1, 17, 15, 11, 5, 810000, tzinfo=tzlocal()), + ), + Version( + value="2.5.0", + release_date=datetime(2020, 3, 24, 14, 22, 39, 960000, tzinfo=tzlocal()), + ), + Version( + value="2.7.0", + release_date=datetime(2020, 4, 21, 12, 27, 36, 427000, tzinfo=tzlocal()), + ), + Version( + value="2.6.0", + release_date=datetime(2020, 3, 27, 11, 6, 27, 500000, tzinfo=tzlocal()), + ), + Version( + value="0.24.0", + release_date=datetime(2018, 3, 30, 7, 25, 18, 393000, tzinfo=tzlocal()), + ), + Version( + value="0.23.0", + release_date=datetime(2018, 1, 17, 9, 32, 59, 283000, tzinfo=tzlocal()), + ), } def test_nuget_url(self): @@ -219,13 +467,30 @@ def test_extract_versions(self): def test_fetch(self): - assert self.version_api.get("Exfat.Ntfs") == set() + assert self.version_api.get("Exfat.Ntfs") == {"new": set(), "valid": set()} client_session = MockClientSession(self.response) asyncio.run(self.version_api.fetch("Exfat.Ntfs", client_session)) - assert self.version_api.get("Exfat.Ntfs") == self.expected_versions + assert self.version_api.get("Exfat.Ntfs") == { + "new": set(), + "valid": { + "2.0.0", + "2.1.0", + "2.0.0-preview01", + "0.24.0", + "0.23.0", + "1.0.1", + "2.2.0", + "2.4.0", + "1.0.0", + "1.0.2", + "2.3.0", + "2.7.0", + "2.5.0", + "2.6.0", + }, + } # def test_load_to_api(self): - # assert self.version_api.get("Exfat.Ntfs") == set() # mock_response = MagicMock() diff --git a/vulnerabilities/tests/test_ruby.py b/vulnerabilities/tests/test_ruby.py index 664f001ed..a666bc5ae 100644 --- a/vulnerabilities/tests/test_ruby.py +++ b/vulnerabilities/tests/test_ruby.py @@ -55,7 +55,7 @@ def setUpClass(cls): @patch( "vulnerabilities.package_managers.RubyVersionAPI.get", - return_value={"1.0.0", "1.8.0", "2.0.3"}, + return_value={"valid": {"1.0.0", "1.8.0", "2.0.3"}, "new": {}}, ) def test_process_file(self, mock_write): expected_advisories = [ diff --git a/vulnerabilities/tests/test_rust.py b/vulnerabilities/tests/test_rust.py index f563ccc5e..7a28150b8 100644 --- a/vulnerabilities/tests/test_rust.py +++ b/vulnerabilities/tests/test_rust.py @@ -30,23 +30,27 @@ from vulnerabilities.importers.rust import categorize_versions from vulnerabilities.importers.rust import get_advisory_data from vulnerabilities.importers.rust import RustDataSource +from vulnerabilities.package_managers import Version +from vulnerabilities.package_managers import CratesVersionAPI from vulnerabilities.helpers import AffectedPackage BASE_DIR = os.path.dirname(os.path.abspath(__file__)) TEST_DATA = os.path.join(BASE_DIR, "test_data/rust") -MOCKED_CRATES_API_VERSIONS = { - "bitvec": {"0.10.0", "0.12.0", "0.18.0"}, - "bumpalo": {"2.8.0", "3.0.1", "3.2.5"}, - "cbox": {"0.10.0", "0.12.0", "0.18.0"}, - "flatbuffers": {"0.3.0", "0.5.0", "0.6.5"}, - "hyper": {"0.10.0", "0.12.0", "0.13.0"}, - "byte_struct": {"0.6.1", "0.6.0", "1.0.0"}, -} +MOCKED_CRATES_API_VERSIONS = CratesVersionAPI( + cache={ + "bitvec": {Version("0.10.0"), Version("0.12.0"), Version("0.18.0")}, + "bumpalo": {Version("2.8.0"), Version("3.0.1"), Version("3.2.5")}, + "cbox": {Version("0.10.0"), Version("0.12.0"), Version("0.18.0")}, + "flatbuffers": {Version("0.3.0"), Version("0.5.0"), Version("0.6.5")}, + "hyper": {Version("0.10.0"), Version("0.12.0"), Version("0.13.0")}, + "byte_struct": {Version("0.6.1"), Version("0.6.0"), Version("1.0.0")}, + } +) def test_categorize_versions(): - flatbuffers_versions = MOCKED_CRATES_API_VERSIONS["flatbuffers"] + flatbuffers_versions = MOCKED_CRATES_API_VERSIONS.get("flatbuffers")["valid"] unaffected_ranges = [VersionSpecifier.from_scheme_version_spec_string("semver", "< 0.4.0")] affected_ranges = [ diff --git a/vulnerabilities/tests/test_safety_db.py b/vulnerabilities/tests/test_safety_db.py index 20bbc87b2..ddc78f9a7 100644 --- a/vulnerabilities/tests/test_safety_db.py +++ b/vulnerabilities/tests/test_safety_db.py @@ -22,16 +22,16 @@ # Visit https://github.com/nexB/vulnerablecode/ for support and download. import json import os -from unittest.mock import patch from unittest import TestCase from packageurl import PackageURL -from vulnerabilities.importers.safety_db import PypiVersionAPI from vulnerabilities.importers.safety_db import categorize_versions from vulnerabilities.importers.safety_db import SafetyDbDataSource from vulnerabilities.data_source import Advisory from vulnerabilities.data_source import Reference +from vulnerabilities.package_managers import PypiVersionAPI +from vulnerabilities.package_managers import Version from vulnerabilities.helpers import AffectedPackage BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -39,9 +39,16 @@ MOCK_VERSION_API = PypiVersionAPI( cache={ - "ampache": {"2.0", "5.2.1"}, - "django": {"1.8", "1.4.19", "1.4.22", "1.5.1", "1.6.9", "1.8.14"}, - "zulip": {"2.0", "2.1.1", "2.1.2", "2.1.3"}, + "ampache": {Version("2.0"), Version("5.2.1")}, + "django": { + Version("1.8"), + Version("1.4.19"), + Version("1.4.22"), + Version("1.5.1"), + Version("1.6.9"), + Version("1.8.14"), + }, + "zulip": {Version("2.0"), Version("2.1.1"), Version("2.1.2"), Version("2.1.3")}, } )