diff --git a/docs/content/en/integrations/parsers/file/humble.md b/docs/content/en/integrations/parsers/file/humble.md new file mode 100644 index 00000000000..56c3f73b52e --- /dev/null +++ b/docs/content/en/integrations/parsers/file/humble.md @@ -0,0 +1,6 @@ +--- +title: "Humble Report" +toc_hide: true +--- +Import JSON report of the Humble scanner + \ No newline at end of file diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index 4f0716a5549..25740feb409 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -1260,6 +1260,7 @@ def saml2_attrib_map_format(dict): 'KubeHunter Scan': ['title', 'description'], 'kube-bench Scan': ['title', 'vuln_id_from_tool', 'description'], 'Threagile risks report': ['title', 'cwe', "severity"], + 'Humble Json Importer': ['title'], } # Override the hardcoded settings here via the env var @@ -1457,6 +1458,7 @@ def saml2_attrib_map_format(dict): 'KubeHunter Scan': DEDUPE_ALGO_HASH_CODE, 'kube-bench Scan': DEDUPE_ALGO_HASH_CODE, 'Threagile risks report': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE, + 'Humble Json Importer': DEDUPE_ALGO_HASH_CODE, } # Override the hardcoded settings here via the env var diff --git a/dojo/tools/humble/__init__.py b/dojo/tools/humble/__init__.py new file mode 100644 index 00000000000..99e8e118c6a --- /dev/null +++ b/dojo/tools/humble/__init__.py @@ -0,0 +1 @@ +__author__ = "manuel_sommer" diff --git a/dojo/tools/humble/parser.py b/dojo/tools/humble/parser.py new file mode 100644 index 00000000000..689ce080187 --- /dev/null +++ b/dojo/tools/humble/parser.py @@ -0,0 +1,61 @@ +import json +from dojo.models import Finding, Endpoint + + +class HumbleParser(object): + """Humble (https://github.com/rfc-st/humble)""" + + def get_scan_types(self): + return ["Humble Json Importer"] + + def get_label_for_scan_types(self, scan_type): + return scan_type # no custom label for now + + def get_description_for_scan_types(self, scan_type): + return "JSON output of Humble scan." + + def get_findings(self, filename, test): + items = [] + try: + data = json.load(filename) + except ValueError as err: + data = {} + if data != {}: + url = data['[0. Info]']['URL'] + for content in data['[1. Missing HTTP Security Headers]']: + if content != "Nothing to report, all seems OK!": + finding = Finding(title="Missing header: " + str(content), + description="This security Header is missing: " + content, + severity="Medium", + static_finding=False, + dynamic_finding=True) + items.append(finding) + finding.unsaved_endpoints = [Endpoint.from_uri(url)] + for content in data['[2. Fingerprint HTTP Response Headers]']: + if content != "Nothing to report, all seems OK!": + finding = Finding(title="Available fingerprint:" + str(content), + description="This fingerprint HTTP Response Header is available. Please remove it: " + content, + severity="Medium", + static_finding=False, + dynamic_finding=True) + items.append(finding) + finding.unsaved_endpoints = [Endpoint.from_uri(url)] + for content in data['[3. Deprecated HTTP Response Headers/Protocols and Insecure Values]']: + if content != "Nothing to report, all seems OK!": + finding = Finding(title="Deprecated header: " + str(content), + description="This deprecated HTTP Response Header is available. Please remove it: " + content, + severity="Medium", + static_finding=False, + dynamic_finding=True) + items.append(finding) + finding.unsaved_endpoints = [Endpoint.from_uri(url)] + for content in data['[4. Empty HTTP Response Headers Values]']: + if content != "Nothing to report, all seems OK!": + finding = Finding(title="Empty HTTP response header: " + str(content), + description="This empty HTTP Response Header value is available. Please remove it: " + content, + severity="Medium", + static_finding=False, + dynamic_finding=True) + items.append(finding) + finding.unsaved_endpoints = [Endpoint.from_uri(url)] + return items diff --git a/unittests/scans/humble/many_findings.json b/unittests/scans/humble/many_findings.json new file mode 100644 index 00000000000..82a81611939 --- /dev/null +++ b/unittests/scans/humble/many_findings.json @@ -0,0 +1,54 @@ +{ + "[0. Info]": { + "Date": "2023/11/13 - 09:20:17", + "URL": "https://asdf.asf.hs" + }, + "[HTTP Response Headers]": { + "Cache-Control": "no-store, no-cache, must-revalidate, post-check=0, pre-check=0", + "Connection": "Keep-Alive", + "Content-Security-Policy": "script-src 'self';", + "Content-Type": "text/html; charset=utf-8", + "Date": "Mon, 13 Nov 2023 08:20:19 GMT", + "Expires": "Wed, 17 Aug 2005 00:00:00 GMT", + "Keep-Alive": "timeout=5, max=100", + "Last-Modified": "Mon, 13 Nov 2023 08:20:19 GMT", + "Permissions-Policy": "interest-cohort=()", + "Pragma": "no-cache", + "Referrer-Policy": "strict-origin", + "Strict-Transport-Security": "max-age=31536000; includeSubDomain$", + "Transfer-Encoding": "chunked", + "Vary": "Accept-Encoding", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "sameorigin", + "X-XSS-Protection": "1; mode=block" + }, + "[1. Missing HTTP Security Headers]": [ + "Clear-Site-Data", + "Cross-Origin-Embedder-Policy", + "Cross-Origin-Opener-Policy", + "Cross-Origin-Resource-Policy", + "NEL", + "X-Permitted-Cross-Domain-Policies" + ], + "[2. Fingerprint HTTP Response Headers]": [ + "Nothing to report, all seems OK!" + ], + "[3. Deprecated HTTP Response Headers/Protocols and Insecure Values]": [ + "Pragma (Deprecated Header)", + "Strict-Transport-Security (Recommended Values)", + "X-XSS-Protection (Unsafe Value)" + ], + "[4. Empty HTTP Response Headers Values]": [ + "Nothing to report, all seems OK!" + ], + "[5. Browser Compatibility for Enabled HTTP Security Headers]": { + "Cache-Control": "https://caniuse.com/?search=Cache-Control", + "Content-Type": "https://caniuse.com/?search=Content-Type", + "Content-Security-Policy": "https://caniuse.com/?search=contentsecuritypolicy2", + "Permissions-Policy": "https://caniuse.com/?search=Permissions-Policy", + "Referrer-Policy": "https://caniuse.com/?search=Referrer-Policy", + "Strict-Transport-Security": "https://caniuse.com/?search=Strict-Transport-Security", + "X-Content-Type-Options": "https://caniuse.com/?search=X-Content-Type-Options", + "X-Frame-Options": "https://caniuse.com/?search=X-Frame-Options" + } +} \ No newline at end of file diff --git a/unittests/scans/humble/many_findings2.json b/unittests/scans/humble/many_findings2.json new file mode 100644 index 00000000000..68f60db55a9 --- /dev/null +++ b/unittests/scans/humble/many_findings2.json @@ -0,0 +1,49 @@ +{ + "[0. Info]": { + "Date": "2023/11/15 - 08:42:38", + "URL": "http://testestset.com" + }, + "[HTTP Response Headers]": { + "CF-Cache-Status": "DYNAMIC", + "CF-RAY": "8265dbd49d362bde-FRA", + "Cache-Control": "no-store, private", + "Connection": "keep-alive", + "Content-Encoding": "gzip", + "Content-Type": "text/html; charset=UTF-8", + "Date": "Wed, 15 Nov 2023 07:42:39 GMT", + "Transfer-Encoding": "chunked", + "Vary": "Accept-Encoding", + "X-Content-Type-Options": "nosniff", + "X-UA-Compatible": "IE=edge" + }, + "[1. Missing HTTP Security Headers]": [ + "Clear-Site-Data", + "Cross-Origin-Embedder-Policy", + "Cross-Origin-Opener-Policy", + "Cross-Origin-Resource-Policy", + "Content-Security-Policy", + "NEL", + "Permissions-Policy", + "Referrer-Policy", + "Strict-Transport-Security", + "X-Permitted-Cross-Domain-Policies", + "X-Frame-Options" + ], + "[2. Fingerprint HTTP Response Headers]": [ + "Cf-Cache-Status", + "Cf-Ray", + "Server" + ], + "[3. Deprecated HTTP Response Headers/Protocols and Insecure Values]": [ + "Cache-Control (Recommended Values)", + "X-UA-compatible (Deprecated Header)" + ], + "[4. Empty HTTP Response Headers Values]": [ + "Nothing to report, all seems OK!" + ], + "[5. Browser Compatibility for Enabled HTTP Security Headers]": { + "Cache-Control": "https://caniuse.com/?search=Cache-Control", + "Content-Type": "https://caniuse.com/?search=Content-Type", + "X-Content-Type-Options": "https://caniuse.com/?search=X-Content-Type-Options" + } +} \ No newline at end of file diff --git a/unittests/tools/test_humble_parser.py b/unittests/tools/test_humble_parser.py new file mode 100644 index 00000000000..ccd99d44373 --- /dev/null +++ b/unittests/tools/test_humble_parser.py @@ -0,0 +1,36 @@ +from dojo.tools.humble.parser import HumbleParser +from dojo.models import Test +from unittests.dojo_test_case import DojoTestCase + + +class TestHumbleParser(DojoTestCase): + def test_humble_parser_with_many_findings(self): + testfile = open("unittests/scans/humble/many_findings.json") + parser = HumbleParser() + findings = parser.get_findings(testfile, Test()) + for finding in findings: + for endpoint in finding.unsaved_endpoints: + endpoint.clean() + testfile.close() + self.assertEqual(9, len(findings)) + finding = findings[0] + self.assertEqual(finding.unsaved_endpoints[0].host, "asdf.asf.hs") + self.assertEqual("Missing header: Clear-Site-Data", finding.title) + finding = findings[7] + self.assertEqual("Deprecated header: Strict-Transport-Security (Recommended Values)", finding.title) + + def test_humble_parser_with_many_findings2(self): + testfile = open("unittests/scans/humble/many_findings2.json") + parser = HumbleParser() + findings = parser.get_findings(testfile, Test()) + for finding in findings: + for endpoint in finding.unsaved_endpoints: + endpoint.clean() + testfile.close() + self.assertEqual(16, len(findings)) + finding = findings[0] + self.assertEqual(finding.unsaved_endpoints[0].host, "testestset.com") + self.assertEqual("Missing header: Clear-Site-Data", finding.title) + finding = findings[7] + self.assertEqual("Missing header: Referrer-Policy", finding.title) + self.assertEqual("This security Header is missing: Referrer-Policy", finding.description)