From d2f2fb57e54896b023394cc27cbd302f1d696972 Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Wed, 16 Nov 2022 10:59:23 -0800 Subject: [PATCH 01/14] Explore testing #971 Reference: https://github.com/nexB/vulnerablecode/issues/971 Signed-off-by: John M. Horan --- vulnerabilities/importers/__init__.py | 2 ++ vulnerabilities/importers/apache_httpd.py | 3 +++ vulnerabilities/tests/conftest.py | 2 +- vulnerabilities/tests/test_apache_httpd.py | 4 ++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/vulnerabilities/importers/__init__.py b/vulnerabilities/importers/__init__.py index 3594cbd4d..2831c1060 100644 --- a/vulnerabilities/importers/__init__.py +++ b/vulnerabilities/importers/__init__.py @@ -8,6 +8,7 @@ # from vulnerabilities.importers import alpine_linux +from vulnerabilities.importers import apache_httpd from vulnerabilities.importers import archlinux from vulnerabilities.importers import debian from vulnerabilities.importers import debian_oval @@ -41,6 +42,7 @@ debian_oval.DebianOvalImporter, npm.NpmImporter, retiredotnet.RetireDotnetImporter, + apache_httpd.ApacheHTTPDImporter, ] IMPORTERS_REGISTRY = {x.qualified_name: x for x in IMPORTERS_REGISTRY} diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index 56a142e30..95ec8b555 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -29,6 +29,8 @@ class ApacheHTTPDImporter(Importer): base_url = "https://httpd.apache.org/security/json/" + # For now, don't use the GH API + def set_api(self): self.version_api = GitHubTagsAPI() asyncio.run(self.version_api.load_api(["apache/httpd"])) @@ -41,6 +43,7 @@ def set_api(self): def updated_advisories(self): links = fetch_links(self.base_url) + # For now, don't use the GH API self.set_api() advisories = [] for link in links: diff --git a/vulnerabilities/tests/conftest.py b/vulnerabilities/tests/conftest.py index 472693f7a..ed6c8bb4b 100644 --- a/vulnerabilities/tests/conftest.py +++ b/vulnerabilities/tests/conftest.py @@ -25,7 +25,7 @@ def no_rmtree(monkeypatch): # Step 2: Run test for importer only if it is activated (pytestmark = pytest.mark.skipif(...)) # Step 3: Migrate all the tests collect_ignore = [ - "test_apache_httpd.py", + # "test_apache_httpd.py", "test_apache_kafka.py", "test_apache_tomcat.py", "test_api.py", diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index e206a88a2..1d0e1ab18 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -110,3 +110,7 @@ def test_to_advisory(self): found_advisories = list(map(AdvisoryData.normalized, found_advisories)) expected_advisories = list(map(AdvisoryData.normalized, expected_advisories)) assert sorted(found_advisories) == sorted(expected_advisories) + + def test_misc_01(self): + print("\nHello!\n") + assert True == True From cea5879af2bb3d65d5cea4599694986331dda1fa Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Mon, 21 Nov 2022 12:54:16 -0800 Subject: [PATCH 02/14] Update testing and importer #971 Reference: https://github.com/nexB/vulnerablecode/issues/971 Signed-off-by: John M. Horan --- vulnerabilities/importers/apache_httpd.py | 161 +++++++++----- vulnerabilities/tests/test_apache_httpd.py | 234 +++++++++++++-------- 2 files changed, 254 insertions(+), 141 deletions(-) diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index 95ec8b555..096c631f8 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -17,12 +17,14 @@ from univers.versions import SemverVersion from vulnerabilities.importer import AdvisoryData +from vulnerabilities.importer import AffectedPackage from vulnerabilities.importer import Importer from vulnerabilities.importer import Reference from vulnerabilities.importer import VulnerabilitySeverity from vulnerabilities.package_managers import GitHubTagsAPI from vulnerabilities.severity_systems import APACHE_HTTPD -from vulnerabilities.utils import nearest_patched_package + +# from vulnerabilities.utils import nearest_patched_package class ApacheHTTPDImporter(Importer): @@ -30,21 +32,20 @@ class ApacheHTTPDImporter(Importer): base_url = "https://httpd.apache.org/security/json/" # For now, don't use the GH API - - def set_api(self): - self.version_api = GitHubTagsAPI() - asyncio.run(self.version_api.load_api(["apache/httpd"])) - self.version_api.cache["apache/httpd"] = set( - filter( - lambda version: version.value not in ignore_tags, - self.version_api.cache["apache/httpd"], - ) - ) + # def set_api(self): + # self.version_api = GitHubTagsAPI() + # asyncio.run(self.version_api.load_api(["apache/httpd"])) + # self.version_api.cache["apache/httpd"] = set( + # filter( + # lambda version: version.value not in ignore_tags, + # self.version_api.cache["apache/httpd"], + # ) + # ) def updated_advisories(self): links = fetch_links(self.base_url) # For now, don't use the GH API - self.set_api() + # self.set_api() advisories = [] for link in links: data = requests.get(link).json() @@ -52,7 +53,8 @@ def updated_advisories(self): return self.batch_advisories(advisories) def to_advisory(self, data): - cve = data["CVE_data_meta"]["ID"] + # cve = data["CVE_data_meta"]["ID"] + alias = data["CVE_data_meta"]["ID"] descriptions = data["description"]["description_data"] description = None for desc in descriptions: @@ -73,67 +75,118 @@ def to_advisory(self, data): ) break reference = Reference( - reference_id=cve, - url=urllib.parse.urljoin(self.base_url, f"{cve}.json"), + # reference_id=cve, + reference_id=alias, + # url=urllib.parse.urljoin(self.base_url, f"{cve}.json"), + url=urllib.parse.urljoin(self.base_url, f"{alias}.json"), severities=severities, ) + # 2022-11-17 Thursday 19:02:16. This redraft of mine looks wrong and unnecessary -- current approach looks like what we want, since sampling suggests there are no real references in the JSON data and that there's always one value in ["impact"]["other"] + # reference_list = [] + # # reference_data = data["references"] + # # if data["references"]["reference_data"]: + # if "reference_data" in data.get("references", {}): + # reference = Reference( + # reference_id=data["references"]["reference_data"][0]["refsource"], + # url=data["references"]["reference_data"][0]["refsource"], + # severities=severities, + # ) + # else: + # reference = Reference( + # reference_id="", + # url="", + # severities=severities, + # ) + versions_data = [] for vendor in data["affects"]["vendor"]["vendor_data"]: for products in vendor["product"]["product_data"]: for version_data in products["version"]["version_data"]: versions_data.append(version_data) - fixed_version_ranges, affected_version_ranges = self.to_version_ranges(versions_data) + print("\n\n==> versions_data = {}\n".format(versions_data)) + for version in versions_data: + print("\n\tversion = {}\n".format(version)) + import json + + # print(json.dumps(version, indent=2)) + print("\n\tversion = \n{}\n".format(json.dumps(version, indent=2))) + + # fixed_version_ranges, affected_version_ranges = self.to_version_ranges(versions_data) + + fixed_version = [] + + for entry in data["timeline"]: + value = entry["value"] + # if "released" in entry["value"]: + if "released" in value: + # fixed_version.append(entry["value"]) + fixed_version.append(value.split(" ")[0]) affected_packages = [] - fixed_packages = [] - - for version_range in fixed_version_ranges: - fixed_packages.extend( - [ - PackageURL(type="apache", name="httpd", version=version) - for version in self.version_api.get("apache/httpd").valid_versions - if SemverVersion(version) in version_range - ] - ) + # fixed_packages = [] - for version_range in affected_version_ranges: - affected_packages.extend( - [ - PackageURL(type="apache", name="httpd", version=version) - for version in self.version_api.get("apache/httpd").valid_versions - if SemverVersion(version) in version_range - ] + for version in versions_data: + affected_package = AffectedPackage( + package=PackageURL( + type="generic", + name="apache_httpd", + ), + # affected_version_range=affected_version_range, + affected_version_range=version.get("version_value", "ERROR!!"), + fixed_version=fixed_version[0], + # fixed_version="to come", ) + affected_packages.append(affected_package) + + # for version_range in fixed_version_ranges: + # fixed_packages.extend( + # [ + # PackageURL(type="apache", name="httpd", version=version) + # for version in self.version_api.get("apache/httpd").valid_versions + # if SemverVersion(version) in version_range + # ] + # ) + + # for version_range in affected_version_ranges: + # affected_packages.extend( + # [ + # PackageURL(type="apache", name="httpd", version=version) + # for version in self.version_api.get("apache/httpd").valid_versions + # if SemverVersion(version) in version_range + # ] + # ) return AdvisoryData( - vulnerability_id=cve, + # vulnerability_id=cve, + aliases=[alias], summary=description, - affected_packages=nearest_patched_package(affected_packages, fixed_packages), + # affected_packages=nearest_patched_package(affected_packages, fixed_packages), + affected_packages=affected_packages, references=[reference], ) - def to_version_ranges(self, versions_data): - fixed_version_ranges = [] - affected_version_ranges = [] - for version_data in versions_data: - version_value = version_data["version_value"] - range_expression = version_data["version_affected"] - if range_expression == "<": - fixed_version_ranges.append( - VersionRange.from_scheme_version_spec_string( - "semver", ">={}".format(version_value) - ) - ) - elif range_expression == "=" or range_expression == "?=": - affected_version_ranges.append( - VersionRange.from_scheme_version_spec_string( - "semver", "{}".format(version_value) - ) - ) + # def to_version_ranges(self, versions_data): + # fixed_version_ranges = [] + # affected_version_ranges = [] + # for version_data in versions_data: + # version_value = version_data["version_value"] + # range_expression = version_data["version_affected"] + # if range_expression == "<": + # fixed_version_ranges.append( + # VersionRange.from_scheme_version_spec_string( + # "semver", ">={}".format(version_value) + # ) + # ) + # elif range_expression == "=" or range_expression == "?=": + # affected_version_ranges.append( + # VersionRange.from_scheme_version_spec_string( + # "semver", "{}".format(version_value) + # ) + # ) - return (fixed_version_ranges, affected_version_ranges) + # return (fixed_version_ranges, affected_version_ranges) def fetch_links(url): diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index 1d0e1ab18..97c4b7d65 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -19,98 +19,158 @@ from vulnerabilities.importer import Reference from vulnerabilities.importer import VulnerabilitySeverity from vulnerabilities.importers.apache_httpd import ApacheHTTPDImporter + +# from vulnerabilities.importers.apache_httpd import to_advisory from vulnerabilities.package_managers import GitHubTagsAPI from vulnerabilities.package_managers import PackageVersion +from vulnerabilities.tests import util_tests from vulnerabilities.utils import AffectedPackage BASE_DIR = os.path.dirname(os.path.abspath(__file__)) -TEST_DATA = os.path.join(BASE_DIR, "test_data", "apache_httpd", "CVE-1999-1199.json") +# TEST_DATA = os.path.join(BASE_DIR, "test_data", "apache_httpd", "CVE-1999-1199.json") +TEST_DATA = os.path.join(BASE_DIR, "test_data/apache_httpd") + + +# class TestApacheHTTPDImporter(TestCase): +# @classmethod +# def setUpClass(cls): +# data_source_cfg = {"etags": {}} +# cls.data_src = ApacheHTTPDImporter(1, config=data_source_cfg) +# known_versions = [PackageVersion("1.3.2"), PackageVersion("1.3.1"), PackageVersion("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) + +# def test_to_version_ranges(self): +# data = [ +# { +# "version_affected": "?=", +# "version_value": "1.3.0", +# }, +# { +# "version_affected": "=", +# "version_value": "1.3.1", +# }, +# { +# "version_affected": "<", +# "version_value": "1.3.2", +# }, +# ] +# fixed_version_ranges, affected_version_ranges = self.data_src.to_version_ranges(data) + +# # Check fixed packages +# assert [ +# VersionRange.from_scheme_version_spec_string("semver", ">=1.3.2") +# ] == fixed_version_ranges + +# # Check vulnerable packages +# assert [ +# VersionRange.from_scheme_version_spec_string("semver", "==1.3.0"), +# VersionRange.from_scheme_version_spec_string("semver", "==1.3.1"), +# ] == affected_version_ranges + +# def test_to_advisory(self): +# expected_advisories = [ +# AdvisoryData( +# summary="A serious problem exists when a client sends a large number of " +# "headers with the same header name. Apache uses up memory faster than the " +# "amount of memory required to simply store the received data itself. That " +# "is, memory use increases faster and faster as more headers are received, " +# "rather than increasing at a constant rate. This makes a denial of service " +# "attack based on this method more effective than methods which cause Apache" +# " to use memory at a constant rate, since the attacker has to send less data.", +# affected_packages=[ +# AffectedPackage( +# vulnerable_package=PackageURL( +# type="apache", +# name="httpd", +# version="1.3.0", +# ), +# ), +# AffectedPackage( +# vulnerable_package=PackageURL( +# type="apache", +# name="httpd", +# version="1.3.1", +# ), +# ), +# ], +# references=[ +# Reference( +# url="https://httpd.apache.org/security/json/CVE-1999-1199.json", +# severities=[ +# VulnerabilitySeverity( +# system=severity_systems.APACHE_HTTPD, +# value="important", +# ), +# ], +# reference_id="CVE-1999-1199", +# ), +# ], +# vulnerability_id="CVE-1999-1199", +# ) +# ] +# found_advisories = [self.data_src.to_advisory(self.data)] +# found_advisories = list(map(AdvisoryData.normalized, found_advisories)) +# expected_advisories = list(map(AdvisoryData.normalized, expected_advisories)) +# assert sorted(found_advisories) == sorted(expected_advisories) + +# def test_misc_01(self): +# print("\nHello!\n") +# assert True == True + + +# def test_misc_01(): +# print("\nHello!\n") +# assert True == True + + +# def test_to_advisory(): +# with open(os.path.join(TEST_DATA, "CVE-1999-1199.json")) as f: +# raw_data = json.load(f) + +# print("\n\nraw_data = \n{}\n".format(raw_data)) + +# # print("\npretty raw_data = {}".format(json.dumps(raw_data, indent=4))) + +# # The following throws an error: TypeError: to_advisory() missing 1 required positional argument: 'data' +# # presumably because it also needs to pass 'self'? +# advisories = ApacheHTTPDImporter.to_advisory(raw_data) +# # result = [data.to_dict() for data in advisories] +# # expected_file = os.path.join(TEST_DATA, f"parse-advisory-postgresql-expected.json") +# # util_tests.check_results_against_json(result, expected_file) class TestApacheHTTPDImporter(TestCase): - @classmethod - def setUpClass(cls): - data_source_cfg = {"etags": {}} - cls.data_src = ApacheHTTPDImporter(1, config=data_source_cfg) - known_versions = [PackageVersion("1.3.2"), PackageVersion("1.3.1"), PackageVersion("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) - - def test_to_version_ranges(self): - data = [ - { - "version_affected": "?=", - "version_value": "1.3.0", - }, - { - "version_affected": "=", - "version_value": "1.3.1", - }, - { - "version_affected": "<", - "version_value": "1.3.2", - }, - ] - fixed_version_ranges, affected_version_ranges = self.data_src.to_version_ranges(data) - - # Check fixed packages - assert [ - VersionRange.from_scheme_version_spec_string("semver", ">=1.3.2") - ] == fixed_version_ranges - - # Check vulnerable packages - assert [ - VersionRange.from_scheme_version_spec_string("semver", "==1.3.0"), - VersionRange.from_scheme_version_spec_string("semver", "==1.3.1"), - ] == affected_version_ranges - - def test_to_advisory(self): - expected_advisories = [ - AdvisoryData( - summary="A serious problem exists when a client sends a large number of " - "headers with the same header name. Apache uses up memory faster than the " - "amount of memory required to simply store the received data itself. That " - "is, memory use increases faster and faster as more headers are received, " - "rather than increasing at a constant rate. This makes a denial of service " - "attack based on this method more effective than methods which cause Apache" - " to use memory at a constant rate, since the attacker has to send less data.", - affected_packages=[ - AffectedPackage( - vulnerable_package=PackageURL( - type="apache", - name="httpd", - version="1.3.0", - ), - ), - AffectedPackage( - vulnerable_package=PackageURL( - type="apache", - name="httpd", - version="1.3.1", - ), - ), - ], - references=[ - Reference( - url="https://httpd.apache.org/security/json/CVE-1999-1199.json", - severities=[ - VulnerabilitySeverity( - system=severity_systems.APACHE_HTTPD, - value="important", - ), - ], - reference_id="CVE-1999-1199", - ), - ], - vulnerability_id="CVE-1999-1199", - ) - ] - found_advisories = [self.data_src.to_advisory(self.data)] - found_advisories = list(map(AdvisoryData.normalized, found_advisories)) - expected_advisories = list(map(AdvisoryData.normalized, expected_advisories)) - assert sorted(found_advisories) == sorted(expected_advisories) - - def test_misc_01(self): - print("\nHello!\n") - assert True == True + base_url = "https://httpd.apache.org/security/json/" + + def test_to_advisory_in_class(self): + # print("\nHello!\n") + with open(os.path.join(TEST_DATA, "CVE-1999-1199.json")) as f: + raw_data = json.load(f) + + # print("\n\nraw_data = \n{}\n".format(raw_data)) + # print("\npretty raw_data = {}".format(json.dumps(raw_data, indent=2))) + + advisory = ApacheHTTPDImporter.to_advisory(self, raw_data) + + print("\n\nadvisory = \n{}\n".format(advisory)) + + print("advisory.aliases = {}\n".format(advisory.aliases)) + + print("advisory.summary = {}\n".format(advisory.summary)) + + print("advisory.affected_packages = {}\n".format(advisory.affected_packages)) + + print("advisory.references = {}\n".format(advisory.references)) + for ref in advisory.references: + print("\treference = {}\n".format(ref)) + + print("advisory.date_published = {}\n".format(advisory.date_published)) + + # result = [data.to_dict() for data in advisories] + result = advisory.to_dict() + + print("result = {}\n".format(result)) + + print("\npretty result = \n{}".format(json.dumps(result, indent=2))) From 749cc36969f4c61bacea82b26134268b3668108c Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Tue, 22 Nov 2022 11:20:22 -0800 Subject: [PATCH 03/14] Update importer and test files #971 Signed-off-by: John M. Horan --- vulnerabilities/importers/apache_httpd.py | 86 +++++++------- vulnerabilities/tests/test_apache_httpd.py | 29 ++--- .../apache_httpd/CVE-2021-44224.json | 106 ++++++++++++++++++ 3 files changed, 165 insertions(+), 56 deletions(-) create mode 100644 vulnerabilities/tests/test_data/apache_httpd/CVE-2021-44224.json diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index 096c631f8..e4555872e 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -8,8 +8,10 @@ # import asyncio +import datetime import urllib +import dateparser import requests from bs4 import BeautifulSoup from packageurl import PackageURL @@ -71,6 +73,7 @@ def to_advisory(self, data): VulnerabilitySeverity( system=APACHE_HTTPD, value=value, + scoring_elements="", ) ) break @@ -82,40 +85,23 @@ def to_advisory(self, data): severities=severities, ) - # 2022-11-17 Thursday 19:02:16. This redraft of mine looks wrong and unnecessary -- current approach looks like what we want, since sampling suggests there are no real references in the JSON data and that there's always one value in ["impact"]["other"] - # reference_list = [] - # # reference_data = data["references"] - # # if data["references"]["reference_data"]: - # if "reference_data" in data.get("references", {}): - # reference = Reference( - # reference_id=data["references"]["reference_data"][0]["refsource"], - # url=data["references"]["reference_data"][0]["refsource"], - # severities=severities, - # ) - # else: - # reference = Reference( - # reference_id="", - # url="", - # severities=severities, - # ) - versions_data = [] for vendor in data["affects"]["vendor"]["vendor_data"]: for products in vendor["product"]["product_data"]: for version_data in products["version"]["version_data"]: versions_data.append(version_data) - print("\n\n==> versions_data = {}\n".format(versions_data)) + # print("\n\n==> versions_data = {}\n".format(versions_data)) for version in versions_data: - print("\n\tversion = {}\n".format(version)) + # print("\n\tversion = {}\n".format(version)) import json - # print(json.dumps(version, indent=2)) - print("\n\tversion = \n{}\n".format(json.dumps(version, indent=2))) + # print("\n\tversion = \n{}\n".format(json.dumps(version, indent=2))) - # fixed_version_ranges, affected_version_ranges = self.to_version_ranges(versions_data) + fixed_version_ranges, affected_version_ranges = self.to_version_ranges(versions_data) fixed_version = [] + date_published = "" for entry in data["timeline"]: value = entry["value"] @@ -123,6 +109,7 @@ def to_advisory(self, data): if "released" in value: # fixed_version.append(entry["value"]) fixed_version.append(value.split(" ")[0]) + date_published = get_published_date(entry["time"]) affected_packages = [] # fixed_packages = [] @@ -165,28 +152,29 @@ def to_advisory(self, data): # affected_packages=nearest_patched_package(affected_packages, fixed_packages), affected_packages=affected_packages, references=[reference], + date_published=date_published, ) - # def to_version_ranges(self, versions_data): - # fixed_version_ranges = [] - # affected_version_ranges = [] - # for version_data in versions_data: - # version_value = version_data["version_value"] - # range_expression = version_data["version_affected"] - # if range_expression == "<": - # fixed_version_ranges.append( - # VersionRange.from_scheme_version_spec_string( - # "semver", ">={}".format(version_value) - # ) - # ) - # elif range_expression == "=" or range_expression == "?=": - # affected_version_ranges.append( - # VersionRange.from_scheme_version_spec_string( - # "semver", "{}".format(version_value) - # ) - # ) - - # return (fixed_version_ranges, affected_version_ranges) + def to_version_ranges(self, versions_data): + fixed_version_ranges = [] + affected_version_ranges = [] + for version_data in versions_data: + version_value = version_data["version_value"] + range_expression = version_data["version_affected"] + if range_expression == "<": + fixed_version_ranges.append( + VersionRange.from_scheme_version_spec_string( + "semver", ">={}".format(version_value) + ) + ) + elif range_expression == "=" or range_expression == "?=": + affected_version_ranges.append( + VersionRange.from_scheme_version_spec_string( + "semver", "{}".format(version_value) + ) + ) + + return (fixed_version_ranges, affected_version_ranges) def fetch_links(url): @@ -201,6 +189,20 @@ def fetch_links(url): return links +# From osv.py +# def get_published_date(raw_data): +# published = raw_data.get("published") +# return published and dateparser.parse(date_string=published) + + +def get_published_date(published): + # return published and dateparser.parse(date_string=published) + # above gives result like this: "date_published": "2021-12-20T00:00:00" + # so does this: + published = datetime.datetime.strptime(published, "%Y-%m-%d") + return published + + ignore_tags = { "AGB_BEFORE_AAA_CHANGES", "APACHE_1_2b1", diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index 97c4b7d65..60362c224 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -145,32 +145,33 @@ class TestApacheHTTPDImporter(TestCase): base_url = "https://httpd.apache.org/security/json/" def test_to_advisory_in_class(self): - # print("\nHello!\n") - with open(os.path.join(TEST_DATA, "CVE-1999-1199.json")) as f: + # with open(os.path.join(TEST_DATA, "CVE-1999-1199.json")) as f: + with open(os.path.join(TEST_DATA, "CVE-2021-44224.json")) as f: raw_data = json.load(f) # print("\n\nraw_data = \n{}\n".format(raw_data)) - # print("\npretty raw_data = {}".format(json.dumps(raw_data, indent=2))) + print( + "\n\nJSON input file CVE-1999-1199.json = \n\n{}".format(json.dumps(raw_data, indent=2)) + ) advisory = ApacheHTTPDImporter.to_advisory(self, raw_data) - print("\n\nadvisory = \n{}\n".format(advisory)) + print("\n\nJSON input file to_advisory() = \n\n{}\n".format(advisory)) - print("advisory.aliases = {}\n".format(advisory.aliases)) + # print("advisory.aliases = {}\n".format(advisory.aliases)) - print("advisory.summary = {}\n".format(advisory.summary)) + # print("advisory.summary = {}\n".format(advisory.summary)) - print("advisory.affected_packages = {}\n".format(advisory.affected_packages)) + # print("advisory.affected_packages = {}\n".format(advisory.affected_packages)) - print("advisory.references = {}\n".format(advisory.references)) - for ref in advisory.references: - print("\treference = {}\n".format(ref)) + # print("advisory.references = {}\n".format(advisory.references)) + # for ref in advisory.references: + # print("\treference = {}\n".format(ref)) - print("advisory.date_published = {}\n".format(advisory.date_published)) + # print("advisory.date_published = {}\n".format(advisory.date_published)) - # result = [data.to_dict() for data in advisories] result = advisory.to_dict() - print("result = {}\n".format(result)) + # print("result = {}\n".format(result)) - print("\npretty result = \n{}".format(json.dumps(result, indent=2))) + print("\nadvisory.to_dict() = \n\n{}\n".format(json.dumps(result, indent=2))) diff --git a/vulnerabilities/tests/test_data/apache_httpd/CVE-2021-44224.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-2021-44224.json new file mode 100644 index 000000000..456732fa7 --- /dev/null +++ b/vulnerabilities/tests/test_data/apache_httpd/CVE-2021-44224.json @@ -0,0 +1,106 @@ +{ + "CVE_data_meta": { + "ASSIGNER": "security@apache.org", + "ID": "CVE-2021-44224", + "STATE": "REVIEW", + "TITLE": "Possible NULL dereference or SSRF in forward proxy configurations in Apache HTTP Server 2.4.51 and earlier" + }, + "affects": { + "vendor": { + "vendor_data": [ + { + "product": { + "product_data": [ + { + "product_name": "Apache HTTP Server", + "version": { + "version_data": [ + { + "version_affected": ">=", + "version_name": "Apache HTTP Server 2.4", + "version_value": "2.4.7" + }, + { + "version_affected": "<=", + "version_name": "Apache HTTP Server 2.4", + "version_value": "2.4.51" + } + ] + } + } + ] + }, + "vendor_name": "Apache Software Foundation" + } + ] + } + }, + "credit": [ + { + "lang": "eng", + "value": "漂亮鼠" + }, + { + "lang": "eng", + "value": "TengMA(@Te3t123)" + } + ], + "data_format": "MITRE", + "data_type": "CVE", + "data_version": "4.0", + "description": { + "description_data": [ + { + "lang": "eng", + "value": "A crafted URI sent to httpd configured as a forward proxy (ProxyRequests on) can cause a crash (NULL pointer dereference) or, for configurations mixing forward and reverse proxy declarations, can allow for requests to be directed to a declared Unix Domain Socket endpoint (Server Side Request Forgery).\n\nThis issue affects Apache HTTP Server 2.4.7 up to 2.4.51 (included)." + } + ] + }, + "generator": { + "engine": "Vulnogram 0.0.9" + }, + "impact": [ + { + "other": "moderate" + } + ], + "problemtype": { + "problemtype_data": [ + { + "description": [ + { + "lang": "eng", + "value": "CWE-476 NULL Pointer Dereference" + } + ] + } + ] + }, + "references": { + "reference_data": [ + { + "refsource": "CONFIRM" + } + ] + }, + "source": { + "discovery": "UNKNOWN" + }, + "timeline": [ + { + "lang": "eng", + "time": "2021-11-18", + "value": "Reported to security team" + }, + { + "lang": "eng", + "time": "2021-12-14", + "value": "fixed by r1895955, r1896044 in 2.4.x" + }, + { + "lang": "eng", + "time": "2021-12-20", + "value": "2.4.52 released" + } + ] +} From 663598cfd147045cce2a3def0a15b2d2725ba8d0 Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Wed, 23 Nov 2022 01:53:23 +0530 Subject: [PATCH 04/14] Add affected package in apache_httpd importer Signed-off-by: Tushar Goel --- vulnerabilities/importers/apache_httpd.py | 110 +++++++++++++--------- 1 file changed, 64 insertions(+), 46 deletions(-) diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index e4555872e..f7702cd1c 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -15,8 +15,9 @@ import requests from bs4 import BeautifulSoup from packageurl import PackageURL -from univers.version_range import VersionRange +from univers.version_range import GenericVersionRange from univers.versions import SemverVersion +from univers.version_constraint import VersionConstraint from vulnerabilities.importer import AdvisoryData from vulnerabilities.importer import AffectedPackage @@ -32,6 +33,7 @@ class ApacheHTTPDImporter(Importer): base_url = "https://httpd.apache.org/security/json/" + spdx_license_expression = "Apache-2.0" # For now, don't use the GH API # def set_api(self): @@ -44,15 +46,13 @@ class ApacheHTTPDImporter(Importer): # ) # ) - def updated_advisories(self): + def advisory_data(self): links = fetch_links(self.base_url) # For now, don't use the GH API # self.set_api() - advisories = [] for link in links: data = requests.get(link).json() - advisories.append(self.to_advisory(data)) - return self.batch_advisories(advisories) + yield self.to_advisory(data) def to_advisory(self, data): # cve = data["CVE_data_meta"]["ID"] @@ -98,34 +98,34 @@ def to_advisory(self, data): # print("\n\tversion = \n{}\n".format(json.dumps(version, indent=2))) - fixed_version_ranges, affected_version_ranges = self.to_version_ranges(versions_data) + affected_version_range = self.to_version_ranges(versions_data) - fixed_version = [] - date_published = "" + # fixed_version = [] + # date_published = "" - for entry in data["timeline"]: - value = entry["value"] - # if "released" in entry["value"]: - if "released" in value: - # fixed_version.append(entry["value"]) - fixed_version.append(value.split(" ")[0]) - date_published = get_published_date(entry["time"]) + # for entry in data["timeline"]: + # value = entry["value"] + # # if "released" in entry["value"]: + # if "released" in value: + # # fixed_version.append(entry["value"]) + # fixed_version.append(value.split(" ")[0]) + # date_published = get_published_date(entry["time"]) - affected_packages = [] + # affected_packages = [] # fixed_packages = [] - for version in versions_data: - affected_package = AffectedPackage( - package=PackageURL( - type="generic", - name="apache_httpd", - ), - # affected_version_range=affected_version_range, - affected_version_range=version.get("version_value", "ERROR!!"), - fixed_version=fixed_version[0], - # fixed_version="to come", - ) - affected_packages.append(affected_package) + # for version in versions_data: + # affected_package = AffectedPackage( + # package=PackageURL( + # type="generic", + # name="apache_httpd", + # ), + # # affected_version_range=affected_version_range, + # affected_version_range=version.get("version_value", "ERROR!!"), + # fixed_version=fixed_version[0], + # # fixed_version="to come", + # ) + # affected_packages.append(affected_package) # for version_range in fixed_version_ranges: # fixed_packages.extend( @@ -150,31 +150,49 @@ def to_advisory(self, data): aliases=[alias], summary=description, # affected_packages=nearest_patched_package(affected_packages, fixed_packages), - affected_packages=affected_packages, + # affected_packages=AffectedPackage( + # package=PackageURL( + # type="apache", + # name="httpd", + # ), + # affected_version_range=affected_version_range + # ) + affected_packages=[ + AffectedPackage( + package=PackageURL( + type="generic", + name="apache_httpd", + ), + affected_version_range=affected_version_range, + ) + ], references=[reference], - date_published=date_published, ) def to_version_ranges(self, versions_data): - fixed_version_ranges = [] - affected_version_ranges = [] + constraints = [] for version_data in versions_data: version_value = version_data["version_value"] range_expression = version_data["version_affected"] - if range_expression == "<": - fixed_version_ranges.append( - VersionRange.from_scheme_version_spec_string( - "semver", ">={}".format(version_value) - ) - ) - elif range_expression == "=" or range_expression == "?=": - affected_version_ranges.append( - VersionRange.from_scheme_version_spec_string( - "semver", "{}".format(version_value) - ) - ) - - return (fixed_version_ranges, affected_version_ranges) + if range_expression == ">=" or range_expression == "!<": + constraints.append(VersionConstraint( + comparator=">=", + version=SemverVersion(version_value), + )) + + if range_expression == "<=": + constraints.append(VersionConstraint( + comparator="<=", + version=SemverVersion(version_value), + )) + + if range_expression == "=" or range_expression == "?=": + constraints.append(VersionConstraint( + comparator="=", + version=SemverVersion(version_value), + )) + + return GenericVersionRange(constraints=constraints) def fetch_links(url): From e6649564b876d9a0e0002a96a693febf68b3a22e Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Mon, 28 Nov 2022 16:18:43 -0800 Subject: [PATCH 05/14] Update and clean importer, replace existing tests with 3 new tests #971 Reference: https://github.com/nexB/vulnerablecode/issues/971 Signed-off-by: John M. Horan --- vulnerabilities/importers/apache_httpd.py | 116 ++--------- vulnerabilities/tests/conftest.py | 2 +- vulnerabilities/tests/test_apache_httpd.py | 194 +++++------------- ...y-CVE-1999-1199-apache-httpd-expected.json | 34 +++ ...-CVE-2021-44224-apache-httpd-expected.json | 34 +++ 5 files changed, 136 insertions(+), 244 deletions(-) create mode 100644 vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-1999-1199-apache-httpd-expected.json create mode 100644 vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2021-44224-apache-httpd-expected.json diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index f7702cd1c..adfbd39cc 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -8,16 +8,14 @@ # import asyncio -import datetime import urllib -import dateparser import requests from bs4 import BeautifulSoup from packageurl import PackageURL +from univers.version_constraint import VersionConstraint from univers.version_range import GenericVersionRange from univers.versions import SemverVersion -from univers.version_constraint import VersionConstraint from vulnerabilities.importer import AdvisoryData from vulnerabilities.importer import AffectedPackage @@ -27,13 +25,12 @@ from vulnerabilities.package_managers import GitHubTagsAPI from vulnerabilities.severity_systems import APACHE_HTTPD -# from vulnerabilities.utils import nearest_patched_package - class ApacheHTTPDImporter(Importer): base_url = "https://httpd.apache.org/security/json/" spdx_license_expression = "Apache-2.0" + license_url = "https://www.apache.org/licenses/" # For now, don't use the GH API # def set_api(self): @@ -55,7 +52,6 @@ def advisory_data(self): yield self.to_advisory(data) def to_advisory(self, data): - # cve = data["CVE_data_meta"]["ID"] alias = data["CVE_data_meta"]["ID"] descriptions = data["description"]["description_data"] description = None @@ -78,9 +74,7 @@ def to_advisory(self, data): ) break reference = Reference( - # reference_id=cve, reference_id=alias, - # url=urllib.parse.urljoin(self.base_url, f"{cve}.json"), url=urllib.parse.urljoin(self.base_url, f"{alias}.json"), severities=severities, ) @@ -91,72 +85,11 @@ def to_advisory(self, data): for version_data in products["version"]["version_data"]: versions_data.append(version_data) - # print("\n\n==> versions_data = {}\n".format(versions_data)) - for version in versions_data: - # print("\n\tversion = {}\n".format(version)) - import json - - # print("\n\tversion = \n{}\n".format(json.dumps(version, indent=2))) - affected_version_range = self.to_version_ranges(versions_data) - # fixed_version = [] - # date_published = "" - - # for entry in data["timeline"]: - # value = entry["value"] - # # if "released" in entry["value"]: - # if "released" in value: - # # fixed_version.append(entry["value"]) - # fixed_version.append(value.split(" ")[0]) - # date_published = get_published_date(entry["time"]) - - # affected_packages = [] - # fixed_packages = [] - - # for version in versions_data: - # affected_package = AffectedPackage( - # package=PackageURL( - # type="generic", - # name="apache_httpd", - # ), - # # affected_version_range=affected_version_range, - # affected_version_range=version.get("version_value", "ERROR!!"), - # fixed_version=fixed_version[0], - # # fixed_version="to come", - # ) - # affected_packages.append(affected_package) - - # for version_range in fixed_version_ranges: - # fixed_packages.extend( - # [ - # PackageURL(type="apache", name="httpd", version=version) - # for version in self.version_api.get("apache/httpd").valid_versions - # if SemverVersion(version) in version_range - # ] - # ) - - # for version_range in affected_version_ranges: - # affected_packages.extend( - # [ - # PackageURL(type="apache", name="httpd", version=version) - # for version in self.version_api.get("apache/httpd").valid_versions - # if SemverVersion(version) in version_range - # ] - # ) - return AdvisoryData( - # vulnerability_id=cve, aliases=[alias], summary=description, - # affected_packages=nearest_patched_package(affected_packages, fixed_packages), - # affected_packages=AffectedPackage( - # package=PackageURL( - # type="apache", - # name="httpd", - # ), - # affected_version_range=affected_version_range - # ) affected_packages=[ AffectedPackage( package=PackageURL( @@ -174,23 +107,30 @@ def to_version_ranges(self, versions_data): for version_data in versions_data: version_value = version_data["version_value"] range_expression = version_data["version_affected"] + if range_expression == ">=" or range_expression == "!<": - constraints.append(VersionConstraint( - comparator=">=", - version=SemverVersion(version_value), - )) + constraints.append( + VersionConstraint( + comparator=">=", + version=SemverVersion(version_value), + ) + ) if range_expression == "<=": - constraints.append(VersionConstraint( - comparator="<=", - version=SemverVersion(version_value), - )) + constraints.append( + VersionConstraint( + comparator="<=", + version=SemverVersion(version_value), + ) + ) if range_expression == "=" or range_expression == "?=": - constraints.append(VersionConstraint( - comparator="=", - version=SemverVersion(version_value), - )) + constraints.append( + VersionConstraint( + comparator="=", + version=SemverVersion(version_value), + ) + ) return GenericVersionRange(constraints=constraints) @@ -207,20 +147,6 @@ def fetch_links(url): return links -# From osv.py -# def get_published_date(raw_data): -# published = raw_data.get("published") -# return published and dateparser.parse(date_string=published) - - -def get_published_date(published): - # return published and dateparser.parse(date_string=published) - # above gives result like this: "date_published": "2021-12-20T00:00:00" - # so does this: - published = datetime.datetime.strptime(published, "%Y-%m-%d") - return published - - ignore_tags = { "AGB_BEFORE_AAA_CHANGES", "APACHE_1_2b1", diff --git a/vulnerabilities/tests/conftest.py b/vulnerabilities/tests/conftest.py index ed6c8bb4b..472693f7a 100644 --- a/vulnerabilities/tests/conftest.py +++ b/vulnerabilities/tests/conftest.py @@ -25,7 +25,7 @@ def no_rmtree(monkeypatch): # Step 2: Run test for importer only if it is activated (pytestmark = pytest.mark.skipif(...)) # Step 3: Migrate all the tests collect_ignore = [ - # "test_apache_httpd.py", + "test_apache_httpd.py", "test_apache_kafka.py", "test_apache_tomcat.py", "test_api.py", diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index 60362c224..0ed8c763d 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -11,167 +11,65 @@ import os from unittest import TestCase -from packageurl import PackageURL -from univers.version_range import VersionRange +from univers.version_constraint import VersionConstraint +from univers.version_range import GenericVersionRange +from univers.versions import SemverVersion -from vulnerabilities import severity_systems -from vulnerabilities.importer import AdvisoryData -from vulnerabilities.importer import Reference -from vulnerabilities.importer import VulnerabilitySeverity from vulnerabilities.importers.apache_httpd import ApacheHTTPDImporter - -# from vulnerabilities.importers.apache_httpd import to_advisory from vulnerabilities.package_managers import GitHubTagsAPI -from vulnerabilities.package_managers import PackageVersion from vulnerabilities.tests import util_tests -from vulnerabilities.utils import AffectedPackage BASE_DIR = os.path.dirname(os.path.abspath(__file__)) -# TEST_DATA = os.path.join(BASE_DIR, "test_data", "apache_httpd", "CVE-1999-1199.json") TEST_DATA = os.path.join(BASE_DIR, "test_data/apache_httpd") -# class TestApacheHTTPDImporter(TestCase): -# @classmethod -# def setUpClass(cls): -# data_source_cfg = {"etags": {}} -# cls.data_src = ApacheHTTPDImporter(1, config=data_source_cfg) -# known_versions = [PackageVersion("1.3.2"), PackageVersion("1.3.1"), PackageVersion("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) - -# def test_to_version_ranges(self): -# data = [ -# { -# "version_affected": "?=", -# "version_value": "1.3.0", -# }, -# { -# "version_affected": "=", -# "version_value": "1.3.1", -# }, -# { -# "version_affected": "<", -# "version_value": "1.3.2", -# }, -# ] -# fixed_version_ranges, affected_version_ranges = self.data_src.to_version_ranges(data) - -# # Check fixed packages -# assert [ -# VersionRange.from_scheme_version_spec_string("semver", ">=1.3.2") -# ] == fixed_version_ranges - -# # Check vulnerable packages -# assert [ -# VersionRange.from_scheme_version_spec_string("semver", "==1.3.0"), -# VersionRange.from_scheme_version_spec_string("semver", "==1.3.1"), -# ] == affected_version_ranges - -# def test_to_advisory(self): -# expected_advisories = [ -# AdvisoryData( -# summary="A serious problem exists when a client sends a large number of " -# "headers with the same header name. Apache uses up memory faster than the " -# "amount of memory required to simply store the received data itself. That " -# "is, memory use increases faster and faster as more headers are received, " -# "rather than increasing at a constant rate. This makes a denial of service " -# "attack based on this method more effective than methods which cause Apache" -# " to use memory at a constant rate, since the attacker has to send less data.", -# affected_packages=[ -# AffectedPackage( -# vulnerable_package=PackageURL( -# type="apache", -# name="httpd", -# version="1.3.0", -# ), -# ), -# AffectedPackage( -# vulnerable_package=PackageURL( -# type="apache", -# name="httpd", -# version="1.3.1", -# ), -# ), -# ], -# references=[ -# Reference( -# url="https://httpd.apache.org/security/json/CVE-1999-1199.json", -# severities=[ -# VulnerabilitySeverity( -# system=severity_systems.APACHE_HTTPD, -# value="important", -# ), -# ], -# reference_id="CVE-1999-1199", -# ), -# ], -# vulnerability_id="CVE-1999-1199", -# ) -# ] -# found_advisories = [self.data_src.to_advisory(self.data)] -# found_advisories = list(map(AdvisoryData.normalized, found_advisories)) -# expected_advisories = list(map(AdvisoryData.normalized, expected_advisories)) -# assert sorted(found_advisories) == sorted(expected_advisories) - -# def test_misc_01(self): -# print("\nHello!\n") -# assert True == True - - -# def test_misc_01(): -# print("\nHello!\n") -# assert True == True - - -# def test_to_advisory(): -# with open(os.path.join(TEST_DATA, "CVE-1999-1199.json")) as f: -# raw_data = json.load(f) - -# print("\n\nraw_data = \n{}\n".format(raw_data)) - -# # print("\npretty raw_data = {}".format(json.dumps(raw_data, indent=4))) - -# # The following throws an error: TypeError: to_advisory() missing 1 required positional argument: 'data' -# # presumably because it also needs to pass 'self'? -# advisories = ApacheHTTPDImporter.to_advisory(raw_data) -# # result = [data.to_dict() for data in advisories] -# # expected_file = os.path.join(TEST_DATA, f"parse-advisory-postgresql-expected.json") -# # util_tests.check_results_against_json(result, expected_file) - - -class TestApacheHTTPDImporter(TestCase): - base_url = "https://httpd.apache.org/security/json/" - - def test_to_advisory_in_class(self): - # with open(os.path.join(TEST_DATA, "CVE-1999-1199.json")) as f: - with open(os.path.join(TEST_DATA, "CVE-2021-44224.json")) as f: - raw_data = json.load(f) - - # print("\n\nraw_data = \n{}\n".format(raw_data)) - print( - "\n\nJSON input file CVE-1999-1199.json = \n\n{}".format(json.dumps(raw_data, indent=2)) +def test_to_version_ranges(): + data = [ + { + "version_affected": "?=", + "version_value": "1.3.0", + }, + { + "version_affected": "=", + "version_value": "1.3.1", + }, + { + "version_affected": "<=", + "version_value": "2.3.4", + }, + ] + affected_version_range = ApacheHTTPDImporter().to_version_ranges(data) + + # Check vulnerable packages + assert ( + GenericVersionRange( + constraints=( + VersionConstraint(comparator="=", version=SemverVersion(string="1.3.0")), + VersionConstraint(comparator="=", version=SemverVersion(string="1.3.1")), + VersionConstraint(comparator="<=", version=SemverVersion(string="2.3.4")), + ) ) + == affected_version_range + ) - advisory = ApacheHTTPDImporter.to_advisory(self, raw_data) - - print("\n\nJSON input file to_advisory() = \n\n{}\n".format(advisory)) - - # print("advisory.aliases = {}\n".format(advisory.aliases)) - - # print("advisory.summary = {}\n".format(advisory.summary)) - - # print("advisory.affected_packages = {}\n".format(advisory.affected_packages)) - # print("advisory.references = {}\n".format(advisory.references)) - # for ref in advisory.references: - # print("\treference = {}\n".format(ref)) +def test_to_advisory_CVE_1999_1199(): + with open(os.path.join(TEST_DATA, "CVE-1999-1199.json")) as f: + data = json.load(f) - # print("advisory.date_published = {}\n".format(advisory.date_published)) + advisories = ApacheHTTPDImporter().to_advisory(data) + result = advisories.to_dict() + expected_file = os.path.join(TEST_DATA, f"to-advisory-CVE-1999-1199-apache-httpd-expected.json") + util_tests.check_results_against_json(result, expected_file) - result = advisory.to_dict() - # print("result = {}\n".format(result)) +def test_to_advisory_CVE_2021_44224(): + with open(os.path.join(TEST_DATA, "CVE-2021-44224.json")) as f: + data = json.load(f) - print("\nadvisory.to_dict() = \n\n{}\n".format(json.dumps(result, indent=2))) + advisories = ApacheHTTPDImporter().to_advisory(data) + result = advisories.to_dict() + expected_file = os.path.join( + TEST_DATA, f"to-advisory-CVE-2021-44224-apache-httpd-expected.json" + ) + util_tests.check_results_against_json(result, expected_file) diff --git a/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-1999-1199-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-1999-1199-apache-httpd-expected.json new file mode 100644 index 000000000..815b7a460 --- /dev/null +++ b/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-1999-1199-apache-httpd-expected.json @@ -0,0 +1,34 @@ +{ + "aliases": [ + "CVE-1999-1199" + ], + "summary": "A serious problem exists when a client sends a large number of headers with the same header name. Apache uses up memory faster than the amount of memory required to simply store the received data itself. That is, memory use increases faster and faster as more headers are received, rather than increasing at a constant rate. This makes a denial of service attack based on this method more effective than methods which cause Apache to use memory at a constant rate, since the attacker has to send less data.", + "affected_packages": [ + { + "package": { + "type": "generic", + "namespace": null, + "name": "apache_httpd", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:generic/1.3.0|1.3.1", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "CVE-1999-1199", + "url": "https://httpd.apache.org/security/json/CVE-1999-1199.json", + "severities": [ + { + "system": "apache_httpd", + "value": "important", + "scoring_elements": "" + } + ] + } + ], + "date_published": null +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2021-44224-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2021-44224-apache-httpd-expected.json new file mode 100644 index 000000000..5387b0716 --- /dev/null +++ b/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2021-44224-apache-httpd-expected.json @@ -0,0 +1,34 @@ +{ + "aliases": [ + "CVE-2021-44224" + ], + "summary": "A crafted URI sent to httpd configured as a forward proxy (ProxyRequests on) can cause a crash (NULL pointer dereference) or, for configurations mixing forward and reverse proxy declarations, can allow for requests to be directed to a declared Unix Domain Socket endpoint (Server Side Request Forgery).\n\nThis issue affects Apache HTTP Server 2.4.7 up to 2.4.51 (included).", + "affected_packages": [ + { + "package": { + "type": "generic", + "namespace": null, + "name": "apache_httpd", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:generic/>=2.4.7|<=2.4.51", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "CVE-2021-44224", + "url": "https://httpd.apache.org/security/json/CVE-2021-44224.json", + "severities": [ + { + "system": "apache_httpd", + "value": "moderate", + "scoring_elements": "" + } + ] + } + ], + "date_published": null +} \ No newline at end of file From 65189504553ec49df423531b1a34434733df9a2a Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Tue, 29 Nov 2022 13:49:51 -0800 Subject: [PATCH 06/14] Update code and test to address problematic comparator #971 Reference: https://github.com/nexB/vulnerablecode/issues/971 Signed-off-by: John M. Horan --- vulnerabilities/importers/apache_httpd.py | 24 +++++++++++++++------- vulnerabilities/tests/test_apache_httpd.py | 1 - 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index adfbd39cc..2ff604f4b 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -25,6 +25,8 @@ from vulnerabilities.package_managers import GitHubTagsAPI from vulnerabilities.severity_systems import APACHE_HTTPD +KNOWN_RANGE_EXPRESSIONS = {"<=", ">=", "?=", "!<", "="} + class ApacheHTTPDImporter(Importer): @@ -87,10 +89,9 @@ def to_advisory(self, data): affected_version_range = self.to_version_ranges(versions_data) - return AdvisoryData( - aliases=[alias], - summary=description, - affected_packages=[ + affected_packages = [] + if affected_version_range: + affected_packages.append( AffectedPackage( package=PackageURL( type="generic", @@ -98,7 +99,12 @@ def to_advisory(self, data): ), affected_version_range=affected_version_range, ) - ], + ) + + return AdvisoryData( + aliases=[alias], + summary=description, + affected_packages=affected_packages, references=[reference], ) @@ -107,7 +113,8 @@ def to_version_ranges(self, versions_data): for version_data in versions_data: version_value = version_data["version_value"] range_expression = version_data["version_affected"] - + if range_expression not in KNOWN_RANGE_EXPRESSIONS: + raise ValueError(f"unknown comparator found! {range_expression}") if range_expression == ">=" or range_expression == "!<": constraints.append( VersionConstraint( @@ -124,7 +131,10 @@ def to_version_ranges(self, versions_data): ) ) - if range_expression == "=" or range_expression == "?=": + if range_expression == "?=": + pass + + if range_expression == "=": constraints.append( VersionConstraint( comparator="=", diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index 0ed8c763d..fb6ac2778 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -44,7 +44,6 @@ def test_to_version_ranges(): assert ( GenericVersionRange( constraints=( - VersionConstraint(comparator="=", version=SemverVersion(string="1.3.0")), VersionConstraint(comparator="=", version=SemverVersion(string="1.3.1")), VersionConstraint(comparator="<=", version=SemverVersion(string="2.3.4")), ) From 1d1df82e0c9366bba390e542f1a0c7218c59f017 Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Thu, 1 Dec 2022 09:41:45 -0800 Subject: [PATCH 07/14] Update to use .invert() #971 Reference: https://github.com/nexB/vulnerablecode/issues/971 Signed-off-by: John M. Horan --- requirements.txt | 4 +- setup.cfg | 2 +- vulnerabilities/importers/apache_httpd.py | 48 ++++++++++++++++++- vulnerabilities/tests/conftest.py | 1 - vulnerabilities/tests/test_apache_httpd.py | 15 +++++- ...y-CVE-1999-1199-apache-httpd-expected.json | 2 +- ...-CVE-2021-44224-apache-httpd-expected.json | 2 +- 7 files changed, 65 insertions(+), 9 deletions(-) diff --git a/requirements.txt b/requirements.txt index add89c95a..2abee0b65 100644 --- a/requirements.txt +++ b/requirements.txt @@ -107,7 +107,7 @@ toml==0.10.2 tomli==2.0.1 traitlets==5.1.1 typing_extensions==4.1.1 -univers==30.9.0 +univers==30.9.1 urllib3==1.26.9 wcwidth==0.2.5 websocket-client==0.59.0 @@ -120,4 +120,4 @@ drf-spectacular-sidecar==2022.10.1 drf-spectacular==0.24.2 coreapi==2.3.3 coreschema==0.0.4 -itypes==1.2.0 \ No newline at end of file +itypes==1.2.0 diff --git a/setup.cfg b/setup.cfg index 73fb4ebbd..bd8626710 100644 --- a/setup.cfg +++ b/setup.cfg @@ -70,7 +70,7 @@ install_requires = #essentials packageurl-python>=0.10.5rc1 - univers>=30.9.0 + univers>=30.9.1 license-expression>=21.6.14 # file and data formats diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index 2ff604f4b..4007bac86 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -87,7 +87,41 @@ def to_advisory(self, data): for version_data in products["version"]["version_data"]: versions_data.append(version_data) - affected_version_range = self.to_version_ranges(versions_data) + # ===================================== + + fixed_versions = [] + for timeline_object in data["timeline"]: + timeline_value = timeline_object["value"] + if "release" in timeline_value: + split_timeline_value = timeline_value.split(" ") + if "never" in timeline_value: + pass + if "release" in split_timeline_value[-1] and "never" not in split_timeline_value[0]: + fixed_versions.append(split_timeline_value[0]) + if "release" in split_timeline_value[0]: + fixed_versions.append(split_timeline_value[-1]) + + # for timeline_object in data["timeline"]: + # if "released" in timeline_object["value"]: + # # need to isolate the fixed version number + # fixed_version = timeline_object["value"] + # fixed_versions.append(fixed_version) + + # ===================================== + + # affected_version_range = self.to_version_ranges(versions_data) + affected_version_range = self.to_version_ranges(versions_data, fixed_versions) + # this assumes I already have the fixed versions + # fixed_versions = [] + # fixed_version_constraints = [] + # for fixed_version in fixed_versions: + # fixed_version_constraints.append( + # VersionConstraint( + # # comparator=">=", + # comparator="=", + # version=SemverVersion(fixed_version), + # ).invert() + # ) affected_packages = [] if affected_version_range: @@ -108,7 +142,8 @@ def to_advisory(self, data): references=[reference], ) - def to_version_ranges(self, versions_data): + # def to_version_ranges(self, versions_data): + def to_version_ranges(self, versions_data, fixed_versions): constraints = [] for version_data in versions_data: version_value = version_data["version_value"] @@ -142,6 +177,15 @@ def to_version_ranges(self, versions_data): ) ) + for fixed_version in fixed_versions: + constraints.append( + VersionConstraint( + # comparator=">=", + comparator="=", + version=SemverVersion(fixed_version), + ).invert() + ) + return GenericVersionRange(constraints=constraints) diff --git a/vulnerabilities/tests/conftest.py b/vulnerabilities/tests/conftest.py index 472693f7a..945af0ea6 100644 --- a/vulnerabilities/tests/conftest.py +++ b/vulnerabilities/tests/conftest.py @@ -25,7 +25,6 @@ def no_rmtree(monkeypatch): # Step 2: Run test for importer only if it is activated (pytestmark = pytest.mark.skipif(...)) # Step 3: Migrate all the tests collect_ignore = [ - "test_apache_httpd.py", "test_apache_kafka.py", "test_apache_tomcat.py", "test_api.py", diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index fb6ac2778..66b83c720 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -38,7 +38,18 @@ def test_to_version_ranges(): "version_value": "2.3.4", }, ] - affected_version_range = ApacheHTTPDImporter().to_version_ranges(data) + + # fixed_versions = [ + # { + # "version_affected": "=", + # "fixed_version": "1.3.2", + # }, + # ] + + fixed_versions = ["1.3.2"] + + # affected_version_range = ApacheHTTPDImporter().to_version_ranges(data) + affected_version_range = ApacheHTTPDImporter().to_version_ranges(data, fixed_versions) # Check vulnerable packages assert ( @@ -46,6 +57,8 @@ def test_to_version_ranges(): constraints=( VersionConstraint(comparator="=", version=SemverVersion(string="1.3.1")), VersionConstraint(comparator="<=", version=SemverVersion(string="2.3.4")), + # based on errors I got, I think we need "!=" because of the use of .invert() + VersionConstraint(comparator="!=", version=SemverVersion(string="1.3.2")), ) ) == affected_version_range diff --git a/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-1999-1199-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-1999-1199-apache-httpd-expected.json index 815b7a460..40e63d7c0 100644 --- a/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-1999-1199-apache-httpd-expected.json +++ b/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-1999-1199-apache-httpd-expected.json @@ -13,7 +13,7 @@ "qualifiers": null, "subpath": null }, - "affected_version_range": "vers:generic/1.3.0|1.3.1", + "affected_version_range": "vers:generic/1.3.0|1.3.1|!=1.3.2", "fixed_version": null } ], diff --git a/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2021-44224-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2021-44224-apache-httpd-expected.json index 5387b0716..b8a0d15c9 100644 --- a/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2021-44224-apache-httpd-expected.json +++ b/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2021-44224-apache-httpd-expected.json @@ -13,7 +13,7 @@ "qualifiers": null, "subpath": null }, - "affected_version_range": "vers:generic/>=2.4.7|<=2.4.51", + "affected_version_range": "vers:generic/>=2.4.7|<=2.4.51|!=2.4.52", "fixed_version": null } ], From b77529c425622e103c48e893bf5593d8cbcdcacc Mon Sep 17 00:00:00 2001 From: Tushar Goel Date: Thu, 1 Dec 2022 23:41:19 +0530 Subject: [PATCH 08/14] Modify get_exact_purls to support multiple fixed versions in an affected_package Signed-off-by: Tushar Goel --- vulnerabilities/improvers/default.py | 38 ++++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/vulnerabilities/improvers/default.py b/vulnerabilities/improvers/default.py index 42522f40b..23dea75aa 100644 --- a/vulnerabilities/improvers/default.py +++ b/vulnerabilities/improvers/default.py @@ -39,21 +39,22 @@ def interesting_advisories(self) -> QuerySet: return Advisory.objects.all() def get_inferences(self, advisory_data: AdvisoryData) -> Iterable[Inference]: - if not advisory_data: return [] if advisory_data.affected_packages: for affected_package in advisory_data.affected_packages: - affected_purls, fixed_purl = get_exact_purls(affected_package) - yield Inference( - aliases=advisory_data.aliases, - confidence=MAX_CONFIDENCE, - summary=advisory_data.summary, - affected_purls=affected_purls, - fixed_purl=fixed_purl, - references=advisory_data.references, - ) + # To deal with multiple fixed versions in a single affected package + affected_purls, fixed_purls = get_exact_purls(affected_package) + for fixed_purl in fixed_purls: + yield Inference( + aliases=advisory_data.aliases, + confidence=MAX_CONFIDENCE, + summary=advisory_data.summary, + affected_purls=affected_purls, + fixed_purl=fixed_purl, + references=advisory_data.references, + ) else: yield Inference.from_advisory_data( @@ -78,7 +79,7 @@ def get_exact_purls(affected_package: AffectedPackage) -> Tuple[List[PackageURL] >>> got = get_exact_purls(affected_package) >>> expected = ( ... [PackageURL(type='turtle', namespace=None, name='green', version='2.0.0', qualifiers={}, subpath=None)], - ... PackageURL(type='turtle', namespace=None, name='green', version='5.0.0', qualifiers={}, subpath=None) + ... [PackageURL(type='turtle', namespace=None, name='green', version='5.0.0', qualifiers={}, subpath=None)] ... ) >>> assert expected == got """ @@ -89,16 +90,25 @@ def get_exact_purls(affected_package: AffectedPackage) -> Tuple[List[PackageURL] # TODO: Revisit after https://github.com/nexB/univers/issues/33 try: affected_purls = [] + fixed_versions = [] if vr: range_versions = [c.version for c in vr.constraints if c] + # Any version that's not affected by a vulnerability is considered + # fixed. + fixed_versions = [c.version for c in vr.constraints if c and c.comparator == "!="] resolved_versions = [v for v in range_versions if v and v in vr] for version in resolved_versions: affected_purl = evolve_purl(purl=affected_package.package, version=str(version)) affected_purls.append(affected_purl) - fixed_purl = affected_package.get_fixed_purl() if affected_package.fixed_version else None + if affected_package.fixed_version: + fixed_versions.append(affected_package.fixed_version) - return affected_purls, fixed_purl + fixed_purls = [ + evolve_purl(purl=affected_package.package, version=str(version)) + for version in fixed_versions + ] + return affected_purls, fixed_purls except Exception as e: logger.error(f"Failed to get exact purls for {affected_package} {e}") - return [], None + return [], [] From 3af61929461c6adc5de52472934b0aec234b202c Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Thu, 1 Dec 2022 12:00:55 -0800 Subject: [PATCH 09/14] Add test for unknown comparator exception #971 Reference: https://github.com/nexB/vulnerablecode/issues/971 Signed-off-by: John M. Horan --- vulnerabilities/importers/apache_httpd.py | 24 ------------- vulnerabilities/tests/test_apache_httpd.py | 39 +++++++++++++++------- 2 files changed, 27 insertions(+), 36 deletions(-) diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index 4007bac86..f32cf4673 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -87,8 +87,6 @@ def to_advisory(self, data): for version_data in products["version"]["version_data"]: versions_data.append(version_data) - # ===================================== - fixed_versions = [] for timeline_object in data["timeline"]: timeline_value = timeline_object["value"] @@ -101,27 +99,7 @@ def to_advisory(self, data): if "release" in split_timeline_value[0]: fixed_versions.append(split_timeline_value[-1]) - # for timeline_object in data["timeline"]: - # if "released" in timeline_object["value"]: - # # need to isolate the fixed version number - # fixed_version = timeline_object["value"] - # fixed_versions.append(fixed_version) - - # ===================================== - - # affected_version_range = self.to_version_ranges(versions_data) affected_version_range = self.to_version_ranges(versions_data, fixed_versions) - # this assumes I already have the fixed versions - # fixed_versions = [] - # fixed_version_constraints = [] - # for fixed_version in fixed_versions: - # fixed_version_constraints.append( - # VersionConstraint( - # # comparator=">=", - # comparator="=", - # version=SemverVersion(fixed_version), - # ).invert() - # ) affected_packages = [] if affected_version_range: @@ -142,7 +120,6 @@ def to_advisory(self, data): references=[reference], ) - # def to_version_ranges(self, versions_data): def to_version_ranges(self, versions_data, fixed_versions): constraints = [] for version_data in versions_data: @@ -180,7 +157,6 @@ def to_version_ranges(self, versions_data, fixed_versions): for fixed_version in fixed_versions: constraints.append( VersionConstraint( - # comparator=">=", comparator="=", version=SemverVersion(fixed_version), ).invert() diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index 66b83c720..5ac7940d0 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -11,6 +11,7 @@ import os from unittest import TestCase +import pytest from univers.version_constraint import VersionConstraint from univers.version_range import GenericVersionRange from univers.versions import SemverVersion @@ -38,26 +39,14 @@ def test_to_version_ranges(): "version_value": "2.3.4", }, ] - - # fixed_versions = [ - # { - # "version_affected": "=", - # "fixed_version": "1.3.2", - # }, - # ] - fixed_versions = ["1.3.2"] - - # affected_version_range = ApacheHTTPDImporter().to_version_ranges(data) affected_version_range = ApacheHTTPDImporter().to_version_ranges(data, fixed_versions) - # Check vulnerable packages assert ( GenericVersionRange( constraints=( VersionConstraint(comparator="=", version=SemverVersion(string="1.3.1")), VersionConstraint(comparator="<=", version=SemverVersion(string="2.3.4")), - # based on errors I got, I think we need "!=" because of the use of .invert() VersionConstraint(comparator="!=", version=SemverVersion(string="1.3.2")), ) ) @@ -65,6 +54,32 @@ def test_to_version_ranges(): ) +def unknown_comparator(): + data = [ + { + "version_affected": "?=", + "version_value": "1.3.0", + }, + { + "version_affected": "=", + "version_value": "1.3.1", + }, + { + "version_affected": "#=", + "version_value": "2.3.4", + }, + ] + fixed_versions = ["1.3.2"] + affected_version_range = ApacheHTTPDImporter().to_version_ranges(data, fixed_versions) + + +def test_unknown_comparator_exception(): + with pytest.raises(ValueError) as excinfo: + unknown_comparator() + + assert "unknown comparator found! #=" in str(excinfo.value) + + def test_to_advisory_CVE_1999_1199(): with open(os.path.join(TEST_DATA, "CVE-1999-1199.json")) as f: data = json.load(f) From 1e79bc632c7053d036272cfe3c3174df2a205325 Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Thu, 1 Dec 2022 17:52:29 -0800 Subject: [PATCH 10/14] Update fixed-version code and add test for 'never released' values #971 Reference: https://github.com/nexB/vulnerablecode/issues/971 Signed-off-by: John M. Horan --- vulnerabilities/importers/apache_httpd.py | 4 +- vulnerabilities/tests/test_apache_httpd.py | 10 + .../test_data/apache_httpd/CVE-2017-9798.json | 333 ++++++++++++++++++ ...y-CVE-2017-9798-apache-httpd-expected.json | 34 ++ 4 files changed, 379 insertions(+), 2 deletions(-) create mode 100644 vulnerabilities/tests/test_data/apache_httpd/CVE-2017-9798.json create mode 100644 vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2017-9798-apache-httpd-expected.json diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index f32cf4673..38974b95b 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -93,8 +93,8 @@ def to_advisory(self, data): if "release" in timeline_value: split_timeline_value = timeline_value.split(" ") if "never" in timeline_value: - pass - if "release" in split_timeline_value[-1] and "never" not in split_timeline_value[0]: + continue + if "release" in split_timeline_value[-1]: fixed_versions.append(split_timeline_value[0]) if "release" in split_timeline_value[0]: fixed_versions.append(split_timeline_value[-1]) diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index 5ac7940d0..84a93178a 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -100,3 +100,13 @@ def test_to_advisory_CVE_2021_44224(): TEST_DATA, f"to-advisory-CVE-2021-44224-apache-httpd-expected.json" ) util_tests.check_results_against_json(result, expected_file) + + +def test_to_advisory_CVE_2017_9798(): + with open(os.path.join(TEST_DATA, "CVE-2017-9798.json")) as f: + data = json.load(f) + + advisories = ApacheHTTPDImporter().to_advisory(data) + result = advisories.to_dict() + expected_file = os.path.join(TEST_DATA, f"to-advisory-CVE-2017-9798-apache-httpd-expected.json") + util_tests.check_results_against_json(result, expected_file) diff --git a/vulnerabilities/tests/test_data/apache_httpd/CVE-2017-9798.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-2017-9798.json new file mode 100644 index 000000000..1191d8c4b --- /dev/null +++ b/vulnerabilities/tests/test_data/apache_httpd/CVE-2017-9798.json @@ -0,0 +1,333 @@ +{ + "data_type": "CVE", + "data_format": "MITRE", + "data_version": "4.0", + "generator": { + "engine": "xmltojsonmjc 1.0" + }, + "references": {}, + "timeline": [ + { + "time": "2017-07-12", + "lang": "eng", + "value": "reported" + }, + { + "time": "2017-09-18", + "lang": "eng", + "value": "public" + }, + { + "time": "2017-10-05", + "lang": "eng", + "value": "2.4.28 released" + }, + { + "time": "--", + "lang": "eng", + "value": "2.2.35-never released" + } + ], + "CNA_private": { + "owner": "httpd" + }, + "CVE_data_meta": { + "ASSIGNER": "security@apache.org", + "AKA": "", + "STATE": "PUBLIC", + "DATE_PUBLIC": "2017-09-18", + "ID": "CVE-2017-9798", + "TITLE": "Use-after-free when using with an unrecognized method in .htaccess (\"OptionsBleed\")" + }, + "source": { + "defect": [], + "advisory": "", + "discovery": "UNKNOWN" + }, + "problemtype": { + "problemtype_data": [ + { + "description": [ + { + "lang": "eng", + "value": "Use-after-free when using with an unrecognized method in .htaccess (\"OptionsBleed\")" + } + ] + } + ] + }, + "credit": [ + { + "lang": "eng", + "value": "We would like to thank Hanno Böck for reporting this issue." + } + ], + "description": { + "description_data": [ + { + "lang": "eng", + "value": "When an unrecognized HTTP Method is given in an directive in an .htaccess file, and that .htaccess file is processed by the corresponding request, the global methods table is corrupted in the current worker process, resulting in erratic behaviour. This behavior may be avoided by listing all unusual HTTP Methods in a global httpd.conf RegisterHttpMethod directive in httpd release 2.4.25 and later. To permit other .htaccess directives while denying the directive, see the AllowOverrideList directive. Source code patch (2.4) is at; CVE-2017-9798-patch-2.4.patch Source code patch (2.2) is at; CVE-2017-9798-patch-2.2.patch Note 2.2 is end-of-life, no further release with this fix is planned. Users are encouraged to migrate to 2.4.28 or later for this and other fixes." + } + ] + }, + "impact": [ + { + "other": "low" + } + ], + "affects": { + "vendor": { + "vendor_data": [ + { + "vendor_name": "Apache Software Foundation", + "product": { + "product_data": [ + { + "product_name": "Apache HTTP Server", + "version": { + "version_data": [ + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.27" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.26" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.25" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.23" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.20" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.18" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.17" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.16" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.12" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.10" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.9" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.7" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.6" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.4" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.3" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.2" + }, + { + "version_name": "2.4", + "version_affected": "=", + "version_value": "2.4.1" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.34" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.32" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.31" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.29" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.27" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.26" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.25" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.24" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.23" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.22" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.21" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.20" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.19" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.18" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.17" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.16" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.15" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.14" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.13" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.12" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.11" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.10" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.9" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.8" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.6" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.5" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.4" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.3" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.2" + }, + { + "version_name": "2.2", + "version_affected": "=", + "version_value": "2.2.0" + } + ] + } + } + ] + } + } + ] + } + } +} diff --git a/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2017-9798-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2017-9798-apache-httpd-expected.json new file mode 100644 index 000000000..e04f4a906 --- /dev/null +++ b/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2017-9798-apache-httpd-expected.json @@ -0,0 +1,34 @@ +{ + "aliases": [ + "CVE-2017-9798" + ], + "summary": "When an unrecognized HTTP Method is given in an directive in an .htaccess file, and that .htaccess file is processed by the corresponding request, the global methods table is corrupted in the current worker process, resulting in erratic behaviour. This behavior may be avoided by listing all unusual HTTP Methods in a global httpd.conf RegisterHttpMethod directive in httpd release 2.4.25 and later. To permit other .htaccess directives while denying the directive, see the AllowOverrideList directive. Source code patch (2.4) is at; CVE-2017-9798-patch-2.4.patch Source code patch (2.2) is at; CVE-2017-9798-patch-2.2.patch Note 2.2 is end-of-life, no further release with this fix is planned. Users are encouraged to migrate to 2.4.28 or later for this and other fixes.", + "affected_packages": [ + { + "package": { + "type": "generic", + "namespace": null, + "name": "apache_httpd", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:generic/2.2.0|2.2.2|2.2.3|2.2.4|2.2.5|2.2.6|2.2.8|2.2.9|2.2.10|2.2.11|2.2.12|2.2.13|2.2.14|2.2.15|2.2.16|2.2.17|2.2.18|2.2.19|2.2.20|2.2.21|2.2.22|2.2.23|2.2.24|2.2.25|2.2.26|2.2.27|2.2.29|2.2.31|2.2.32|2.2.34|2.4.1|2.4.2|2.4.3|2.4.4|2.4.6|2.4.7|2.4.9|2.4.10|2.4.12|2.4.16|2.4.17|2.4.18|2.4.20|2.4.23|2.4.25|2.4.26|2.4.27|!=2.4.28", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "CVE-2017-9798", + "url": "https://httpd.apache.org/security/json/CVE-2017-9798.json", + "severities": [ + { + "system": "apache_httpd", + "value": "low", + "scoring_elements": "" + } + ] + } + ], + "date_published": null +} \ No newline at end of file From 71e52a29ae6d72b0a7fcd490a9f72cea99223f2a Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Tue, 13 Dec 2022 12:19:10 -0800 Subject: [PATCH 11/14] Refactor importer and test files #971 Reference: https://github.com/nexB/vulnerablecode/issues/971 Signed-off-by: John M. Horan --- vulnerabilities/importers/apache_httpd.py | 57 +++++-------------- vulnerabilities/tests/test_apache_httpd.py | 8 +-- ... CVE-1999-1199-apache-httpd-expected.json} | 0 ... CVE-2017-9798-apache-httpd-expected.json} | 0 ...CVE-2021-44224-apache-httpd-expected.json} | 0 5 files changed, 17 insertions(+), 48 deletions(-) rename vulnerabilities/tests/test_data/apache_httpd/{to-advisory-CVE-1999-1199-apache-httpd-expected.json => CVE-1999-1199-apache-httpd-expected.json} (100%) rename vulnerabilities/tests/test_data/apache_httpd/{to-advisory-CVE-2017-9798-apache-httpd-expected.json => CVE-2017-9798-apache-httpd-expected.json} (100%) rename vulnerabilities/tests/test_data/apache_httpd/{to-advisory-CVE-2021-44224-apache-httpd-expected.json => CVE-2021-44224-apache-httpd-expected.json} (100%) diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index 38974b95b..5f595be29 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -22,11 +22,8 @@ from vulnerabilities.importer import Importer from vulnerabilities.importer import Reference from vulnerabilities.importer import VulnerabilitySeverity -from vulnerabilities.package_managers import GitHubTagsAPI from vulnerabilities.severity_systems import APACHE_HTTPD -KNOWN_RANGE_EXPRESSIONS = {"<=", ">=", "?=", "!<", "="} - class ApacheHTTPDImporter(Importer): @@ -34,21 +31,8 @@ class ApacheHTTPDImporter(Importer): spdx_license_expression = "Apache-2.0" license_url = "https://www.apache.org/licenses/" - # For now, don't use the GH API - # def set_api(self): - # self.version_api = GitHubTagsAPI() - # asyncio.run(self.version_api.load_api(["apache/httpd"])) - # self.version_api.cache["apache/httpd"] = set( - # filter( - # lambda version: version.value not in ignore_tags, - # self.version_api.cache["apache/httpd"], - # ) - # ) - def advisory_data(self): links = fetch_links(self.base_url) - # For now, don't use the GH API - # self.set_api() for link in links: data = requests.get(link).json() yield self.to_advisory(data) @@ -99,9 +83,8 @@ def to_advisory(self, data): if "release" in split_timeline_value[0]: fixed_versions.append(split_timeline_value[-1]) - affected_version_range = self.to_version_ranges(versions_data, fixed_versions) - affected_packages = [] + affected_version_range = self.to_version_ranges(versions_data, fixed_versions) if affected_version_range: affected_packages.append( AffectedPackage( @@ -125,36 +108,24 @@ def to_version_ranges(self, versions_data, fixed_versions): for version_data in versions_data: version_value = version_data["version_value"] range_expression = version_data["version_affected"] - if range_expression not in KNOWN_RANGE_EXPRESSIONS: + if range_expression not in {"<=", ">=", "?=", "!<", "="}: raise ValueError(f"unknown comparator found! {range_expression}") - if range_expression == ">=" or range_expression == "!<": + comparator_by_range_expression = { + ">=": ">=", + "!<": ">=", + "<=": "<=", + "=": "=", + } + comparator = comparator_by_range_expression.get(range_expression) + if comparator: constraints.append( - VersionConstraint( - comparator=">=", - version=SemverVersion(version_value), - ) - ) - - if range_expression == "<=": - constraints.append( - VersionConstraint( - comparator="<=", - version=SemverVersion(version_value), - ) - ) - - if range_expression == "?=": - pass - - if range_expression == "=": - constraints.append( - VersionConstraint( - comparator="=", - version=SemverVersion(version_value), - ) + VersionConstraint(comparator=comparator, version=SemverVersion(version_value)) ) for fixed_version in fixed_versions: + # The VersionConstraint method `invert()` inverts the fixed_version's comparator, + # enabling us to include the fixed_version among the `affected_version_range` values + # and accurately reflect its fixed value. constraints.append( VersionConstraint( comparator="=", diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index 84a93178a..c9de58f68 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -86,7 +86,7 @@ def test_to_advisory_CVE_1999_1199(): advisories = ApacheHTTPDImporter().to_advisory(data) result = advisories.to_dict() - expected_file = os.path.join(TEST_DATA, f"to-advisory-CVE-1999-1199-apache-httpd-expected.json") + expected_file = os.path.join(TEST_DATA, f"CVE-1999-1199-apache-httpd-expected.json") util_tests.check_results_against_json(result, expected_file) @@ -96,9 +96,7 @@ def test_to_advisory_CVE_2021_44224(): advisories = ApacheHTTPDImporter().to_advisory(data) result = advisories.to_dict() - expected_file = os.path.join( - TEST_DATA, f"to-advisory-CVE-2021-44224-apache-httpd-expected.json" - ) + expected_file = os.path.join(TEST_DATA, f"CVE-2021-44224-apache-httpd-expected.json") util_tests.check_results_against_json(result, expected_file) @@ -108,5 +106,5 @@ def test_to_advisory_CVE_2017_9798(): advisories = ApacheHTTPDImporter().to_advisory(data) result = advisories.to_dict() - expected_file = os.path.join(TEST_DATA, f"to-advisory-CVE-2017-9798-apache-httpd-expected.json") + expected_file = os.path.join(TEST_DATA, f"CVE-2017-9798-apache-httpd-expected.json") util_tests.check_results_against_json(result, expected_file) diff --git a/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-1999-1199-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-1999-1199-apache-httpd-expected.json similarity index 100% rename from vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-1999-1199-apache-httpd-expected.json rename to vulnerabilities/tests/test_data/apache_httpd/CVE-1999-1199-apache-httpd-expected.json diff --git a/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2017-9798-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-2017-9798-apache-httpd-expected.json similarity index 100% rename from vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2017-9798-apache-httpd-expected.json rename to vulnerabilities/tests/test_data/apache_httpd/CVE-2017-9798-apache-httpd-expected.json diff --git a/vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2021-44224-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-2021-44224-apache-httpd-expected.json similarity index 100% rename from vulnerabilities/tests/test_data/apache_httpd/to-advisory-CVE-2021-44224-apache-httpd-expected.json rename to vulnerabilities/tests/test_data/apache_httpd/CVE-2021-44224-apache-httpd-expected.json From 0b74c9f1c1c993c5131efaa9e33ca95439b340a3 Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Tue, 13 Dec 2022 14:23:49 -0800 Subject: [PATCH 12/14] Refactor, add test and clean up #971 Reference: https://github.com/nexB/vulnerablecode/issues/971 Signed-off-by: John M. Horan --- vulnerabilities/importers/apache_httpd.py | 3 +- vulnerabilities/tests/test_apache_httpd.py | 12 +- .../CVE-2022-28614-apache-httpd-expected.json | 34 +++++ .../apache_httpd/CVE-2022-28614.json | 122 ++++++++++++++++++ 4 files changed, 167 insertions(+), 4 deletions(-) create mode 100644 vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614-apache-httpd-expected.json create mode 100644 vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614.json diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index 5f595be29..f178def16 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -124,8 +124,7 @@ def to_version_ranges(self, versions_data, fixed_versions): for fixed_version in fixed_versions: # The VersionConstraint method `invert()` inverts the fixed_version's comparator, - # enabling us to include the fixed_version among the `affected_version_range` values - # and accurately reflect its fixed value. + # enabling inclusion of multiple fixed versions with the `affected_version_range` values. constraints.append( VersionConstraint( comparator="=", diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index c9de58f68..941932e7b 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -9,7 +9,6 @@ import json import os -from unittest import TestCase import pytest from univers.version_constraint import VersionConstraint @@ -17,7 +16,6 @@ from univers.versions import SemverVersion from vulnerabilities.importers.apache_httpd import ApacheHTTPDImporter -from vulnerabilities.package_managers import GitHubTagsAPI from vulnerabilities.tests import util_tests BASE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -108,3 +106,13 @@ def test_to_advisory_CVE_2017_9798(): result = advisories.to_dict() expected_file = os.path.join(TEST_DATA, f"CVE-2017-9798-apache-httpd-expected.json") util_tests.check_results_against_json(result, expected_file) + + +def test_to_advisory_CVE_2022_28614(): + with open(os.path.join(TEST_DATA, "CVE-2022-28614.json")) as f: + data = json.load(f) + + advisories = ApacheHTTPDImporter().to_advisory(data) + result = advisories.to_dict() + expected_file = os.path.join(TEST_DATA, f"CVE-2022-28614-apache-httpd-expected.json") + util_tests.check_results_against_json(result, expected_file) diff --git a/vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614-apache-httpd-expected.json new file mode 100644 index 000000000..c7aec8e38 --- /dev/null +++ b/vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614-apache-httpd-expected.json @@ -0,0 +1,34 @@ +{ + "aliases": [ + "CVE-2022-28614" + ], + "summary": "The ap_rwrite() function in Apache HTTP Server 2.4.53 and earlier may read unintended memory if an attacker can cause the server to reflect very large input using ap_rwrite() or ap_rputs(), such as with mod_luas r:puts() function.\n\nModules compiled and distributed separately from Apache HTTP Server that use the \"ap_rputs\" function and may pass it a very large (INT_MAX or larger) string must be compiled against current headers to resolve the issue.", + "affected_packages": [ + { + "package": { + "type": "generic", + "namespace": null, + "name": "apache_httpd", + "version": null, + "qualifiers": null, + "subpath": null + }, + "affected_version_range": "vers:generic/<=2.4.53|!=2.4.54", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "CVE-2022-28614", + "url": "https://httpd.apache.org/security/json/CVE-2022-28614.json", + "severities": [ + { + "system": "apache_httpd", + "value": "low", + "scoring_elements": "" + } + ] + } + ], + "date_published": null +} \ No newline at end of file diff --git a/vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614.json new file mode 100644 index 000000000..8bb37379c --- /dev/null +++ b/vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614.json @@ -0,0 +1,122 @@ +{ + "data_type": "CVE", + "data_format": "MITRE", + "data_version": "4.0", + "generator": { + "engine": "Vulnogram 0.0.9" + }, + "CVE_data_meta": { + "ID": "CVE-2022-28614", + "ASSIGNER": "security@apache.org", + "DATE_PUBLIC": "", + "TITLE": "read beyond bounds via ap_rwrite() ", + "AKA": "", + "STATE": "PUBLIC" + }, + "source": { + "defect": [], + "advisory": "", + "discovery": "UNKNOWN" + }, + "affects": { + "vendor": { + "vendor_data": [ + { + "vendor_name": "Apache Software Foundation", + "product": { + "product_data": [ + { + "product_name": "Apache HTTP Server", + "version": { + "version_data": [ + { + "version_name": "", + "version_affected": "<=", + "version_value": "2.4.53", + "platform": "" + } + ] + } + } + ] + } + } + ] + } + }, + "problemtype": { + "problemtype_data": [ + { + "description": [ + { + "lang": "eng", + "value": "CWE-190 Integer Overflow or Wraparound" + } + ] + }, + { + "description": [ + { + "lang": "eng", + "value": "CWE-200 Exposure of Sensitive Information to an Unauthorized Actor" + } + ] + } + ] + }, + "description": { + "description_data": [ + { + "value": "The ap_rwrite() function in Apache HTTP Server 2.4.53 and earlier may read unintended memory if an attacker can cause the server to reflect very large input using ap_rwrite() or ap_rputs(), such as with mod_luas r:puts() function.\n\nModules compiled and distributed separately from Apache HTTP Server that use the \"ap_rputs\" function and may pass it a very large (INT_MAX or larger) string must be compiled against current headers to resolve the issue.", + "lang": "eng" + } + ] + }, + "references": { + "reference_data": [ + { + "refsource": "CONFIRM", + "url": "https://httpd.apache.org/security/vulnerabilities_24.html", + "name": "" + } + ] + }, + "configuration": [], + "impact": [ + { + "other": "low" + } + ], + "exploit": [], + "work_around": [], + "solution": [], + "credit": [ + { + "lang": "eng", + "value": "The Apache HTTP Server project would like to thank Ronald Crane (Zippenhop LLC) for reporting this issue" + } + ], + "CNA_private": { + "owner": "httpd", + "publish": { + "ym": "", + "year": "", + "month": "" + }, + "share_with_CVE": true, + "CVE_table_description": [], + "CVE_list": [], + "internal_comments": "", + "todo": [], + "emailed": "yes", + "userslist": "dev@httpd.apache.org", + "email": "" + }, + "timeline": [ + { + "time": "2022-06-08", + "lang": "eng", + "value": "released in 2.4.54" + } + ] +} From bb539b88aa8bf668fa7bbb1a445bb00141ca4435 Mon Sep 17 00:00:00 2001 From: "John M. Horan" Date: Tue, 3 Jan 2023 12:21:45 -0800 Subject: [PATCH 13/14] Add changelog entry for apache_httpd #971 Reference: https://github.com/nexB/vulnerablecode/issues/971 Signed-off-by: John M. Horan --- CHANGELOG.rst | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 98148bf8e..424dc698e 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,10 +2,16 @@ Release notes ============= +Version v31.1.1 +--------------- + +- We re-enabled support for the Apache HTTPD securities advisories importer. + + Version v31.1.0 ---------------- -- We re-enabled support for the NPM vulnerabilities advisories importer. +- We re-enabled support for the NPM vulnerabilities advisories importer. - We re-enabled support for the Retiredotnet vulnerabilities advisories importer. - We are now handling purl fragments in package search. For example: you can now serch using queries in the UI like this : ``cherrypy@2.1.1``, @@ -30,7 +36,7 @@ Version v31.0.0 - We made bulk search faster by pre-computing `package_url` and `plain_package_url` in Package model. And provided two options in package bulk search ``purl_only`` option to get only vulnerable purls without any - extra details, ``plain_purl`` option to filter purls without qualifiers and + extra details, ``plain_purl`` option to filter purls without qualifiers and subpath and also return them without qualifiers and subpath. The names used are provisional and may be updated in a future release. From b6899163a1f859a7860c6df5f4e78bcae8a0c620 Mon Sep 17 00:00:00 2001 From: Tushar Goel <34160672+TG1999@users.noreply.github.com> Date: Fri, 6 Jan 2023 20:25:20 +0530 Subject: [PATCH 14/14] Apply suggestions from code review Signed-off-by: Tushar Goel --- CHANGELOG.rst | 2 +- vulnerabilities/importers/apache_httpd.py | 12 ++++++------ vulnerabilities/tests/test_apache_httpd.py | 4 ++-- .../CVE-1999-1199-apache-httpd-expected.json | 6 +++--- .../CVE-2017-9798-apache-httpd-expected.json | 6 +++--- .../CVE-2021-44224-apache-httpd-expected.json | 6 +++--- .../CVE-2022-28614-apache-httpd-expected.json | 6 +++--- 7 files changed, 21 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 424dc698e..19707af2c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,7 +5,7 @@ Release notes Version v31.1.1 --------------- -- We re-enabled support for the Apache HTTPD securities advisories importer. +- We re-enabled support for the Apache HTTPD security advisories importer. Version v31.1.0 diff --git a/vulnerabilities/importers/apache_httpd.py b/vulnerabilities/importers/apache_httpd.py index f178def16..cadcd6800 100644 --- a/vulnerabilities/importers/apache_httpd.py +++ b/vulnerabilities/importers/apache_httpd.py @@ -14,7 +14,7 @@ from bs4 import BeautifulSoup from packageurl import PackageURL from univers.version_constraint import VersionConstraint -from univers.version_range import GenericVersionRange +from univers.version_range import ApacheVersionRange from univers.versions import SemverVersion from vulnerabilities.importer import AdvisoryData @@ -29,7 +29,7 @@ class ApacheHTTPDImporter(Importer): base_url = "https://httpd.apache.org/security/json/" spdx_license_expression = "Apache-2.0" - license_url = "https://www.apache.org/licenses/" + license_url = "https://www.apache.org/licenses/LICENSE-2.0" def advisory_data(self): links = fetch_links(self.base_url) @@ -72,7 +72,7 @@ def to_advisory(self, data): versions_data.append(version_data) fixed_versions = [] - for timeline_object in data["timeline"]: + for timeline_object in data.get("timeline") or []: timeline_value = timeline_object["value"] if "release" in timeline_value: split_timeline_value = timeline_value.split(" ") @@ -89,8 +89,8 @@ def to_advisory(self, data): affected_packages.append( AffectedPackage( package=PackageURL( - type="generic", - name="apache_httpd", + type="apache", + name="httpd", ), affected_version_range=affected_version_range, ) @@ -132,7 +132,7 @@ def to_version_ranges(self, versions_data, fixed_versions): ).invert() ) - return GenericVersionRange(constraints=constraints) + return ApacheVersionRange(constraints=constraints) def fetch_links(url): diff --git a/vulnerabilities/tests/test_apache_httpd.py b/vulnerabilities/tests/test_apache_httpd.py index 941932e7b..9ff6bd5b6 100644 --- a/vulnerabilities/tests/test_apache_httpd.py +++ b/vulnerabilities/tests/test_apache_httpd.py @@ -12,7 +12,7 @@ import pytest from univers.version_constraint import VersionConstraint -from univers.version_range import GenericVersionRange +from univers.version_range import ApacheVersionRange from univers.versions import SemverVersion from vulnerabilities.importers.apache_httpd import ApacheHTTPDImporter @@ -41,7 +41,7 @@ def test_to_version_ranges(): affected_version_range = ApacheHTTPDImporter().to_version_ranges(data, fixed_versions) assert ( - GenericVersionRange( + ApacheVersionRange( constraints=( VersionConstraint(comparator="=", version=SemverVersion(string="1.3.1")), VersionConstraint(comparator="<=", version=SemverVersion(string="2.3.4")), diff --git a/vulnerabilities/tests/test_data/apache_httpd/CVE-1999-1199-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-1999-1199-apache-httpd-expected.json index 40e63d7c0..5e24e496c 100644 --- a/vulnerabilities/tests/test_data/apache_httpd/CVE-1999-1199-apache-httpd-expected.json +++ b/vulnerabilities/tests/test_data/apache_httpd/CVE-1999-1199-apache-httpd-expected.json @@ -6,14 +6,14 @@ "affected_packages": [ { "package": { - "type": "generic", + "type": "apache", "namespace": null, - "name": "apache_httpd", + "name": "httpd", "version": null, "qualifiers": null, "subpath": null }, - "affected_version_range": "vers:generic/1.3.0|1.3.1|!=1.3.2", + "affected_version_range": "vers:apache/1.3.0|1.3.1|!=1.3.2", "fixed_version": null } ], diff --git a/vulnerabilities/tests/test_data/apache_httpd/CVE-2017-9798-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-2017-9798-apache-httpd-expected.json index e04f4a906..53bf3e31d 100644 --- a/vulnerabilities/tests/test_data/apache_httpd/CVE-2017-9798-apache-httpd-expected.json +++ b/vulnerabilities/tests/test_data/apache_httpd/CVE-2017-9798-apache-httpd-expected.json @@ -6,14 +6,14 @@ "affected_packages": [ { "package": { - "type": "generic", + "type": "apache", "namespace": null, - "name": "apache_httpd", + "name": "httpd", "version": null, "qualifiers": null, "subpath": null }, - "affected_version_range": "vers:generic/2.2.0|2.2.2|2.2.3|2.2.4|2.2.5|2.2.6|2.2.8|2.2.9|2.2.10|2.2.11|2.2.12|2.2.13|2.2.14|2.2.15|2.2.16|2.2.17|2.2.18|2.2.19|2.2.20|2.2.21|2.2.22|2.2.23|2.2.24|2.2.25|2.2.26|2.2.27|2.2.29|2.2.31|2.2.32|2.2.34|2.4.1|2.4.2|2.4.3|2.4.4|2.4.6|2.4.7|2.4.9|2.4.10|2.4.12|2.4.16|2.4.17|2.4.18|2.4.20|2.4.23|2.4.25|2.4.26|2.4.27|!=2.4.28", + "affected_version_range": "vers:apache/2.2.0|2.2.2|2.2.3|2.2.4|2.2.5|2.2.6|2.2.8|2.2.9|2.2.10|2.2.11|2.2.12|2.2.13|2.2.14|2.2.15|2.2.16|2.2.17|2.2.18|2.2.19|2.2.20|2.2.21|2.2.22|2.2.23|2.2.24|2.2.25|2.2.26|2.2.27|2.2.29|2.2.31|2.2.32|2.2.34|2.4.1|2.4.2|2.4.3|2.4.4|2.4.6|2.4.7|2.4.9|2.4.10|2.4.12|2.4.16|2.4.17|2.4.18|2.4.20|2.4.23|2.4.25|2.4.26|2.4.27|!=2.4.28", "fixed_version": null } ], diff --git a/vulnerabilities/tests/test_data/apache_httpd/CVE-2021-44224-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-2021-44224-apache-httpd-expected.json index b8a0d15c9..411e4dee3 100644 --- a/vulnerabilities/tests/test_data/apache_httpd/CVE-2021-44224-apache-httpd-expected.json +++ b/vulnerabilities/tests/test_data/apache_httpd/CVE-2021-44224-apache-httpd-expected.json @@ -6,14 +6,14 @@ "affected_packages": [ { "package": { - "type": "generic", + "type": "apache", "namespace": null, - "name": "apache_httpd", + "name": "httpd", "version": null, "qualifiers": null, "subpath": null }, - "affected_version_range": "vers:generic/>=2.4.7|<=2.4.51|!=2.4.52", + "affected_version_range": "vers:apache/>=2.4.7|<=2.4.51|!=2.4.52", "fixed_version": null } ], diff --git a/vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614-apache-httpd-expected.json b/vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614-apache-httpd-expected.json index c7aec8e38..68e72b691 100644 --- a/vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614-apache-httpd-expected.json +++ b/vulnerabilities/tests/test_data/apache_httpd/CVE-2022-28614-apache-httpd-expected.json @@ -6,14 +6,14 @@ "affected_packages": [ { "package": { - "type": "generic", + "type": "apache", "namespace": null, - "name": "apache_httpd", + "name": "httpd", "version": null, "qualifiers": null, "subpath": null }, - "affected_version_range": "vers:generic/<=2.4.53|!=2.4.54", + "affected_version_range": "vers:apache/<=2.4.53|!=2.4.54", "fixed_version": null } ],