From 2fa69153893579f82b76d8add7b7640a6d9c00a4 Mon Sep 17 00:00:00 2001 From: Ziad Date: Fri, 15 Jul 2022 14:25:50 +0200 Subject: [PATCH] Add ruby importer Fix style test Fix test Rewrite affected_packages Ruby initial config Reference: #796 Signed-off-by: ziadhany --- vulnerabilities/importers/ruby.py | 271 +++++++++++------- vulnerabilities/tests/conftest.py | 1 - .../ruby/CVE-2007-5770-expected.json | 46 +++ .../tests/test_data/ruby/CVE-2007-5770.yml | 17 ++ .../ruby/CVE-2010-1330-expected.json | 35 +++ .../tests/test_data/ruby/CVE-2010-1330.yml | 15 + .../ruby/CVE-2018-11627-expected.json | 34 +++ .../ruby/{sinatra => }/CVE-2018-11627.yml | 0 .../ruby/CVE-2018-7212-expected.json | 57 ++++ .../ruby/{sinatra => }/CVE-2018-7212.yml | 0 .../test_data/ruby/OSVDB-125675-expected.json | 30 ++ .../ruby/{sidekiq => }/OSVDB-125675.yml | 0 .../test_data/ruby/sidekiq/OSVDB-125676.yml | 14 - .../test_data/ruby/sidekiq/OSVDB-125678.yml | 9 - vulnerabilities/tests/test_ruby.py | 146 ++-------- 15 files changed, 422 insertions(+), 253 deletions(-) create mode 100644 vulnerabilities/tests/test_data/ruby/CVE-2007-5770-expected.json create mode 100644 vulnerabilities/tests/test_data/ruby/CVE-2007-5770.yml create mode 100644 vulnerabilities/tests/test_data/ruby/CVE-2010-1330-expected.json create mode 100644 vulnerabilities/tests/test_data/ruby/CVE-2010-1330.yml create mode 100644 vulnerabilities/tests/test_data/ruby/CVE-2018-11627-expected.json rename vulnerabilities/tests/test_data/ruby/{sinatra => }/CVE-2018-11627.yml (100%) create mode 100644 vulnerabilities/tests/test_data/ruby/CVE-2018-7212-expected.json rename vulnerabilities/tests/test_data/ruby/{sinatra => }/CVE-2018-7212.yml (100%) create mode 100644 vulnerabilities/tests/test_data/ruby/OSVDB-125675-expected.json rename vulnerabilities/tests/test_data/ruby/{sidekiq => }/OSVDB-125675.yml (100%) delete mode 100644 vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125676.yml delete mode 100644 vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125678.yml diff --git a/vulnerabilities/importers/ruby.py b/vulnerabilities/importers/ruby.py index 1e116e3ff..ddc86d0b5 100644 --- a/vulnerabilities/importers/ruby.py +++ b/vulnerabilities/importers/ruby.py @@ -7,132 +7,189 @@ # See https://aboutcode.org for more information about nexB OSS projects. # -import asyncio -from typing import List -from typing import Set +import logging +from pathlib import Path +from typing import Iterable from dateutil.parser import parse +from django.db.models import QuerySet from packageurl import PackageURL from pytz import UTC -from univers.version_range import VersionRange -from univers.versions import SemverVersion +from univers.version_range import GemVersionRange +from univers.versions import RubygemsVersion from vulnerabilities.importer import AdvisoryData +from vulnerabilities.importer import AffectedPackage from vulnerabilities.importer import GitImporter from vulnerabilities.importer import Reference +from vulnerabilities.importer import VulnerabilitySeverity +from vulnerabilities.improver import Improver +from vulnerabilities.improver import Inference +from vulnerabilities.models import Advisory from vulnerabilities.package_managers import RubyVersionAPI +from vulnerabilities.severity_systems import SCORING_SYSTEMS +from vulnerabilities.utils import build_description +from vulnerabilities.utils import evolve_purl from vulnerabilities.utils import load_yaml -from vulnerabilities.utils import nearest_patched_package +logger = logging.getLogger(__name__) -class RubyImporter(GitImporter): - def __enter__(self): - super(RubyImporter, self).__enter__() - - if not getattr(self, "_added_files", None): - self._added_files, self._updated_files = self.file_changes( - recursive=True, file_ext="yml", subdir="./gems" - ) - self.pkg_manager_api = RubyVersionAPI() - self.set_api(self.collect_packages()) - - def set_api(self, packages): - asyncio.run(self.pkg_manager_api.load_api(packages)) - - def updated_advisories(self) -> Set[AdvisoryData]: - files = self._updated_files.union(self._added_files) - advisories = [] - for f in files: - processed_data = self.process_file(f) - if processed_data: - advisories.append(processed_data) - return self.batch_advisories(advisories) - - def collect_packages(self): - packages = set() - files = self._updated_files.union(self._added_files) - for f in files: - data = load_yaml(f) - if data.get("gem"): - packages.add(data["gem"]) - - return packages - - def process_file(self, path) -> List[AdvisoryData]: - record = load_yaml(path) +class RubyImporter(GitImporter): + license_url = "https://github.com/rubysec/ruby-advisory-db/blob/master/LICENSE.txt" + spdx_license_expression = "unknown" + + def __init__(self): + super().__init__(repo_url="git+https://github.com/rubysec/ruby-advisory-db") + + def advisory_data(self) -> Iterable[AdvisoryData]: + self.clone() + base_path = Path(self.vcs_response.dest_dir) + supported_subdir = ["rubies", "gems"] + for subdir in supported_subdir: + for file_path in base_path.glob(f"{subdir}/**/*.yml"): + raw_data = load_yaml(file_path) + yield parse_ruby_advisory(raw_data, subdir) + + +def parse_ruby_advisory(record, schema_type): + """ + Parse a ruby advisory file and return an AdvisoryData or None. + Each advisory file contains the advisory information in YAML format. + Schema: https://github.com/rubysec/ruby-advisory-db/tree/master/spec/schemas + """ + if schema_type == "gems": package_name = record.get("gem") - if not package_name: - return - - if "cve" in record: - cve_id = "CVE-{}".format(record["cve"]) - 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 :( ). - if not safe_version_ranges: - safe_version_ranges = [] - safe_version_ranges += record.get("unaffected_versions", []) - safe_version_ranges = [i for i in safe_version_ranges if i] - - if not getattr(self, "pkg_manager_api", None): - self.pkg_manager_api = RubyVersionAPI() - all_vers = self.pkg_manager_api.get(package_name, until=publish_time).valid_versions - safe_versions, affected_versions = self.categorize_versions(all_vers, safe_version_ranges) - - impacted_purls = [ - PackageURL( - name=package_name, - type="gem", - version=version, - ) - for version in affected_versions - ] - - resolved_purls = [ - PackageURL( - name=package_name, - type="gem", - version=version, - ) - for version in safe_versions - ] + library = record.get("library") + framework = record.get("framework") + platform = record.get("platform") + purl = PackageURL(type="gem", name=package_name) - references = [] - if record.get("url"): - references.append(Reference(url=record.get("url"))) + return AdvisoryData( + aliases=get_aliases(record), + summary=get_summary(record), + affected_packages=get_affected_packages(record, purl), + references=get_references(record), + date_published=get_publish_time(record), + ) + elif schema_type == "rubies": + engine = record.get("engine") # engine enum: [jruby, rbx, ruby] + purl = PackageURL(type="ruby", name=engine) return AdvisoryData( - summary=record.get("description", ""), - affected_packages=nearest_patched_package(impacted_purls, resolved_purls), - references=references, - vulnerability_id=cve_id, + aliases=get_aliases(record), + summary=get_summary(record), + affected_packages=get_affected_packages(record, purl), + references=get_references(record), + date_published=get_publish_time(record), ) - @staticmethod - def categorize_versions(all_versions, unaffected_version_ranges): - for id, elem in enumerate(unaffected_version_ranges): - unaffected_version_ranges[id] = VersionRange.from_scheme_version_spec_string( - "semver", elem +def get_affected_packages(record, purl): + 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 :( ). + if not safe_version_ranges: + safe_version_ranges = [] + safe_version_ranges += record.get("unaffected_versions", []) + safe_version_ranges = [i for i in safe_version_ranges if i] + + affected_packages = [] + affected_version_ranges = [ + GemVersionRange.from_native(elem).invert() for elem in safe_version_ranges + ] + + for affected_version_range in affected_version_ranges: + affected_packages.append( + AffectedPackage( + package=purl, + affected_version_range=affected_version_range, ) + ) + return affected_packages + + +def get_aliases(record) -> [str]: + aliases = [] + if record.get("cve"): + aliases.append("CVE-{}".format(record.get("cve"))) + if record.get("osvdb"): + aliases.append("OSV-{}".format(record.get("osvdb"))) + if record.get("ghsa"): + aliases.append("GHSA-{}".format(record.get("ghsa"))) + return aliases + - safe_versions = [] - vulnerable_versions = [] - for i in all_versions: - vobj = SemverVersion(i) - is_vulnerable = False - for ver_rng in unaffected_version_ranges: - if vobj in ver_rng: - safe_versions.append(i) - is_vulnerable = True - break - - if not is_vulnerable: - vulnerable_versions.append(i) - - return safe_versions, vulnerable_versions +def get_references(record) -> [Reference]: + references = [] + cvss_v2 = record.get("cvss_v2") + cvss_v3 = record.get("cvss_v3") + + if record.get("url"): + if not (cvss_v2 or cvss_v3): + references.append(Reference(url=record.get("url"))) + if cvss_v2: + references.append( + Reference( + url=record.get("url"), + severities=[ + VulnerabilitySeverity(system=SCORING_SYSTEMS["cvssv2"], value=cvss_v2) + ], + ) + ) + if cvss_v3: + references.append( + Reference( + url=record.get("url"), + severities=[ + VulnerabilitySeverity(system=SCORING_SYSTEMS["cvssv3"], value=cvss_v3) + ], + ) + ) + return references + + +def get_publish_time(record): + return parse(record["date"]).replace(tzinfo=UTC) + + +def get_summary(record): + title = record.get("title") + description = record.get("description", "") + return build_description(summary=title, description=description) + + +class RubyImprover(Improver): + pkg_manager_api = RubyVersionAPI() + + @property + def interesting_advisories(self) -> QuerySet: + return Advisory.objects.filter(created_by=RubyImporter.qualified_name) + + def get_inferences(self, advisory_data) -> Iterable[Inference]: + for affected_package in advisory_data.affected_packages: + purl = affected_package.package + pkg_name = purl.name + all_vers_pkgs = self.pkg_manager_api.fetch(pkg_name) + + safe_versions = [] + affected_purls = [] + for pkg_version in all_vers_pkgs: + vobj = RubygemsVersion(pkg_version.value) + try: + if vobj in affected_package.affected_version_range: + new_purl = evolve_purl(purl=purl, version=str(pkg_version.value)) + affected_purls.append(new_purl) + else: + safe_versions.append(pkg_version.value) + except Exception as e: + logger.error(f"{e}") + + for fixed_version in safe_versions: + fixed_purl = evolve_purl(purl=purl, version=str(fixed_version)) + yield Inference.from_advisory_data( + advisory_data, + confidence=90, + affected_purls=affected_purls, + fixed_purl=fixed_purl, + ) diff --git a/vulnerabilities/tests/conftest.py b/vulnerabilities/tests/conftest.py index e759b1db6..f7ae73b18 100644 --- a/vulnerabilities/tests/conftest.py +++ b/vulnerabilities/tests/conftest.py @@ -28,7 +28,6 @@ def no_rmtree(monkeypatch): "test_apache_kafka.py", "test_models.py", "test_package_managers.py", - "test_ruby.py", "test_rust.py", "test_suse_backports.py", "test_suse.py", diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2007-5770-expected.json b/vulnerabilities/tests/test_data/ruby/CVE-2007-5770-expected.json new file mode 100644 index 000000000..e502318e7 --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2007-5770-expected.json @@ -0,0 +1,46 @@ +{ + "aliases": [ + "CVE-2007-5770" + ], + "summary": "Ruby Net::HTTPS library does not validate server certificate CN\nThe (1) Net::ftptls, (2) Net::telnets, (3) Net::imap, (4) Net::pop, and (5)\nNet::smtp libraries in Ruby 1.8.5 and 1.8.6 do not verify that the\ncommonName (CN) field in a server certificate matches the domain name in a\nrequest sent over SSL, which makes it easier for remote attackers to\nintercept SSL transmissions via a man-in-the-middle attack or spoofed web\nsite, different components than CVE-2007-5162.", + "affected_packages": [ + { + "package": { + "type": "ruby", + "namespace": null, + "name": "ruby", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<1.8.6.230|>=1.8.7", + "fixed_version": null + }, + { + "package": { + "type": "ruby", + "namespace": null, + "name": "ruby", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<1.8.7", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "http://www.cvedetails.com/cve/CVE-2007-5770/", + "severities": [ + { + "system": "cvssv2", + "value": "4.3", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2007-10-08T00:00:00+00:00" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2007-5770.yml b/vulnerabilities/tests/test_data/ruby/CVE-2007-5770.yml new file mode 100644 index 000000000..fcb1c372b --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2007-5770.yml @@ -0,0 +1,17 @@ +--- +engine: ruby +cve: 2007-5770 +url: http://www.cvedetails.com/cve/CVE-2007-5770/ +title: Ruby Net::HTTPS library does not validate server certificate CN +date: 2007-10-08 +description: | + The (1) Net::ftptls, (2) Net::telnets, (3) Net::imap, (4) Net::pop, and (5) + Net::smtp libraries in Ruby 1.8.5 and 1.8.6 do not verify that the + commonName (CN) field in a server certificate matches the domain name in a + request sent over SSL, which makes it easier for remote attackers to + intercept SSL transmissions via a man-in-the-middle attack or spoofed web + site, different components than CVE-2007-5162. +cvss_v2: 4.3 +patched_versions: +- ~> 1.8.6.230 +- '>= 1.8.7' diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2010-1330-expected.json b/vulnerabilities/tests/test_data/ruby/CVE-2010-1330-expected.json new file mode 100644 index 000000000..9ce846c16 --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2010-1330-expected.json @@ -0,0 +1,35 @@ +{ + "aliases": [ + "CVE-2010-1330", + "OSV-77297" + ], + "summary": "CVE-2010-1330 jruby: XSS in the regular expression engine when processing invalid UTF-8 byte sequences\nThe regular expression engine in JRuby before 1.4.1, when $KCODE is set to 'u', does not properly handle characters immediately after a UTF-8 character, which allows remote attackers to conduct cross-site scripting (XSS) attacks via a crafted string.", + "affected_packages": [ + { + "package": { + "type": "ruby", + "namespace": null, + "name": "jruby", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<1.4.1", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "http://jruby.org/2010/04/26/jruby-1-4-1-xss-vulnerability", + "severities": [ + { + "system": "cvssv2", + "value": "4.3", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2010-04-26T00:00:00+00:00" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2010-1330.yml b/vulnerabilities/tests/test_data/ruby/CVE-2010-1330.yml new file mode 100644 index 000000000..78e0dfdd9 --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2010-1330.yml @@ -0,0 +1,15 @@ +--- +engine: jruby +cve: 2010-1330 +osvdb: 77297 +url: http://jruby.org/2010/04/26/jruby-1-4-1-xss-vulnerability +title: 'CVE-2010-1330 jruby: XSS in the regular expression engine when processing + invalid UTF-8 byte sequences' +date: 2010-04-26 +description: The regular expression engine in JRuby before 1.4.1, when $KCODE is set + to 'u', does not properly handle characters immediately after a UTF-8 character, + which allows remote attackers to conduct cross-site scripting (XSS) attacks via + a crafted string. +cvss_v2: 4.3 +patched_versions: +- '>= 1.4.1' diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2018-11627-expected.json b/vulnerabilities/tests/test_data/ruby/CVE-2018-11627-expected.json new file mode 100644 index 000000000..59c2013d1 --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2018-11627-expected.json @@ -0,0 +1,34 @@ +{ + "aliases": [ + "CVE-2018-11627" + ], + "summary": "Sinatra before 2.0.2 has XSS via the 400 Bad Request page that occurs upon a params parser exception.", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<2.0.2", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/issues/1428", + "severities": [ + { + "system": "cvssv3", + "value": "6.1", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2018-05-31T00:00:00+00:00" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/sinatra/CVE-2018-11627.yml b/vulnerabilities/tests/test_data/ruby/CVE-2018-11627.yml similarity index 100% rename from vulnerabilities/tests/test_data/ruby/sinatra/CVE-2018-11627.yml rename to vulnerabilities/tests/test_data/ruby/CVE-2018-11627.yml diff --git a/vulnerabilities/tests/test_data/ruby/CVE-2018-7212-expected.json b/vulnerabilities/tests/test_data/ruby/CVE-2018-7212-expected.json new file mode 100644 index 000000000..5827eb113 --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/CVE-2018-7212-expected.json @@ -0,0 +1,57 @@ +{ + "aliases": [ + "CVE-2018-7212" + ], + "summary": "sinatra ruby gem path traversal via backslash characters on Windows\nAn issue was discovered in rack-protection/lib/rack/protection/path_traversal.rb\nin Sinatra 2.x before 2.0.1 on Windows. Path traversal is possible via backslash\ncharacters.", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<2.0.1", + "fixed_version": null + }, + { + "package": { + "type": "gem", + "namespace": null, + "name": "sinatra", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/>1.0.0", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv2", + "value": "5.0", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "", + "url": "https://github.com/sinatra/sinatra/pull/1379", + "severities": [ + { + "system": "cvssv3", + "value": "5.3", + "scoring_elements": "" + } + ] + } + ], + "date_published": "2018-01-09T00:00:00+00:00" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/sinatra/CVE-2018-7212.yml b/vulnerabilities/tests/test_data/ruby/CVE-2018-7212.yml similarity index 100% rename from vulnerabilities/tests/test_data/ruby/sinatra/CVE-2018-7212.yml rename to vulnerabilities/tests/test_data/ruby/CVE-2018-7212.yml diff --git a/vulnerabilities/tests/test_data/ruby/OSVDB-125675-expected.json b/vulnerabilities/tests/test_data/ruby/OSVDB-125675-expected.json new file mode 100644 index 000000000..78498d5b9 --- /dev/null +++ b/vulnerabilities/tests/test_data/ruby/OSVDB-125675-expected.json @@ -0,0 +1,30 @@ +{ + "aliases": [ + "OSV-125675" + ], + "summary": "Sidekiq Gem for Ruby Multiple Unspecified CSRF\nSidekiq::Web lacks CSRF protection", + "affected_packages": [ + { + "package": { + "type": "gem", + "namespace": null, + "name": "sidekiq", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:gem/<3.4.2", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "", + "url": "https://github.com/mperham/sidekiq/pull/2422", + "severities": [ + + ] + } + ], + "date_published": "2015-07-06T00:00:00+00:00" +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125675.yml b/vulnerabilities/tests/test_data/ruby/OSVDB-125675.yml similarity index 100% rename from vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125675.yml rename to vulnerabilities/tests/test_data/ruby/OSVDB-125675.yml diff --git a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125676.yml b/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125676.yml deleted file mode 100644 index 18ba94428..000000000 --- a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125676.yml +++ /dev/null @@ -1,14 +0,0 @@ ---- -gem: sidekiq -osvdb: 125676 -url: https://github.com/mperham/sidekiq/issues/2330 -title: | - Sidekiq Gem for Ruby web/views/queue.erb CurrentMessagesInQueue Element - Reflected XSS -date: 2015-06-04 -description: XSS via queue name in Sidekiq::Web -patched_versions: - - ">= 3.4.0" -related: - osvdb: - - 125677 diff --git a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125678.yml b/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125678.yml deleted file mode 100644 index 1566d10a7..000000000 --- a/vulnerabilities/tests/test_data/ruby/sidekiq/OSVDB-125678.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -gem: sidekiq -osvdb: 125678 -url: https://github.com/mperham/sidekiq/pull/2309 -title: Sidekiq Gem for Ruby web/views/queue.erb msg.display_class Element XSS -date: 2015-04-21 -description: XSS via job arguments display class in Sidekiq::Web -patched_versions: - - ">= 3.4.0" diff --git a/vulnerabilities/tests/test_ruby.py b/vulnerabilities/tests/test_ruby.py index e57026fe3..d54eaf5ec 100644 --- a/vulnerabilities/tests/test_ruby.py +++ b/vulnerabilities/tests/test_ruby.py @@ -6,135 +6,37 @@ # See https://github.com/nexB/vulnerablecode for support or download. # See https://aboutcode.org for more information about nexB OSS projects. # - - import os -import pathlib from unittest import TestCase -from unittest.mock import patch -from packageurl import PackageURL +import pytest -from vulnerabilities.importer import AdvisoryData -from vulnerabilities.importer import Reference -from vulnerabilities.importers.ruby import RubyImporter -from vulnerabilities.package_managers import RubyVersionAPI -from vulnerabilities.package_managers import VersionResponse -from vulnerabilities.utils import AffectedPackage +from vulnerabilities.importers.ruby import parse_ruby_advisory +from vulnerabilities.tests.util_tests import check_results_against_json +from vulnerabilities.utils import load_yaml BASE_DIR = os.path.dirname(os.path.abspath(__file__)) TEST_DATA = os.path.join(BASE_DIR, "test_data", "ruby") -MOCK_ADDED_FILES = [] - -for filepath in pathlib.Path(TEST_DATA).glob("**/*.yml"): - MOCK_ADDED_FILES.append(filepath.absolute()) - class RubyImporterTest(TestCase): - @classmethod - def setUpClass(cls): - data_source_cfg = { - "repository_url": "https://github.com/rubysec/ruby-advisory-db.git", - } - cls.data_src = RubyImporter(1, config=data_source_cfg) - cls.data_src.pkg_manager_api = RubyVersionAPI() - - @patch( - "vulnerabilities.package_managers.RubyVersionAPI.get", - return_value=VersionResponse( - valid_versions={"1.0.0", "1.8.0", "2.0.3"}, newer_versions=set() - ), - ) - def test_process_file(self, mock_write): - expected_advisories = [ - Advisory( - summary="An issue was discovered in rack-protection/lib/rack/protection/path_traversal.rb\nin Sinatra 2.x before 2.0.1 on Windows. Path traversal is possible via backslash\ncharacters.\n", - vulnerability_id="CVE-2018-7212", - affected_packages=[ - AffectedPackage( - vulnerable_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="1.8.0", - ), - patched_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="2.0.3", - ), - ) - ], - references=[ - Reference( - reference_id="", - url="https://github.com/sinatra/sinatra/pull/1379", - severities=[], - ) - ], - ), - Advisory( - summary="Sinatra before 2.0.2 has XSS via the 400 Bad Request page that occurs upon a params parser exception.\n", - vulnerability_id="CVE-2018-11627", - affected_packages=[ - AffectedPackage( - vulnerable_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="1.0.0", - ), - patched_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="2.0.3", - ), - ), - AffectedPackage( - vulnerable_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="1.8.0", - ), - patched_package=PackageURL( - type="gem", - namespace=None, - name="sinatra", - version="2.0.3", - ), - ), - ], - references=[ - Reference( - reference_id="", - url="https://github.com/sinatra/sinatra/issues/1428", - severities=[], - ) - ], - ), - ] - found_advisories = [] - for p in MOCK_ADDED_FILES: - advisory = self.data_src.process_file(p) - if advisory: - found_advisories.append(advisory) - - found_advisories = list(map(Advisory.normalized, found_advisories)) - expected_advisories = list(map(Advisory.normalized, expected_advisories)) - assert sorted(found_advisories) == sorted(expected_advisories) - - def test_categorize_versions(self): - - all_versions = ["1.0.0", "1.2.0", "9.0.2", "0.2.3"] - safe_ver_ranges = ["==1.0.0", ">1.2.0"] - - exp_safe_vers = ["1.0.0", "9.0.2"] - exp_aff_vers = ["1.2.0", "0.2.3"] - - safe_vers, aff_vers = self.data_src.categorize_versions(all_versions, safe_ver_ranges) - assert exp_aff_vers == aff_vers - assert exp_safe_vers == safe_vers + def test_get_affected_packages(self): + pass + + +@pytest.mark.parametrize( + "filename,expected_filename,schema_type", + [ + ("CVE-2018-7212.yml", "CVE-2018-7212-expected.json", "gems"), + ("CVE-2018-11627.yml", "CVE-2018-11627-expected.json", "gems"), + ("OSVDB-125675.yml", "OSVDB-125675-expected.json", "gems"), + ("CVE-2007-5770.yml", "CVE-2007-5770-expected.json", "rubies"), + ("CVE-2010-1330.yml", "CVE-2010-1330-expected.json", "rubies"), + ], +) +def test_advisories(filename, expected_filename, schema_type): + file_path = os.path.join(TEST_DATA, filename) + mock_response = load_yaml(file_path) + results = parse_ruby_advisory(mock_response, schema_type).to_dict() + expected_file = os.path.join(TEST_DATA, expected_filename) + check_results_against_json(results=results, expected_file=expected_file)