From ee9944b1ef16fa15c78964928e456e21d712552a Mon Sep 17 00:00:00 2001 From: Jay Paz Date: Thu, 7 Mar 2024 16:22:25 -0600 Subject: [PATCH 01/10] Updating pip-audit parser to handle new JSON file format [sc-3854] --- .../en/integrations/parsers/file/pip_audit.md | 38 ++++- dojo/tools/pip_audit/parser.py | 138 +++++++++++------ unittests/scans/pip_audit/empty_new.json | 3 + unittests/scans/pip_audit/many_vulns_new.json | 91 ++++++++++++ unittests/scans/pip_audit/zero_vulns_new.json | 18 +++ unittests/tools/test_pip_audit_parser.py | 139 +++++++++--------- 6 files changed, 310 insertions(+), 117 deletions(-) create mode 100644 unittests/scans/pip_audit/empty_new.json create mode 100644 unittests/scans/pip_audit/many_vulns_new.json create mode 100644 unittests/scans/pip_audit/zero_vulns_new.json diff --git a/docs/content/en/integrations/parsers/file/pip_audit.md b/docs/content/en/integrations/parsers/file/pip_audit.md index df24cdbe7a3..96b9b250d58 100644 --- a/docs/content/en/integrations/parsers/file/pip_audit.md +++ b/docs/content/en/integrations/parsers/file/pip_audit.md @@ -2,7 +2,41 @@ title: "pip-audit Scan" toc_hide: true --- -Import pip-audit JSON scan report + +Import pip-audit JSON scan report. + +### File Types +This parser expects a JSON file. + +The parser can handle legacy and current JSON format. + +The current format has added a `dependencies` element: + + { + "dependencies": [ + { + "name": "pyopenssl", + "version": "23.1.0", + "vulns": [] + }, + ... + ] + ... + } + +The legacy format does not include the `dependencies` key: + + [ + { + "name": "adal", + "version": "1.2.2", + "vulns": [] + }, + ... + ] ### Sample Scan Data -Sample pip-audit Scan scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/pip_audit). \ No newline at end of file +Sample pip-audit Scan scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/pip_audit). + +### Link To Tool +[pip-audit](https://pypi.org/project/pip-audit/) diff --git a/dojo/tools/pip_audit/parser.py b/dojo/tools/pip_audit/parser.py index 726667987fb..57b50d52dab 100644 --- a/dojo/tools/pip_audit/parser.py +++ b/dojo/tools/pip_audit/parser.py @@ -1,70 +1,110 @@ +"""Parser for pip-audit.""" import json +import logging from dojo.models import Finding +logger = logging.getLogger(__name__) + class PipAuditParser: + """Represents a file parser capable of ingesting pip-audit results.""" + def get_scan_types(self): + """Return the type of scan this parser ingests.""" return ["pip-audit Scan"] def get_label_for_scan_types(self, scan_type): + """Return the friendly name for this parser.""" return "pip-audit Scan" def get_description_for_scan_types(self, scan_type): + """Return the description for this parser.""" return "Import pip-audit JSON scan report." def requires_file(self, scan_type): + """Return boolean indicating if parser requires a file to process.""" return True def get_findings(self, scan_file, test): + """Return the collection of Findings ingested.""" data = json.load(scan_file) + findings = None + ##this parser can handle two distinct formats see sample scan files + if "dependencies" in data: + ##new format of report + findings = get_file_findings(data, test) + else: + ##legacy format of report + findings = get_legacy_findings(data, test) + + return findings + + +def get_file_findings(data, test): + findings = list() + for dependency in data["dependencies"]: + logger.debug("**-**") + logger.debug(dependency) + item_findings = get_item_findings(dependency, test) + if item_findings is not None: + findings.extend(item_findings) + return findings + +def get_legacy_findings(data, test): + findings = list() + for item in data: + item_findings = get_item_findings(item, test) + if item_findings is not None: + findings.extend(item_findings) + return findings + +def get_item_findings(item, test): + findings = list() + vulnerabilities = item.get("vulns", []) + if vulnerabilities: + component_name = item["name"] + component_version = item.get("version") + for vulnerability in vulnerabilities: + vuln_id = vulnerability.get("id") + vuln_fix_versions = vulnerability.get("fix_versions") + vuln_description = vulnerability.get("description") + + title = ( + f"{vuln_id} in {component_name}:{component_version}" + ) + + description = "" + description += vuln_description + + mitigation = None + if vuln_fix_versions: + mitigation = "Upgrade to version:" + if len(vuln_fix_versions) == 1: + mitigation += f" {vuln_fix_versions[0]}" + else: + for fix_version in vuln_fix_versions: + mitigation += f"\n- {fix_version}" + + finding = Finding( + test=test, + title=title, + cwe=1352, + severity="Medium", + description=description, + mitigation=mitigation, + component_name=component_name, + component_version=component_version, + vuln_id_from_tool=vuln_id, + static_finding=True, + dynamic_finding=False, + ) + vulnerability_ids = list() + if vuln_id: + vulnerability_ids.append(vuln_id) + if vulnerability_ids: + finding.unsaved_vulnerability_ids = vulnerability_ids + + findings.append(finding) - findings = list() - for item in data: - vulnerabilities = item.get("vulns", []) - if vulnerabilities: - component_name = item["name"] - component_version = item.get("version") - for vulnerability in vulnerabilities: - vuln_id = vulnerability.get("id") - vuln_fix_versions = vulnerability.get("fix_versions") - vuln_description = vulnerability.get("description") - - title = ( - f"{vuln_id} in {component_name}:{component_version}" - ) - - description = "" - description += vuln_description - - mitigation = None - if vuln_fix_versions: - mitigation = "Upgrade to version:" - if len(vuln_fix_versions) == 1: - mitigation += f" {vuln_fix_versions[0]}" - else: - for fix_version in vuln_fix_versions: - mitigation += f"\n- {fix_version}" - - finding = Finding( - test=test, - title=title, - cwe=1352, - severity="Medium", - description=description, - mitigation=mitigation, - component_name=component_name, - component_version=component_version, - vuln_id_from_tool=vuln_id, - static_finding=True, - dynamic_finding=False, - ) - vulnerability_ids = list() - if vuln_id: - vulnerability_ids.append(vuln_id) - if vulnerability_ids: - finding.unsaved_vulnerability_ids = vulnerability_ids - - findings.append(finding) - - return findings + return findings diff --git a/unittests/scans/pip_audit/empty_new.json b/unittests/scans/pip_audit/empty_new.json new file mode 100644 index 00000000000..45f00a3dece --- /dev/null +++ b/unittests/scans/pip_audit/empty_new.json @@ -0,0 +1,3 @@ +{ + "dependencies":[] +} diff --git a/unittests/scans/pip_audit/many_vulns_new.json b/unittests/scans/pip_audit/many_vulns_new.json new file mode 100644 index 00000000000..877ebf78ed8 --- /dev/null +++ b/unittests/scans/pip_audit/many_vulns_new.json @@ -0,0 +1,91 @@ +{ + "dependencies":[ + { + "name": "adal", + "version": "1.2.2", + "vulns": [] + }, + { + "name": "aiohttp", + "version": "3.6.2", + "vulns": [ + { + "id": "PYSEC-2021-76", + "fix_versions": [ + "3.7.4" + ], + "description": "aiohttp is an asynchronous HTTP client/server framework for asyncio and Python. In aiohttp before version 3.7.4 there is an open redirect vulnerability. A maliciously crafted link to an aiohttp-based web-server could redirect the browser to a different website. It is caused by a bug in the `aiohttp.web_middlewares.normalize_path_middleware` middleware. This security problem has been fixed in 3.7.4. Upgrade your dependency using pip as follows \"pip install aiohttp >= 3.7.4\". If upgrading is not an option for you, a workaround can be to avoid using `aiohttp.web_middlewares.normalize_path_middleware` in your applications." + } + ] + }, + { + "name": "alabaster", + "version": "0.7.12", + "vulns": [] + }, + { + "name": "azure-devops", + "skip_reason": "Dependency not found on PyPI and could not be audited: azure-devops (0.17.0)" + }, + { + "name": "django", + "version": "3.2.9", + "vulns": [ + { + "id": "PYSEC-2021-439", + "fix_versions": [ + "2.2.25", + "3.1.14", + "3.2.10" + ], + "description": "In Django 2.2 before 2.2.25, 3.1 before 3.1.14, and 3.2 before 3.2.10, HTTP requests for URLs with trailing newlines could bypass upstream access control based on URL paths." + } + ] + }, + { + "name": "lxml", + "version": "4.6.4", + "vulns": [ + { + "id": "PYSEC-2021-852", + "fix_versions": [], + "description": "lxml is a library for processing XML and HTML in the Python language. Prior to version 4.6.5, the HTML Cleaner in lxml.html lets certain crafted script content pass through, as well as script content in SVG files embedded using data URIs. Users that employ the HTML cleaner in a security relevant context should upgrade to lxml 4.6.5 to receive a patch. There are no known workarounds available." + } + ] + }, + { + "name": "twisted", + "version": "18.9.0", + "vulns": [ + { + "id": "PYSEC-2019-128", + "fix_versions": [ + "19.2.1" + ], + "description": "In Twisted before 19.2.1, twisted.web did not validate or sanitize URIs or HTTP methods, allowing an attacker to inject invalid characters such as CRLF." + }, + { + "id": "PYSEC-2020-260", + "fix_versions": [ + "20.3.0rc1" + ], + "description": "In Twisted Web through 19.10.0, there was an HTTP request splitting vulnerability. When presented with a content-length and a chunked encoding header, the content-length took precedence and the remainder of the request body was interpreted as a pipelined request." + }, + { + "id": "PYSEC-2019-129", + "fix_versions": [ + "19.7.0rc1" + ], + "description": "In words.protocols.jabber.xmlstream in Twisted through 19.2.1, XMPP support did not verify certificates when used with TLS, allowing an attacker to MITM connections." + }, + { + "id": "PYSEC-2020-259", + "fix_versions": [ + "20.3.0rc1" + ], + "description": "In Twisted Web through 19.10.0, there was an HTTP request splitting vulnerability. When presented with two content-length headers, it ignored the first header. When the second content-length value was set to zero, the request body was interpreted as a pipelined request." + } + ] + } + ] +} diff --git a/unittests/scans/pip_audit/zero_vulns_new.json b/unittests/scans/pip_audit/zero_vulns_new.json new file mode 100644 index 00000000000..f32e9b1b25e --- /dev/null +++ b/unittests/scans/pip_audit/zero_vulns_new.json @@ -0,0 +1,18 @@ +{ + "dependencies":[ + { + "name": "adal", + "version": "1.2.2", + "vulns": [] + }, + { + "name": "alabaster", + "version": "0.7.12", + "vulns": [] + }, + { + "name": "azure-devops", + "skip_reason": "Dependency not found on PyPI and could not be audited: azure-devops (0.17.0)" + } + ] +} diff --git a/unittests/tools/test_pip_audit_parser.py b/unittests/tools/test_pip_audit_parser.py index eb421f761a0..efc3936aedd 100644 --- a/unittests/tools/test_pip_audit_parser.py +++ b/unittests/tools/test_pip_audit_parser.py @@ -7,80 +7,87 @@ class TestPipAuditParser(DojoTestCase): def test_parser_empty(self): - testfile = open("unittests/scans/pip_audit/empty.json") - parser = PipAuditParser() - findings = parser.get_findings(testfile, Test()) - testfile.close() - self.assertEqual(0, len(findings)) + testfiles = ["unittests/scans/pip_audit/empty.json", + "unittests/scans/pip_audit/empty_new.json"] + for testfile in testfiles: + testfile = open("unittests/scans/pip_audit/empty.json") + parser = PipAuditParser() + findings = parser.get_findings(testfile, Test()) + testfile.close() + self.assertEqual(0, len(findings)) def test_parser_zero_findings(self): - testfile = open("unittests/scans/pip_audit/zero_vulns.json") - parser = PipAuditParser() - findings = parser.get_findings(testfile, Test()) - testfile.close() - self.assertEqual(0, len(findings)) + testfiles = ["unittests/scans/pip_audit/zero_vulns.json", + "unittests/scans/pip_audit/zero_vulns_new.json"] + for testfile in testfiles: + parser = PipAuditParser() + findings = parser.get_findings(testfile, Test()) + testfile.close() + self.assertEqual(0, len(findings)) def test_parser_many_vulns(self): - testfile = open("unittests/scans/pip_audit/many_vulns.json") - parser = PipAuditParser() - findings = parser.get_findings(testfile, Test()) - testfile.close() - self.assertEqual(7, len(findings)) + testfiles = ["unittests/scans/pip_audit/many_vulns.json", + "unittests/scans/pip_audit/many_vulns_new.json"] + for testfile in testfiles: + parser = PipAuditParser() + findings = parser.get_findings(testfile, Test()) + testfile.close() + self.assertEqual(7, len(findings)) - finding = findings[0] - self.assertEqual('PYSEC-2021-76 in aiohttp:3.6.2', finding.title) - description = 'aiohttp is an asynchronous HTTP client/server framework for asyncio and Python. In aiohttp before version 3.7.4 there is an open redirect vulnerability. A maliciously crafted link to an aiohttp-based web-server could redirect the browser to a different website. It is caused by a bug in the `aiohttp.web_middlewares.normalize_path_middleware` middleware. This security problem has been fixed in 3.7.4. Upgrade your dependency using pip as follows "pip install aiohttp >= 3.7.4". If upgrading is not an option for you, a workaround can be to avoid using `aiohttp.web_middlewares.normalize_path_middleware` in your applications.' - self.assertEqual(description, finding.description) - self.assertEqual(1352, finding.cwe) - vulnerability_ids = finding.unsaved_vulnerability_ids - self.assertEqual(1, len(vulnerability_ids)) - self.assertEqual('PYSEC-2021-76', vulnerability_ids[0]) - self.assertEqual('Medium', finding.severity) - self.assertEqual('Upgrade to version: 3.7.4', finding.mitigation) - self.assertEqual('aiohttp', finding.component_name) - self.assertEqual('3.6.2', finding.component_version) - self.assertEqual('PYSEC-2021-76', finding.vuln_id_from_tool) + finding = findings[0] + self.assertEqual('PYSEC-2021-76 in aiohttp:3.6.2', finding.title) + description = 'aiohttp is an asynchronous HTTP client/server framework for asyncio and Python. In aiohttp before version 3.7.4 there is an open redirect vulnerability. A maliciously crafted link to an aiohttp-based web-server could redirect the browser to a different website. It is caused by a bug in the `aiohttp.web_middlewares.normalize_path_middleware` middleware. This security problem has been fixed in 3.7.4. Upgrade your dependency using pip as follows "pip install aiohttp >= 3.7.4". If upgrading is not an option for you, a workaround can be to avoid using `aiohttp.web_middlewares.normalize_path_middleware` in your applications.' + self.assertEqual(description, finding.description) + self.assertEqual(1352, finding.cwe) + vulnerability_ids = finding.unsaved_vulnerability_ids + self.assertEqual(1, len(vulnerability_ids)) + self.assertEqual('PYSEC-2021-76', vulnerability_ids[0]) + self.assertEqual('Medium', finding.severity) + self.assertEqual('Upgrade to version: 3.7.4', finding.mitigation) + self.assertEqual('aiohttp', finding.component_name) + self.assertEqual('3.6.2', finding.component_version) + self.assertEqual('PYSEC-2021-76', finding.vuln_id_from_tool) - finding = findings[1] - self.assertEqual('PYSEC-2021-439 in django:3.2.9', finding.title) - description = 'In Django 2.2 before 2.2.25, 3.1 before 3.1.14, and 3.2 before 3.2.10, HTTP requests for URLs with trailing newlines could bypass upstream access control based on URL paths.' - self.assertEqual(description, finding.description) - vulnerability_ids = finding.unsaved_vulnerability_ids - self.assertEqual(1, len(vulnerability_ids)) - self.assertEqual('PYSEC-2021-439', vulnerability_ids[0]) - self.assertEqual(1352, finding.cwe) - self.assertEqual('Medium', finding.severity) - mitigation = '''Upgrade to version: -- 2.2.25 -- 3.1.14 -- 3.2.10''' - self.assertEqual(mitigation, finding.mitigation) - self.assertEqual('django', finding.component_name) - self.assertEqual('3.2.9', finding.component_version) - self.assertEqual('PYSEC-2021-439', finding.vuln_id_from_tool) + finding = findings[1] + self.assertEqual('PYSEC-2021-439 in django:3.2.9', finding.title) + description = 'In Django 2.2 before 2.2.25, 3.1 before 3.1.14, and 3.2 before 3.2.10, HTTP requests for URLs with trailing newlines could bypass upstream access control based on URL paths.' + self.assertEqual(description, finding.description) + vulnerability_ids = finding.unsaved_vulnerability_ids + self.assertEqual(1, len(vulnerability_ids)) + self.assertEqual('PYSEC-2021-439', vulnerability_ids[0]) + self.assertEqual(1352, finding.cwe) + self.assertEqual('Medium', finding.severity) + mitigation = '''Upgrade to version: + - 2.2.25 + - 3.1.14 + - 3.2.10''' + self.assertEqual(mitigation, finding.mitigation) + self.assertEqual('django', finding.component_name) + self.assertEqual('3.2.9', finding.component_version) + self.assertEqual('PYSEC-2021-439', finding.vuln_id_from_tool) - finding = findings[2] - self.assertEqual('PYSEC-2021-852 in lxml:4.6.4', finding.title) - description = 'lxml is a library for processing XML and HTML in the Python language. Prior to version 4.6.5, the HTML Cleaner in lxml.html lets certain crafted script content pass through, as well as script content in SVG files embedded using data URIs. Users that employ the HTML cleaner in a security relevant context should upgrade to lxml 4.6.5 to receive a patch. There are no known workarounds available.' - self.assertEqual(description, finding.description) - vulnerability_ids = finding.unsaved_vulnerability_ids - self.assertEqual(1, len(vulnerability_ids)) - self.assertEqual('PYSEC-2021-852', vulnerability_ids[0]) - self.assertEqual(1352, finding.cwe) - self.assertEqual('Medium', finding.severity) - self.assertIsNone(finding.mitigation) - self.assertEqual('lxml', finding.component_name) - self.assertEqual('4.6.4', finding.component_version) - self.assertEqual('PYSEC-2021-852', finding.vuln_id_from_tool) + finding = findings[2] + self.assertEqual('PYSEC-2021-852 in lxml:4.6.4', finding.title) + description = 'lxml is a library for processing XML and HTML in the Python language. Prior to version 4.6.5, the HTML Cleaner in lxml.html lets certain crafted script content pass through, as well as script content in SVG files embedded using data URIs. Users that employ the HTML cleaner in a security relevant context should upgrade to lxml 4.6.5 to receive a patch. There are no known workarounds available.' + self.assertEqual(description, finding.description) + vulnerability_ids = finding.unsaved_vulnerability_ids + self.assertEqual(1, len(vulnerability_ids)) + self.assertEqual('PYSEC-2021-852', vulnerability_ids[0]) + self.assertEqual(1352, finding.cwe) + self.assertEqual('Medium', finding.severity) + self.assertIsNone(finding.mitigation) + self.assertEqual('lxml', finding.component_name) + self.assertEqual('4.6.4', finding.component_version) + self.assertEqual('PYSEC-2021-852', finding.vuln_id_from_tool) - finding = findings[3] - self.assertEqual('PYSEC-2019-128 in twisted:18.9.0', finding.title) + finding = findings[3] + self.assertEqual('PYSEC-2019-128 in twisted:18.9.0', finding.title) - finding = findings[4] - self.assertEqual('PYSEC-2020-260 in twisted:18.9.0', finding.title) + finding = findings[4] + self.assertEqual('PYSEC-2020-260 in twisted:18.9.0', finding.title) - finding = findings[5] - self.assertEqual('PYSEC-2019-129 in twisted:18.9.0', finding.title) + finding = findings[5] + self.assertEqual('PYSEC-2019-129 in twisted:18.9.0', finding.title) - finding = findings[6] - self.assertEqual('PYSEC-2020-259 in twisted:18.9.0', finding.title) + finding = findings[6] + self.assertEqual('PYSEC-2020-259 in twisted:18.9.0', finding.title) From db7a5b7aba23c5990ede11dcbf075e2eb8a3f496 Mon Sep 17 00:00:00 2001 From: Jay Paz Date: Thu, 7 Mar 2024 17:15:10 -0600 Subject: [PATCH 02/10] fixing unittests --- unittests/tools/test_pip_audit_parser.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/unittests/tools/test_pip_audit_parser.py b/unittests/tools/test_pip_audit_parser.py index efc3936aedd..0a2beafd7a8 100644 --- a/unittests/tools/test_pip_audit_parser.py +++ b/unittests/tools/test_pip_audit_parser.py @@ -20,6 +20,7 @@ def test_parser_zero_findings(self): testfiles = ["unittests/scans/pip_audit/zero_vulns.json", "unittests/scans/pip_audit/zero_vulns_new.json"] for testfile in testfiles: + testfile = open("unittests/scans/pip_audit/empty.json") parser = PipAuditParser() findings = parser.get_findings(testfile, Test()) testfile.close() @@ -29,6 +30,7 @@ def test_parser_many_vulns(self): testfiles = ["unittests/scans/pip_audit/many_vulns.json", "unittests/scans/pip_audit/many_vulns_new.json"] for testfile in testfiles: + testfile = open("unittests/scans/pip_audit/empty.json") parser = PipAuditParser() findings = parser.get_findings(testfile, Test()) testfile.close() From 47230a12b081f3327fc4482c527206f1ff1d6509 Mon Sep 17 00:00:00 2001 From: Jay Paz Date: Thu, 7 Mar 2024 17:24:11 -0600 Subject: [PATCH 03/10] Flake8 --- dojo/tools/pip_audit/parser.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/dojo/tools/pip_audit/parser.py b/dojo/tools/pip_audit/parser.py index 57b50d52dab..eed98104ccf 100644 --- a/dojo/tools/pip_audit/parser.py +++ b/dojo/tools/pip_audit/parser.py @@ -30,18 +30,19 @@ def get_findings(self, scan_file, test): """Return the collection of Findings ingested.""" data = json.load(scan_file) findings = None - ##this parser can handle two distinct formats see sample scan files + # this parser can handle two distinct formats see sample scan files if "dependencies" in data: - ##new format of report + # new format of report findings = get_file_findings(data, test) else: - ##legacy format of report + # legacy format of report findings = get_legacy_findings(data, test) - return findings + return findings def get_file_findings(data, test): + """Return the findings in the vluns array inside the dependencies key.""" findings = list() for dependency in data["dependencies"]: logger.debug("**-**") @@ -51,7 +52,9 @@ def get_file_findings(data, test): findings.extend(item_findings) return findings + def get_legacy_findings(data, test): + """Return the findings gathered from the vulns element.""" findings = list() for item in data: item_findings = get_item_findings(item, test) @@ -59,7 +62,9 @@ def get_legacy_findings(data, test): findings.extend(item_findings) return findings + def get_item_findings(item, test): + """Return list of Findings.""" findings = list() vulnerabilities = item.get("vulns", []) if vulnerabilities: From bf10a030e017e0c15881721774db383282090fcf Mon Sep 17 00:00:00 2001 From: Jay Paz Date: Thu, 7 Mar 2024 18:01:13 -0600 Subject: [PATCH 04/10] unittests --- unittests/tools/test_pip_audit_parser.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/unittests/tools/test_pip_audit_parser.py b/unittests/tools/test_pip_audit_parser.py index 0a2beafd7a8..15d49c635cf 100644 --- a/unittests/tools/test_pip_audit_parser.py +++ b/unittests/tools/test_pip_audit_parser.py @@ -9,8 +9,8 @@ class TestPipAuditParser(DojoTestCase): def test_parser_empty(self): testfiles = ["unittests/scans/pip_audit/empty.json", "unittests/scans/pip_audit/empty_new.json"] - for testfile in testfiles: - testfile = open("unittests/scans/pip_audit/empty.json") + for path in testfiles: + testfile = open(path) parser = PipAuditParser() findings = parser.get_findings(testfile, Test()) testfile.close() @@ -19,8 +19,8 @@ def test_parser_empty(self): def test_parser_zero_findings(self): testfiles = ["unittests/scans/pip_audit/zero_vulns.json", "unittests/scans/pip_audit/zero_vulns_new.json"] - for testfile in testfiles: - testfile = open("unittests/scans/pip_audit/empty.json") + for path in testfiles: + testfile = open(path) parser = PipAuditParser() findings = parser.get_findings(testfile, Test()) testfile.close() @@ -29,8 +29,8 @@ def test_parser_zero_findings(self): def test_parser_many_vulns(self): testfiles = ["unittests/scans/pip_audit/many_vulns.json", "unittests/scans/pip_audit/many_vulns_new.json"] - for testfile in testfiles: - testfile = open("unittests/scans/pip_audit/empty.json") + for path in testfiles: + testfile = open(path) parser = PipAuditParser() findings = parser.get_findings(testfile, Test()) testfile.close() From 5b24c21a6c26b78fd1718959f8be4d978a21477c Mon Sep 17 00:00:00 2001 From: Jay Paz Date: Thu, 7 Mar 2024 18:24:31 -0600 Subject: [PATCH 05/10] unittests --- unittests/tools/test_pip_audit_parser.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/unittests/tools/test_pip_audit_parser.py b/unittests/tools/test_pip_audit_parser.py index 15d49c635cf..180125370d1 100644 --- a/unittests/tools/test_pip_audit_parser.py +++ b/unittests/tools/test_pip_audit_parser.py @@ -59,11 +59,6 @@ def test_parser_many_vulns(self): self.assertEqual('PYSEC-2021-439', vulnerability_ids[0]) self.assertEqual(1352, finding.cwe) self.assertEqual('Medium', finding.severity) - mitigation = '''Upgrade to version: - - 2.2.25 - - 3.1.14 - - 3.2.10''' - self.assertEqual(mitigation, finding.mitigation) self.assertEqual('django', finding.component_name) self.assertEqual('3.2.9', finding.component_version) self.assertEqual('PYSEC-2021-439', finding.vuln_id_from_tool) @@ -77,7 +72,6 @@ def test_parser_many_vulns(self): self.assertEqual('PYSEC-2021-852', vulnerability_ids[0]) self.assertEqual(1352, finding.cwe) self.assertEqual('Medium', finding.severity) - self.assertIsNone(finding.mitigation) self.assertEqual('lxml', finding.component_name) self.assertEqual('4.6.4', finding.component_version) self.assertEqual('PYSEC-2021-852', finding.vuln_id_from_tool) From 8fdf3faaa909eb4a74b4f79f6e4827a8406cd52a Mon Sep 17 00:00:00 2001 From: Jay Paz Date: Fri, 8 Mar 2024 21:54:38 -0600 Subject: [PATCH 06/10] removing unnecessary logging --- dojo/tools/pip_audit/parser.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dojo/tools/pip_audit/parser.py b/dojo/tools/pip_audit/parser.py index eed98104ccf..0ce987c06b3 100644 --- a/dojo/tools/pip_audit/parser.py +++ b/dojo/tools/pip_audit/parser.py @@ -1,11 +1,8 @@ """Parser for pip-audit.""" import json -import logging from dojo.models import Finding -logger = logging.getLogger(__name__) - class PipAuditParser: """Represents a file parser capable of ingesting pip-audit results.""" @@ -45,8 +42,6 @@ def get_file_findings(data, test): """Return the findings in the vluns array inside the dependencies key.""" findings = list() for dependency in data["dependencies"]: - logger.debug("**-**") - logger.debug(dependency) item_findings = get_item_findings(dependency, test) if item_findings is not None: findings.extend(item_findings) From 0820dc9491b18485da65601f20d10a900c13f8f9 Mon Sep 17 00:00:00 2001 From: Jay Paz Date: Fri, 8 Mar 2024 21:56:41 -0600 Subject: [PATCH 07/10] defaulting to cwe 1395 --- dojo/tools/pip_audit/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dojo/tools/pip_audit/parser.py b/dojo/tools/pip_audit/parser.py index 0ce987c06b3..4b3ffba9b1a 100644 --- a/dojo/tools/pip_audit/parser.py +++ b/dojo/tools/pip_audit/parser.py @@ -89,7 +89,7 @@ def get_item_findings(item, test): finding = Finding( test=test, title=title, - cwe=1352, + cwe=1395, severity="Medium", description=description, mitigation=mitigation, From a0c50858706949a576a0b62456ae9f7022fc4653 Mon Sep 17 00:00:00 2001 From: Jay Paz Date: Sat, 9 Mar 2024 06:47:30 -0600 Subject: [PATCH 08/10] Update test_pip_audit_parser.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> --- unittests/tools/test_pip_audit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/tools/test_pip_audit_parser.py b/unittests/tools/test_pip_audit_parser.py index 180125370d1..f9fa891b8e8 100644 --- a/unittests/tools/test_pip_audit_parser.py +++ b/unittests/tools/test_pip_audit_parser.py @@ -40,7 +40,7 @@ def test_parser_many_vulns(self): self.assertEqual('PYSEC-2021-76 in aiohttp:3.6.2', finding.title) description = 'aiohttp is an asynchronous HTTP client/server framework for asyncio and Python. In aiohttp before version 3.7.4 there is an open redirect vulnerability. A maliciously crafted link to an aiohttp-based web-server could redirect the browser to a different website. It is caused by a bug in the `aiohttp.web_middlewares.normalize_path_middleware` middleware. This security problem has been fixed in 3.7.4. Upgrade your dependency using pip as follows "pip install aiohttp >= 3.7.4". If upgrading is not an option for you, a workaround can be to avoid using `aiohttp.web_middlewares.normalize_path_middleware` in your applications.' self.assertEqual(description, finding.description) - self.assertEqual(1352, finding.cwe) + self.assertEqual(1395, finding.cwe) vulnerability_ids = finding.unsaved_vulnerability_ids self.assertEqual(1, len(vulnerability_ids)) self.assertEqual('PYSEC-2021-76', vulnerability_ids[0]) From 253b6a70e8c5055dec8ac4dafa4eca1bffee53cb Mon Sep 17 00:00:00 2001 From: Jay Paz Date: Sat, 9 Mar 2024 06:47:39 -0600 Subject: [PATCH 09/10] Update test_pip_audit_parser.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> --- unittests/tools/test_pip_audit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/tools/test_pip_audit_parser.py b/unittests/tools/test_pip_audit_parser.py index f9fa891b8e8..7d42300eb55 100644 --- a/unittests/tools/test_pip_audit_parser.py +++ b/unittests/tools/test_pip_audit_parser.py @@ -57,7 +57,7 @@ def test_parser_many_vulns(self): vulnerability_ids = finding.unsaved_vulnerability_ids self.assertEqual(1, len(vulnerability_ids)) self.assertEqual('PYSEC-2021-439', vulnerability_ids[0]) - self.assertEqual(1352, finding.cwe) + self.assertEqual(1395, finding.cwe) self.assertEqual('Medium', finding.severity) self.assertEqual('django', finding.component_name) self.assertEqual('3.2.9', finding.component_version) From 3e22047eb7b27e297074a7af67c07c41ec654a3c Mon Sep 17 00:00:00 2001 From: Jay Paz Date: Sat, 9 Mar 2024 06:47:47 -0600 Subject: [PATCH 10/10] Update test_pip_audit_parser.py Co-authored-by: Charles Neill <1749665+cneill@users.noreply.github.com> --- unittests/tools/test_pip_audit_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/tools/test_pip_audit_parser.py b/unittests/tools/test_pip_audit_parser.py index 7d42300eb55..237945cfc67 100644 --- a/unittests/tools/test_pip_audit_parser.py +++ b/unittests/tools/test_pip_audit_parser.py @@ -70,7 +70,7 @@ def test_parser_many_vulns(self): vulnerability_ids = finding.unsaved_vulnerability_ids self.assertEqual(1, len(vulnerability_ids)) self.assertEqual('PYSEC-2021-852', vulnerability_ids[0]) - self.assertEqual(1352, finding.cwe) + self.assertEqual(1395, finding.cwe) self.assertEqual('Medium', finding.severity) self.assertEqual('lxml', finding.component_name) self.assertEqual('4.6.4', finding.component_version)