From 515784e1c78a4ce0686a5b326c3a6a78d16a304b Mon Sep 17 00:00:00 2001 From: Michael Ehab Mikhail Date: Mon, 16 Jun 2025 21:07:57 +0300 Subject: [PATCH] Modify Nginx pipeline importer to support package-first mode #1916 * Update Nginx importer to filter and process advisories relevant to the purl passed in the constructor * Update Nginx importer tests to include testing the package-first mode Signed-off-by: Michael Ehab Mikhail --- vulnerabilities/pipelines/nginx_importer.py | 54 ++- .../pipelines/test_nginx_importer_pipeline.py | 39 ++ ...ty_advisories-single-version-expected.json | 385 ++++++++++++++++++ 3 files changed, 476 insertions(+), 2 deletions(-) create mode 100644 vulnerabilities/tests/test_data/nginx/security_advisories-single-version-expected.json diff --git a/vulnerabilities/pipelines/nginx_importer.py b/vulnerabilities/pipelines/nginx_importer.py index c5e017033..cf4a7a815 100644 --- a/vulnerabilities/pipelines/nginx_importer.py +++ b/vulnerabilities/pipelines/nginx_importer.py @@ -34,6 +34,18 @@ class NginxImporterPipeline(VulnerableCodeBaseImporterPipeline): url = "https://nginx.org/en/security_advisories.html" importer_name = "Nginx Importer" + is_batch_run = True + + def __init__(self, *args, purl=None, **kwargs): + super().__init__(*args, **kwargs) + self.purl = purl + if self.purl: + NginxImporterPipeline.is_batch_run = False + if self.purl.type != "nginx": + print( + f"Warning: PURL type {self.purl.type} is not 'nginx', may not match any advisories" + ) + @classmethod def steps(cls): return ( @@ -57,8 +69,46 @@ def collect_advisories(self) -> Iterable[AdvisoryData]: soup = BeautifulSoup(self.advisory_data, features="lxml") vulnerability_list = soup.select("li p") for vulnerability_info in vulnerability_list: - ngnix_advisory = parse_advisory_data_from_paragraph(vulnerability_info) - yield to_advisory_data(ngnix_advisory) + nginx_advisory = parse_advisory_data_from_paragraph(vulnerability_info) + advisory_data = to_advisory_data(nginx_advisory) + + if self.purl and not self._is_advisory_affecting_purl(advisory_data): + continue + + yield advisory_data + + def _is_advisory_affecting_purl(self, advisory_data): + if not self.purl: + return True + + if self.purl.type != "nginx": + return False + + for affected_package in advisory_data.affected_packages: + if affected_package.package.type != self.purl.type: + continue + + if affected_package.package.name != self.purl.name: + continue + + if self.purl.qualifiers and "os" in self.purl.qualifiers: + if ( + not affected_package.package.qualifiers + or "os" not in affected_package.package.qualifiers + or affected_package.package.qualifiers["os"] != self.purl.qualifiers["os"] + ): + continue + + if self.purl.version: + purl_version = NginxVersion(self.purl.version) + + affected_range = affected_package.affected_version_range + if affected_range and purl_version not in affected_range: + continue + + return True + + return False class NginxAdvisory(NamedTuple): diff --git a/vulnerabilities/tests/pipelines/test_nginx_importer_pipeline.py b/vulnerabilities/tests/pipelines/test_nginx_importer_pipeline.py index 80f3705bf..2195bf56d 100644 --- a/vulnerabilities/tests/pipelines/test_nginx_importer_pipeline.py +++ b/vulnerabilities/tests/pipelines/test_nginx_importer_pipeline.py @@ -15,6 +15,7 @@ from bs4 import BeautifulSoup from commoncode import testcase from django.db.models.query import QuerySet +from packageurl import PackageURL from univers.version_range import NginxVersionRange from vulnerabilities import models @@ -239,3 +240,41 @@ def test_NginxBasicImprover__get_inferences_from_versions_end_to_end(self): "improver/improver-inferences-expected.json", must_exist=False ) util_tests.check_results_against_json(results, expected_file) + + def test_nginx_importer_package_first_mode_found(self): + test_file = self.get_test_loc("security_advisories.html") + with open(test_file) as tf: + test_text = tf.read() + + purl = PackageURL(type="nginx", name="nginx", version="1.17.2") + pipeline = nginx_importer.NginxImporterPipeline(purl=purl) + pipeline.advisory_data = test_text + advisories = list(pipeline.collect_advisories()) + expected_file = self.get_test_loc("security_advisories-single-version-expected.json") + advisories_dicts = [a.to_dict() for a in advisories] + util_tests.check_results_against_json(advisories_dicts, expected_file) + + def test_nginx_importer_package_first_mode_none_found(self): + test_file = self.get_test_loc("security_advisories.html") + with open(test_file) as tf: + test_text = tf.read() + + purl = PackageURL(type="nginx", name="nonexistent") + pipeline = nginx_importer.NginxImporterPipeline(purl=purl) + pipeline.advisory_data = test_text + advisories = list(pipeline.collect_advisories()) + assert advisories == [] + + def test_nginx_importer_package_first_mode_with_os_qualifier(self): + test_file = self.get_test_loc("security_advisories.html") + with open(test_file) as tf: + test_text = tf.read() + + purl = PackageURL(type="nginx", name="nginx", qualifiers={"os": "windows"}) + pipeline = nginx_importer.NginxImporterPipeline(purl=purl) + pipeline.advisory_data = test_text + advisories = list(pipeline.collect_advisories()) + + for adv in advisories: + for pkg in adv.affected_packages: + assert pkg.package.qualifiers.get("os") == "windows" diff --git a/vulnerabilities/tests/test_data/nginx/security_advisories-single-version-expected.json b/vulnerabilities/tests/test_data/nginx/security_advisories-single-version-expected.json new file mode 100644 index 000000000..d97ee4415 --- /dev/null +++ b/vulnerabilities/tests/test_data/nginx/security_advisories-single-version-expected.json @@ -0,0 +1,385 @@ +[ + { + "aliases": ["CVE-2022-41741"], + "summary": "Memory corruption in the ngx_http_mp4_module", + "affected_packages": [ + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=1.0.7|<=1.0.15|>=1.1.3|<=1.23.1", + "fixed_version": "1.23.2" + }, + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=1.0.7|<=1.0.15|>=1.1.3|<=1.23.1", + "fixed_version": "1.22.1" + } + ], + "references": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://mailman.nginx.org/pipermail/nginx-announce/2022/RBRRON6PYBJJM2XIAPQBFBVLR4Q6IHRA.html", + "severities": [ + { + "system": "generic_textual", + "value": "medium", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "CVE-2022-41741", + "reference_type": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-41741", + "severities": [] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://nginx.org/download/patch.2022.mp4.txt", + "severities": [] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://nginx.org/download/patch.2022.mp4.txt.asc", + "severities": [] + } + ], + "date_published": null, + "weaknesses": [], + "url": "https://nginx.org/en/security_advisories.html" + }, + { + "aliases": ["CVE-2022-41742"], + "summary": "Memory disclosure in the ngx_http_mp4_module", + "affected_packages": [ + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=1.0.7|<=1.0.15|>=1.1.3|<=1.23.1", + "fixed_version": "1.23.2" + }, + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=1.0.7|<=1.0.15|>=1.1.3|<=1.23.1", + "fixed_version": "1.22.1" + } + ], + "references": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://mailman.nginx.org/pipermail/nginx-announce/2022/RBRRON6PYBJJM2XIAPQBFBVLR4Q6IHRA.html", + "severities": [ + { + "system": "generic_textual", + "value": "medium", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "CVE-2022-41742", + "reference_type": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-41742", + "severities": [] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://nginx.org/download/patch.2022.mp4.txt", + "severities": [] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://nginx.org/download/patch.2022.mp4.txt.asc", + "severities": [] + } + ], + "date_published": null, + "weaknesses": [], + "url": "https://nginx.org/en/security_advisories.html" + }, + { + "aliases": ["CVE-2021-23017"], + "summary": "1-byte memory overwrite in resolver", + "affected_packages": [ + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=0.6.18|<=1.20.0", + "fixed_version": "1.21.0" + }, + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=0.6.18|<=1.20.0", + "fixed_version": "1.20.1" + } + ], + "references": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://mailman.nginx.org/pipermail/nginx-announce/2021/000300.html", + "severities": [ + { + "system": "generic_textual", + "value": "medium", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "CVE-2021-23017", + "reference_type": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-23017", + "severities": [] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://nginx.org/download/patch.2021.resolver.txt", + "severities": [] + }, + { + "reference_id": "", + "reference_type": "", + "url": "https://nginx.org/download/patch.2021.resolver.txt.asc", + "severities": [] + } + ], + "date_published": null, + "weaknesses": [], + "url": "https://nginx.org/en/security_advisories.html" + }, + { + "aliases": ["CVE-2019-9511"], + "summary": "Excessive CPU usage in HTTP/2 with small window updates", + "affected_packages": [ + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=1.9.5|<=1.17.2", + "fixed_version": "1.17.3" + }, + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=1.9.5|<=1.17.2", + "fixed_version": "1.16.1" + } + ], + "references": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://mailman.nginx.org/pipermail/nginx-announce/2019/000249.html", + "severities": [ + { + "system": "generic_textual", + "value": "medium", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "CVE-2019-9511", + "reference_type": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2019-9511", + "severities": [] + } + ], + "date_published": null, + "weaknesses": [], + "url": "https://nginx.org/en/security_advisories.html" + }, + { + "aliases": ["CVE-2019-9513"], + "summary": "Excessive CPU usage in HTTP/2 with priority changes", + "affected_packages": [ + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=1.9.5|<=1.17.2", + "fixed_version": "1.17.3" + }, + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=1.9.5|<=1.17.2", + "fixed_version": "1.16.1" + } + ], + "references": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://mailman.nginx.org/pipermail/nginx-announce/2019/000249.html", + "severities": [ + { + "system": "generic_textual", + "value": "low", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "CVE-2019-9513", + "reference_type": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2019-9513", + "severities": [] + } + ], + "date_published": null, + "weaknesses": [], + "url": "https://nginx.org/en/security_advisories.html" + }, + { + "aliases": ["CVE-2019-9516"], + "summary": "Excessive memory usage in HTTP/2 with zero length headers", + "affected_packages": [ + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=1.9.5|<=1.17.2", + "fixed_version": "1.17.3" + }, + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/>=1.9.5|<=1.17.2", + "fixed_version": "1.16.1" + } + ], + "references": [ + { + "reference_id": "", + "reference_type": "", + "url": "https://mailman.nginx.org/pipermail/nginx-announce/2019/000249.html", + "severities": [ + { + "system": "generic_textual", + "value": "low", + "scoring_elements": "" + } + ] + }, + { + "reference_id": "CVE-2019-9516", + "reference_type": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2019-9516", + "severities": [] + } + ], + "date_published": null, + "weaknesses": [], + "url": "https://nginx.org/en/security_advisories.html" + }, + { + "aliases": ["CVE-2009-4487"], + "summary": "An error log data are not sanitized", + "affected_packages": [ + { + "package": { + "type": "nginx", + "namespace": "", + "name": "nginx", + "version": "", + "qualifiers": "", + "subpath": "" + }, + "affected_version_range": "vers:nginx/*", + "fixed_version": null + } + ], + "references": [ + { + "reference_id": "CVE-2009-4487", + "reference_type": "", + "url": "https://nvd.nist.gov/vuln/detail/CVE-2009-4487", + "severities": [] + } + ], + "date_published": null, + "weaknesses": [], + "url": "https://nginx.org/en/security_advisories.html" + } +]