From 06c16e7825145ec67c91f5d4246a70f89e272699 Mon Sep 17 00:00:00 2001 From: kagahd Date: Wed, 5 Jun 2024 10:51:16 +0200 Subject: [PATCH 01/27] add prowler v4 parser --- .../parsers/file/aws_prowler_v4.md | 102 ++++++++ dojo/tools/aws_prowler_v4/__init__.py | 0 dojo/tools/aws_prowler_v4/parser.py | 109 ++++++++ unittests/scans/aws_prowler_v4/many_vuln.json | 247 ++++++++++++++++++ unittests/scans/aws_prowler_v4/no_vuln.json | 1 + unittests/scans/aws_prowler_v4/one_vuln.json | 80 ++++++ unittests/tools/test_aws_prowler_v4_parser.py | 40 +++ 7 files changed, 579 insertions(+) create mode 100644 docs/content/en/integrations/parsers/file/aws_prowler_v4.md create mode 100644 dojo/tools/aws_prowler_v4/__init__.py create mode 100644 dojo/tools/aws_prowler_v4/parser.py create mode 100644 unittests/scans/aws_prowler_v4/many_vuln.json create mode 100644 unittests/scans/aws_prowler_v4/no_vuln.json create mode 100644 unittests/scans/aws_prowler_v4/one_vuln.json create mode 100644 unittests/tools/test_aws_prowler_v4_parser.py diff --git a/docs/content/en/integrations/parsers/file/aws_prowler_v4.md b/docs/content/en/integrations/parsers/file/aws_prowler_v4.md new file mode 100644 index 00000000000..77f8ff10aad --- /dev/null +++ b/docs/content/en/integrations/parsers/file/aws_prowler_v4.md @@ -0,0 +1,102 @@ +--- +title: "AWS Prowler V3" +toc_hide: true +--- + +### File Types +DefectDojo parser accepts a .json-ocsf file. Please note: earlier versions of AWS Prowler create output data in a different format. See our other documentation if you are using an earlier version of AWS Prowler: +* [Prowler v2](https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler/) +* [Prowler v3](https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler_v3/) + +JSON reports can be created from the [AWS Prowler V4 CLI](https://docs.prowler.cloud/en/latest/tutorials/reporting/#json) using the following command: `prowler -M json-ocsf` + +### Acceptable JSON Format +The parser expects an array of assessments. All properties are strings and are required by the parser. + +~~~ + +[{ + "metadata": { + "event_code": "iam_role_administratoraccess_policy_permissive_trust_relationship", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role myAdministratorExecutionRole has AdministratorAccess policy attached that has too permissive trust relationship.", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": {} + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "product_uid": "prowler", + "title": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "uid": "prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [], + "name": "myAdministratorExecutionRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/myAdministratorExecutionRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege. If a role really needs AdministratorAccess, the trust relationship must be well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "references": [ + "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" + ] + }, + "risk_details": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It is therefore particularly important that the trust relationship is well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "type_uid": 200401, + "type_name": "Create" +}] + +~~~ + +### Sample Scan Data +Unit tests of AWS Prowler V4 JSON-OCSF can be found at https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/aws_prowler_v4. \ No newline at end of file diff --git a/dojo/tools/aws_prowler_v4/__init__.py b/dojo/tools/aws_prowler_v4/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dojo/tools/aws_prowler_v4/parser.py b/dojo/tools/aws_prowler_v4/parser.py new file mode 100644 index 00000000000..f8298104cc1 --- /dev/null +++ b/dojo/tools/aws_prowler_v4/parser.py @@ -0,0 +1,109 @@ + +import hashlib +import json +import textwrap +from datetime import date + +from dojo.models import Finding + + +class AWSProwlerV4Parser: + SCAN_TYPE = ["AWS Prowler V4"] + + def get_scan_types(self): + return AWSProwlerV4Parser.SCAN_TYPE + + def get_label_for_scan_types(self, scan_type): + return AWSProwlerV4Parser.SCAN_TYPE[0] + + def get_description_for_scan_types(self, scan_type): + return "Export of AWS Prowler V4 JSON OCSF v1.1.0 format." + + def get_findings(self, file, test): + if file.name.lower().endswith('.json'): + return self.process_json(file, test) + else: + msg = 'Unknown file format' + raise ValueError(msg) + + def process_json(self, file, test): + dupes = {} + + data = json.load(file) + # mapping of json fields between Prowler v3 and v4: + # https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/reporting/#json + for deserialized in data: + + status = deserialized.get('status_code') + if status.upper() != 'FAIL': + continue + + account_id = deserialized.get('cloud', {}).get('account', {}).get("uid", '') + region = deserialized.get('resources', [{}])[0].get('region', '') + provider = deserialized.get('cloud', {}).get('provider', '') + compliance = '' + compliance_field = deserialized.get('unmapped', {}).get("compliance", {}) + if compliance_field: + compliance = ' | '.join([f"{key}:{','.join(value)}" for key, value in compliance_field.items()]) + result_extended = deserialized.get('status_detail') + general_description = deserialized.get('finding_info', {}).get('desc', '') + asff_compliance_type = deserialized.get('unmapped', {}).get('check_type', '') + severity = deserialized.get('severity', 'Info').capitalize() + aws_service_name = deserialized.get('resources', [{}])[0].get('group', {}).get('name', '') + impact = deserialized.get('risk_details') + mitigation = deserialized.get('remediation', {}).get("desc", '') + documentation = deserialized.get('remediation', {}).get("references", '') + documentation = str(documentation) + "\n" + str(deserialized.get('unmapped', {}).get('related_url', '')) + security_domain = deserialized.get('resources', [{}])[0].get('type', '') + timestamp = deserialized.get("event_time") + resource_arn = deserialized.get('resources', [{}])[0].get('uid', '') + resource_id = deserialized.get('resources', [{}])[0].get('name', '') + unique_id_from_tool = deserialized.get('finding_info', {}).get('uid', '') + if not resource_arn or resource_arn == "": + component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) + else: + component_name = resource_arn + + description = "**Issue:** " + str(result_extended) + \ + "\n**Description:** " + str(general_description) + \ + "\n**AWS Account:** " + str(account_id) + \ + "\n**Region:** " + str(region) + \ + "\n**AWS Service:** " + str(aws_service_name) + \ + "\n**Security Domain:** " + str(security_domain) + \ + "\n**Compliance:** " + str(compliance) + \ + "\n**ASFF Compliance Type:** " + str(asff_compliance_type) + + + # improving key to get duplicates + dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() + if dupe_key in dupes: + find = dupes[dupe_key] + if description is not None: + find.description += description + "\n\n" + find.nb_occurences += 1 + else: + find = Finding( + title=textwrap.shorten(result_extended, 150), + cwe=1032, # Security Configuration Weaknesses, would like to fine tune + test=test, + description=description, + component_name=component_name, + unique_id_from_tool=unique_id_from_tool, + severity=severity, + references=documentation, + date=date.fromisoformat(timestamp[:10]), + static_finding=True, + dynamic_finding=False, + nb_occurences=1, + mitigation=mitigation, + impact=impact, + ) + dupes[dupe_key] = find + + return list(dupes.values()) + + def formatview(self, depth): + if depth > 1: + return "* " + else: + return "" diff --git a/unittests/scans/aws_prowler_v4/many_vuln.json b/unittests/scans/aws_prowler_v4/many_vuln.json new file mode 100644 index 00000000000..e0f563c2a05 --- /dev/null +++ b/unittests/scans/aws_prowler_v4/many_vuln.json @@ -0,0 +1,247 @@ +[{ + "metadata": { + "event_code": "iam_role_administratoraccess_policy_permissive_trust_relationship", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role myAdministratorExecutionRole has AdministratorAccess policy attached that has too permissive trust relationship.", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": {} + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "product_uid": "prowler", + "title": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "uid": "prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [], + "name": "myAdministratorExecutionRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/myAdministratorExecutionRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege. If a role really needs AdministratorAccess, the trust relationship must be well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "references": [ + "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" + ] + }, + "risk_details": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It is therefore particularly important that the trust relationship is well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "type_uid": 200401, + "type_name": "Create" +},{ + "metadata": { + "event_code": "iam_role_cross_account_readonlyaccess_policy", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role AuditRole gives cross account read-only access.", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#awsmp_readonlyaccess", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": { + "MITRE-ATTACK": [ + "T1078" + ], + "AWS-Foundational-Technical-Review": [ + "IAM-0012" + ] + } + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts", + "product_uid": "prowler", + "title": "Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts", + "uid": "prowler-aws-iam_role_cross_account_readonlyaccess_policy-123456789012-us-east-1-AuditRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [ + "some-label=some value" + ], + "name": "AuditRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/AuditRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Remove the AWS-managed ReadOnlyAccess policy from all roles that have a trust policy, including third-party cloud accounts, or remove third-party cloud accounts from the trust policy of all roles that need the ReadOnlyAccess policy.", + "references": [ + "https://docs.securestate.vmware.com/rule-docs/aws-iam-role-cross-account-readonlyaccess-policy" + ] + }, + "risk_details": "The AWS-managed ReadOnlyAccess policy is highly potent and exposes the customer to a significant data leakage threat. It should be granted very conservatively. For granting access to 3rd party vendors, consider using alternative managed policies, such as ViewOnlyAccess or SecurityAudit.", + "type_uid": 200401, + "type_name": "Create" +},{ + "metadata": { + "event_code": "iam_role_permissive_trust_relationship", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role CrossAccountResourceAccessRole has permissive trust relationship to other accounts", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html#principal-accounts", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": {} + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles do not allow assume role from any role of a cross account", + "product_uid": "prowler", + "title": "Ensure IAM Roles do not allow assume role from any role of a cross account", + "uid": "prowler-aws-iam_role_permissive_trust_relationship-123456789012-us-east-1-CrossAccountResourceAccessRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [], + "name": "CrossAccountResourceAccessRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/CrossAccountResourceAccessRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Ensure IAM Roles do not allow assume role from any role of a cross account but only from specific roles of specific accounts.", + "references": [ + "https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html#principal-roles" + ] + }, + "risk_details": "If an IAM role allows assume role from any role of a cross account, it can lead to privilege escalation.", + "type_uid": 200401, + "type_name": "Create" +}] \ No newline at end of file diff --git a/unittests/scans/aws_prowler_v4/no_vuln.json b/unittests/scans/aws_prowler_v4/no_vuln.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/unittests/scans/aws_prowler_v4/no_vuln.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/unittests/scans/aws_prowler_v4/one_vuln.json b/unittests/scans/aws_prowler_v4/one_vuln.json new file mode 100644 index 00000000000..5e45f2077e3 --- /dev/null +++ b/unittests/scans/aws_prowler_v4/one_vuln.json @@ -0,0 +1,80 @@ +[{ + "metadata": { + "event_code": "iam_role_administratoraccess_policy_permissive_trust_relationship", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role myAdministratorExecutionRole has AdministratorAccess policy attached that has too permissive trust relationship.", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": {} + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "product_uid": "prowler", + "title": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "uid": "prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [], + "name": "myAdministratorExecutionRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/myAdministratorExecutionRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege. If a role really needs AdministratorAccess, the trust relationship must be well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "references": [ + "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" + ] + }, + "risk_details": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It is therefore particularly important that the trust relationship is well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "type_uid": 200401, + "type_name": "Create" +}] \ No newline at end of file diff --git a/unittests/tools/test_aws_prowler_v4_parser.py b/unittests/tools/test_aws_prowler_v4_parser.py new file mode 100644 index 00000000000..74e97050f6e --- /dev/null +++ b/unittests/tools/test_aws_prowler_v4_parser.py @@ -0,0 +1,40 @@ +from dojo.models import Test +from dojo.tools.aws_prowler_v4.parser import AWSProwlerV4Parser + +from ..dojo_test_case import DojoTestCase + + +class TestAwsProwlerV4Parser(DojoTestCase): + def setup(self, testfile): + parser = AWSProwlerV4Parser() + findings = parser.get_findings(testfile, Test()) + testfile.close() + return findings + + def test_aws_prowler_parser_with_no_vuln_has_no_findings_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v4/no_vuln.json")) + self.assertEqual(0, len(findings)) + + def test_aws_prowler_parser_with_critical_vuln_has_one_findings_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v4/one_vuln.json")) + self.assertEqual(1, len(findings)) + self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) + self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) + self.assertEqual("arn:aws:iam::123456789012:role/myAdministratorExecutionRole", findings[0].component_name) + self.assertIn('https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege', findings[0].references) + + def test_aws_prowler_parser_with_many_vuln_has_many_findings_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v4/many_vuln.json")) + self.assertEqual(3, len(findings)) + with self.subTest(i=0): + self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) + self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) + with self.subTest(i=1): + self.assertEqual("prowler-aws-iam_role_cross_account_readonlyaccess_policy-123456789012-us-east-1-AuditRole", findings[1].unique_id_from_tool) + self.assertIn('Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts', findings[1].description) + with self.subTest(i=3): + self.assertEqual("prowler-aws-iam_role_permissive_trust_relationship-123456789012-us-east-1-CrossAccountResourceAccessRole", findings[2].unique_id_from_tool) + self.assertIn('Ensure IAM Roles do not allow assume role from any role of a cross account', findings[2].description) From b76f19b6a6c96ba97f74203f876891b7761a3fb1 Mon Sep 17 00:00:00 2001 From: kagahd Date: Wed, 5 Jun 2024 10:55:03 +0200 Subject: [PATCH 02/27] remove line --- dojo/tools/aws_prowler_v4/parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dojo/tools/aws_prowler_v4/parser.py b/dojo/tools/aws_prowler_v4/parser.py index f8298104cc1..54ae4e092b0 100644 --- a/dojo/tools/aws_prowler_v4/parser.py +++ b/dojo/tools/aws_prowler_v4/parser.py @@ -73,7 +73,6 @@ def process_json(self, file, test): "\n**Compliance:** " + str(compliance) + \ "\n**ASFF Compliance Type:** " + str(asff_compliance_type) - # improving key to get duplicates dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() if dupe_key in dupes: From 01b94991eaaccab9c8070b3a9f31e00ee2e5ad12 Mon Sep 17 00:00:00 2001 From: kagahd Date: Wed, 5 Jun 2024 14:24:14 +0200 Subject: [PATCH 03/27] fix typo --- docs/content/en/integrations/parsers/file/aws_prowler_v4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/integrations/parsers/file/aws_prowler_v4.md b/docs/content/en/integrations/parsers/file/aws_prowler_v4.md index 77f8ff10aad..aada1887ae1 100644 --- a/docs/content/en/integrations/parsers/file/aws_prowler_v4.md +++ b/docs/content/en/integrations/parsers/file/aws_prowler_v4.md @@ -1,5 +1,5 @@ --- -title: "AWS Prowler V3" +title: "AWS Prowler V4" toc_hide: true --- From 77e7e067828b1f68d9069d5d796e9931db5816c1 Mon Sep 17 00:00:00 2001 From: kagahd Date: Wed, 5 Jun 2024 16:30:16 +0200 Subject: [PATCH 04/27] add settings.dist.py although it's written that one should not touch it but use env vars --- dojo/settings/settings.dist.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index b63bdc0f705..7363505a60e 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -1293,6 +1293,7 @@ def saml2_attrib_map_format(dict): 'Anchore Grype': True, 'AWS Prowler Scan': True, 'AWS Prowler V3': True, + 'AWS Prowler V4': True, 'Checkmarx Scan': False, 'Checkmarx OSA': True, 'Cloudsploit Scan': True, @@ -1378,6 +1379,7 @@ def saml2_attrib_map_format(dict): 'AuditJS Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'AWS Prowler Scan': DEDUPE_ALGO_HASH_CODE, 'AWS Prowler V3': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, + 'AWS Prowler V4': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, "AWS Security Finding Format (ASFF) Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Burp REST API': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Bandit Scan': DEDUPE_ALGO_HASH_CODE, From 1851d73525cc33697ef32e290ae0835aa928e687 Mon Sep 17 00:00:00 2001 From: kagahd Date: Thu, 6 Jun 2024 09:37:13 +0200 Subject: [PATCH 05/27] add modified .settings.dist.py.sha256sum --- dojo/settings/.settings.dist.py.sha256sum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dojo/settings/.settings.dist.py.sha256sum b/dojo/settings/.settings.dist.py.sha256sum index c0de37b5fe3..8870b37793c 100644 --- a/dojo/settings/.settings.dist.py.sha256sum +++ b/dojo/settings/.settings.dist.py.sha256sum @@ -1 +1 @@ -c0f6db3774e94fcfd22a5861d47e42cdd52839d01dd99ce361ecfd44f42b221c +a7b0e9897d9fe41abaad45297dab541ac687715ac9fd44a8b01805c237637c15 From 3efc6e5bd235e342d25443af34a2d69b9e86e768 Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 14:22:40 +0200 Subject: [PATCH 06/27] extend prowler v3 parser to parse also prowler v4 reports in oscf-json format --- docker-compose.yml | 7 ++ dojo/tools/aws_prowler_v3/parser.py | 82 ++++++++++++- dojo/tools/aws_prowler_v4/__init__.py | 0 dojo/tools/aws_prowler_v4/parser.py | 108 ------------------ .../many_vuln.ocsf.json} | 0 .../no_vuln.ocsf.json} | 0 .../one_vuln.ocsf.json} | 0 unittests/tools/test_aws_prowler_v3_parser.py | 28 +++++ unittests/tools/test_aws_prowler_v4_parser.py | 40 ------- 9 files changed, 115 insertions(+), 150 deletions(-) delete mode 100644 dojo/tools/aws_prowler_v4/__init__.py delete mode 100644 dojo/tools/aws_prowler_v4/parser.py rename unittests/scans/{aws_prowler_v4/many_vuln.json => aws_prowler_v3/many_vuln.ocsf.json} (100%) rename unittests/scans/{aws_prowler_v4/no_vuln.json => aws_prowler_v3/no_vuln.ocsf.json} (100%) rename unittests/scans/{aws_prowler_v4/one_vuln.json => aws_prowler_v3/one_vuln.ocsf.json} (100%) delete mode 100644 unittests/tools/test_aws_prowler_v4_parser.py diff --git a/docker-compose.yml b/docker-compose.yml index 80e2d783f7c..6e4a6c22f47 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,6 +54,9 @@ services: DD_CELERY_BROKER_URL: ${DD_CELERY_BROKER_URL} DD_SECRET_KEY: "${DD_SECRET_KEY:-hhZCp@D28z!n@NED*yB!ROMt+WzsY*iq}" DD_CREDENTIAL_AES_256_KEY: "${DD_CREDENTIAL_AES_256_KEY:-&91a*agLqesc*0DJ+2*bAbsUZfR*4nLw}" + DD_HASHCODE_FIELDS_PER_SCANNER: '{"AWS Prowler Scan": [ "cwe", "severity", "description" ], "AWS Prowler V3": [ "unique_id_from_tool" ]}' + DD_DEDUPLICATION_ALGORITHM_PER_PARSER: '{ "AWS Prowler V3": "unique_id_from_tool"}' + volumes: - type: bind source: ./docker/extra_settings @@ -75,6 +78,8 @@ services: DD_CELERY_BROKER_URL: ${DD_CELERY_BROKER_URL} DD_SECRET_KEY: "${DD_SECRET_KEY:-hhZCp@D28z!n@NED*yB!ROMt+WzsY*iq}" DD_CREDENTIAL_AES_256_KEY: "${DD_CREDENTIAL_AES_256_KEY:-&91a*agLqesc*0DJ+2*bAbsUZfR*4nLw}" + DD_HASHCODE_FIELDS_PER_SCANNER: '{"AWS Prowler Scan": [ "cwe", "severity", "description" ], "AWS Prowler V3": [ "unique_id_from_tool" ] }' + DD_DEDUPLICATION_ALGORITHM_PER_PARSER: '{ "AWS Prowler V3": "unique_id_from_tool" }' volumes: - type: bind source: ./docker/extra_settings @@ -95,6 +100,8 @@ services: DD_CELERY_BROKER_URL: ${DD_CELERY_BROKER_URL} DD_SECRET_KEY: "${DD_SECRET_KEY:-hhZCp@D28z!n@NED*yB!ROMt+WzsY*iq}" DD_CREDENTIAL_AES_256_KEY: "${DD_CREDENTIAL_AES_256_KEY:-&91a*agLqesc*0DJ+2*bAbsUZfR*4nLw}" + DD_HASHCODE_FIELDS_PER_SCANNER: '{"AWS Prowler Scan": [ "cwe", "severity", "description" ], "AWS Prowler V3": [ "unique_id_from_tool" ] }' + DD_DEDUPLICATION_ALGORITHM_PER_PARSER: '{ "AWS Prowler V3": "unique_id_from_tool" }' volumes: - type: bind source: ./docker/extra_settings diff --git a/dojo/tools/aws_prowler_v3/parser.py b/dojo/tools/aws_prowler_v3/parser.py index 60f7a5dc7e8..5796677766f 100644 --- a/dojo/tools/aws_prowler_v3/parser.py +++ b/dojo/tools/aws_prowler_v3/parser.py @@ -17,15 +17,93 @@ def get_label_for_scan_types(self, scan_type): return AWSProwlerV3Parser.SCAN_TYPE[0] def get_description_for_scan_types(self, scan_type): - return "Export of AWS Prowler JSON V3 format." + return "Exports from AWS Prowler v3 in JSON format or from Prowler v4 in OCSF-JSON format." def get_findings(self, file, test): - if file.name.lower().endswith('.json'): + if file.name.lower().endswith('.ocsf.json'): + return self.process_ocsf_json(file, test) + elif file.name.lower().endswith('.json'): return self.process_json(file, test) else: msg = 'Unknown file format' raise ValueError(msg) + + def process_ocsf_json(self, file, test): + dupes = {} + + data = json.load(file) + # mapping of json fields between Prowler v3 and v4: + # https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/reporting/#json + for deserialized in data: + + status = deserialized.get('status_code') + if status.upper() != 'FAIL': + continue + + account_id = deserialized.get('cloud', {}).get('account', {}).get("uid", '') + region = deserialized.get('resources', [{}])[0].get('region', '') + provider = deserialized.get('cloud', {}).get('provider', '') + compliance = '' + compliance_field = deserialized.get('unmapped', {}).get("compliance", {}) + if compliance_field: + compliance = ' | '.join([f"{key}:{','.join(value)}" for key, value in compliance_field.items()]) + result_extended = deserialized.get('status_detail') + general_description = deserialized.get('finding_info', {}).get('desc', '') + asff_compliance_type = deserialized.get('unmapped', {}).get('check_type', '') + severity = deserialized.get('severity', 'Info').capitalize() + aws_service_name = deserialized.get('resources', [{}])[0].get('group', {}).get('name', '') + impact = deserialized.get('risk_details') + mitigation = deserialized.get('remediation', {}).get("desc", '') + documentation = deserialized.get('remediation', {}).get("references", '') + documentation = str(documentation) + "\n" + str(deserialized.get('unmapped', {}).get('related_url', '')) + security_domain = deserialized.get('resources', [{}])[0].get('type', '') + timestamp = deserialized.get("event_time") + resource_arn = deserialized.get('resources', [{}])[0].get('uid', '') + resource_id = deserialized.get('resources', [{}])[0].get('name', '') + unique_id_from_tool = deserialized.get('finding_info', {}).get('uid', '') + if not resource_arn or resource_arn == "": + component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) + else: + component_name = resource_arn + + description = "**Issue:** " + str(result_extended) + \ + "\n**Description:** " + str(general_description) + \ + "\n**AWS Account:** " + str(account_id) + \ + "\n**Region:** " + str(region) + \ + "\n**AWS Service:** " + str(aws_service_name) + \ + "\n**Security Domain:** " + str(security_domain) + \ + "\n**Compliance:** " + str(compliance) + \ + "\n**ASFF Compliance Type:** " + str(asff_compliance_type) + + # improving key to get duplicates + dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() + if dupe_key in dupes: + find = dupes[dupe_key] + if description is not None: + find.description += description + "\n\n" + find.nb_occurences += 1 + else: + find = Finding( + title=textwrap.shorten(result_extended, 150), + cwe=1032, # Security Configuration Weaknesses, would like to fine tune + test=test, + description=description, + component_name=component_name, + unique_id_from_tool=unique_id_from_tool, + severity=severity, + references=documentation, + date=date.fromisoformat(timestamp[:10]), + static_finding=True, + dynamic_finding=False, + nb_occurences=1, + mitigation=mitigation, + impact=impact, + ) + dupes[dupe_key] = find + + return list(dupes.values()) + def process_json(self, file, test): dupes = {} diff --git a/dojo/tools/aws_prowler_v4/__init__.py b/dojo/tools/aws_prowler_v4/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/dojo/tools/aws_prowler_v4/parser.py b/dojo/tools/aws_prowler_v4/parser.py deleted file mode 100644 index 54ae4e092b0..00000000000 --- a/dojo/tools/aws_prowler_v4/parser.py +++ /dev/null @@ -1,108 +0,0 @@ - -import hashlib -import json -import textwrap -from datetime import date - -from dojo.models import Finding - - -class AWSProwlerV4Parser: - SCAN_TYPE = ["AWS Prowler V4"] - - def get_scan_types(self): - return AWSProwlerV4Parser.SCAN_TYPE - - def get_label_for_scan_types(self, scan_type): - return AWSProwlerV4Parser.SCAN_TYPE[0] - - def get_description_for_scan_types(self, scan_type): - return "Export of AWS Prowler V4 JSON OCSF v1.1.0 format." - - def get_findings(self, file, test): - if file.name.lower().endswith('.json'): - return self.process_json(file, test) - else: - msg = 'Unknown file format' - raise ValueError(msg) - - def process_json(self, file, test): - dupes = {} - - data = json.load(file) - # mapping of json fields between Prowler v3 and v4: - # https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/reporting/#json - for deserialized in data: - - status = deserialized.get('status_code') - if status.upper() != 'FAIL': - continue - - account_id = deserialized.get('cloud', {}).get('account', {}).get("uid", '') - region = deserialized.get('resources', [{}])[0].get('region', '') - provider = deserialized.get('cloud', {}).get('provider', '') - compliance = '' - compliance_field = deserialized.get('unmapped', {}).get("compliance", {}) - if compliance_field: - compliance = ' | '.join([f"{key}:{','.join(value)}" for key, value in compliance_field.items()]) - result_extended = deserialized.get('status_detail') - general_description = deserialized.get('finding_info', {}).get('desc', '') - asff_compliance_type = deserialized.get('unmapped', {}).get('check_type', '') - severity = deserialized.get('severity', 'Info').capitalize() - aws_service_name = deserialized.get('resources', [{}])[0].get('group', {}).get('name', '') - impact = deserialized.get('risk_details') - mitigation = deserialized.get('remediation', {}).get("desc", '') - documentation = deserialized.get('remediation', {}).get("references", '') - documentation = str(documentation) + "\n" + str(deserialized.get('unmapped', {}).get('related_url', '')) - security_domain = deserialized.get('resources', [{}])[0].get('type', '') - timestamp = deserialized.get("event_time") - resource_arn = deserialized.get('resources', [{}])[0].get('uid', '') - resource_id = deserialized.get('resources', [{}])[0].get('name', '') - unique_id_from_tool = deserialized.get('finding_info', {}).get('uid', '') - if not resource_arn or resource_arn == "": - component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) - else: - component_name = resource_arn - - description = "**Issue:** " + str(result_extended) + \ - "\n**Description:** " + str(general_description) + \ - "\n**AWS Account:** " + str(account_id) + \ - "\n**Region:** " + str(region) + \ - "\n**AWS Service:** " + str(aws_service_name) + \ - "\n**Security Domain:** " + str(security_domain) + \ - "\n**Compliance:** " + str(compliance) + \ - "\n**ASFF Compliance Type:** " + str(asff_compliance_type) - - # improving key to get duplicates - dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() - if dupe_key in dupes: - find = dupes[dupe_key] - if description is not None: - find.description += description + "\n\n" - find.nb_occurences += 1 - else: - find = Finding( - title=textwrap.shorten(result_extended, 150), - cwe=1032, # Security Configuration Weaknesses, would like to fine tune - test=test, - description=description, - component_name=component_name, - unique_id_from_tool=unique_id_from_tool, - severity=severity, - references=documentation, - date=date.fromisoformat(timestamp[:10]), - static_finding=True, - dynamic_finding=False, - nb_occurences=1, - mitigation=mitigation, - impact=impact, - ) - dupes[dupe_key] = find - - return list(dupes.values()) - - def formatview(self, depth): - if depth > 1: - return "* " - else: - return "" diff --git a/unittests/scans/aws_prowler_v4/many_vuln.json b/unittests/scans/aws_prowler_v3/many_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v4/many_vuln.json rename to unittests/scans/aws_prowler_v3/many_vuln.ocsf.json diff --git a/unittests/scans/aws_prowler_v4/no_vuln.json b/unittests/scans/aws_prowler_v3/no_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v4/no_vuln.json rename to unittests/scans/aws_prowler_v3/no_vuln.ocsf.json diff --git a/unittests/scans/aws_prowler_v4/one_vuln.json b/unittests/scans/aws_prowler_v3/one_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v4/one_vuln.json rename to unittests/scans/aws_prowler_v3/one_vuln.ocsf.json diff --git a/unittests/tools/test_aws_prowler_v3_parser.py b/unittests/tools/test_aws_prowler_v3_parser.py index abbecc578ed..5d3079d8b79 100644 --- a/unittests/tools/test_aws_prowler_v3_parser.py +++ b/unittests/tools/test_aws_prowler_v3_parser.py @@ -38,3 +38,31 @@ def test_aws_prowler_parser_with_many_vuln_has_many_findings_json(self): with self.subTest(i=3): self.assertEqual("prowler-aws-account_maintain_current_contact_details-999999999999-us-east-1-999999999999", findings[2].unique_id_from_tool) self.assertIn('Maintain current contact details.', findings[2].description) + + def test_aws_prowler_parser_with_no_vuln_has_no_findings_ocsf_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v3/no_vuln.ocsf.json")) + self.assertEqual(0, len(findings)) + + def test_aws_prowler_parser_with_critical_vuln_has_one_findings_ocsf_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v3/one_vuln.ocsf.json")) + self.assertEqual(1, len(findings)) + self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) + self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) + self.assertEqual("arn:aws:iam::123456789012:role/myAdministratorExecutionRole", findings[0].component_name) + self.assertIn('https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege', findings[0].references) + + def test_aws_prowler_parser_with_many_vuln_has_many_findings_ocsf_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v3/many_vuln.ocsf.json")) + self.assertEqual(3, len(findings)) + with self.subTest(i=0): + self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) + self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) + with self.subTest(i=1): + self.assertEqual("prowler-aws-iam_role_cross_account_readonlyaccess_policy-123456789012-us-east-1-AuditRole", findings[1].unique_id_from_tool) + self.assertIn('Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts', findings[1].description) + with self.subTest(i=3): + self.assertEqual("prowler-aws-iam_role_permissive_trust_relationship-123456789012-us-east-1-CrossAccountResourceAccessRole", findings[2].unique_id_from_tool) + self.assertIn('Ensure IAM Roles do not allow assume role from any role of a cross account', findings[2].description) \ No newline at end of file diff --git a/unittests/tools/test_aws_prowler_v4_parser.py b/unittests/tools/test_aws_prowler_v4_parser.py deleted file mode 100644 index 74e97050f6e..00000000000 --- a/unittests/tools/test_aws_prowler_v4_parser.py +++ /dev/null @@ -1,40 +0,0 @@ -from dojo.models import Test -from dojo.tools.aws_prowler_v4.parser import AWSProwlerV4Parser - -from ..dojo_test_case import DojoTestCase - - -class TestAwsProwlerV4Parser(DojoTestCase): - def setup(self, testfile): - parser = AWSProwlerV4Parser() - findings = parser.get_findings(testfile, Test()) - testfile.close() - return findings - - def test_aws_prowler_parser_with_no_vuln_has_no_findings_json(self): - findings = self.setup( - open("unittests/scans/aws_prowler_v4/no_vuln.json")) - self.assertEqual(0, len(findings)) - - def test_aws_prowler_parser_with_critical_vuln_has_one_findings_json(self): - findings = self.setup( - open("unittests/scans/aws_prowler_v4/one_vuln.json")) - self.assertEqual(1, len(findings)) - self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) - self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) - self.assertEqual("arn:aws:iam::123456789012:role/myAdministratorExecutionRole", findings[0].component_name) - self.assertIn('https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege', findings[0].references) - - def test_aws_prowler_parser_with_many_vuln_has_many_findings_json(self): - findings = self.setup( - open("unittests/scans/aws_prowler_v4/many_vuln.json")) - self.assertEqual(3, len(findings)) - with self.subTest(i=0): - self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) - self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) - with self.subTest(i=1): - self.assertEqual("prowler-aws-iam_role_cross_account_readonlyaccess_policy-123456789012-us-east-1-AuditRole", findings[1].unique_id_from_tool) - self.assertIn('Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts', findings[1].description) - with self.subTest(i=3): - self.assertEqual("prowler-aws-iam_role_permissive_trust_relationship-123456789012-us-east-1-CrossAccountResourceAccessRole", findings[2].unique_id_from_tool) - self.assertIn('Ensure IAM Roles do not allow assume role from any role of a cross account', findings[2].description) From 870bada8a63bbb6a70600b80480b96ce4a0dfe5d Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 14:44:55 +0200 Subject: [PATCH 07/27] update aws_prowler_v3.md --- .../parsers/file/aws_prowler_v3.md | 101 ++++++++++++++++- .../parsers/file/aws_prowler_v4.md | 102 ------------------ 2 files changed, 96 insertions(+), 107 deletions(-) delete mode 100644 docs/content/en/integrations/parsers/file/aws_prowler_v4.md diff --git a/docs/content/en/integrations/parsers/file/aws_prowler_v3.md b/docs/content/en/integrations/parsers/file/aws_prowler_v3.md index 17dcf9698ae..687d9faf586 100644 --- a/docs/content/en/integrations/parsers/file/aws_prowler_v3.md +++ b/docs/content/en/integrations/parsers/file/aws_prowler_v3.md @@ -4,12 +4,16 @@ toc_hide: true --- ### File Types -DefectDojo parser accepts a .json file. Please note: earlier versions of AWS Prowler create output data in a different format. See our other documentation if you are using an earlier version of AWS Prowler: https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler/ +DefectDojo parser accepts a native `json` file produced by prowler v3 with file extension `.json` or a `ocsf-json` file produced by prowler v4 with file extension `.ocsf.json`. +Please note: earlier versions of AWS Prowler create output data in a different format. See our other [prowler parser documentation](https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler/) if you are using an earlier version of AWS Prowler. -JSON reports can be created from the [AWS Prowler V3 CLI](https://docs.prowler.cloud/en/latest/tutorials/reporting/#json) using the following command: `prowler -M json` +JSON reports can be created from the [AWS Prowler v3 CLI](https://docs.prowler.com/projects/prowler-open-source/en/v3/tutorials/reporting/#json) using the following command: `prowler -M json` -### Acceptable JSON Format -Parser expects an array of assessments. All properties are strings and are required by the parser. +JSON-OCSF reports can be created from the [AWS Prowler v4 CLI](https://docs.prowler.cloud/en/latest/tutorials/reporting/#json) using the following command: `prowler -M json-ocsf` + + +### Acceptable Prowler v3 JSON format +Parser expects an array of assessments. All properties are strings and are required by the parser. ~~~ @@ -68,5 +72,92 @@ Parser expects an array of assessments. All properties are strings and are requ ~~~ +### Acceptable Prowler v4 JSON-OCSF format +The parser expects an array of assessments. All properties are strings and are required by the parser. + +~~~ +[{ + "metadata": { + "event_code": "iam_role_administratoraccess_policy_permissive_trust_relationship", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role myAdministratorExecutionRole has AdministratorAccess policy attached that has too permissive trust relationship.", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": {} + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "product_uid": "prowler", + "title": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "uid": "prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [], + "name": "myAdministratorExecutionRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/myAdministratorExecutionRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege. If a role really needs AdministratorAccess, the trust relationship must be well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "references": [ + "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" + ] + }, + "risk_details": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It is therefore particularly important that the trust relationship is well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "type_uid": 200401, + "type_name": "Create" +}] + +~~~ + ### Sample Scan Data -Unit tests of AWS Prowler V3 JSON can be found at https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/aws_prowler_v3. \ No newline at end of file +Unit tests of AWS Prowler v3 JSON and Prowler v4 JSON-OCSF can be found at https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/aws_prowler_v3. \ No newline at end of file diff --git a/docs/content/en/integrations/parsers/file/aws_prowler_v4.md b/docs/content/en/integrations/parsers/file/aws_prowler_v4.md deleted file mode 100644 index aada1887ae1..00000000000 --- a/docs/content/en/integrations/parsers/file/aws_prowler_v4.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: "AWS Prowler V4" -toc_hide: true ---- - -### File Types -DefectDojo parser accepts a .json-ocsf file. Please note: earlier versions of AWS Prowler create output data in a different format. See our other documentation if you are using an earlier version of AWS Prowler: -* [Prowler v2](https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler/) -* [Prowler v3](https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler_v3/) - -JSON reports can be created from the [AWS Prowler V4 CLI](https://docs.prowler.cloud/en/latest/tutorials/reporting/#json) using the following command: `prowler -M json-ocsf` - -### Acceptable JSON Format -The parser expects an array of assessments. All properties are strings and are required by the parser. - -~~~ - -[{ - "metadata": { - "event_code": "iam_role_administratoraccess_policy_permissive_trust_relationship", - "product": { - "name": "Prowler", - "vendor_name": "Prowler", - "version": "4.2.1" - }, - "version": "1.2.0" - }, - "severity_id": 4, - "severity": "High", - "status": "Suppressed", - "status_code": "FAIL", - "status_detail": "IAM Role myAdministratorExecutionRole has AdministratorAccess policy attached that has too permissive trust relationship.", - "status_id": 3, - "unmapped": { - "check_type": "", - "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator", - "categories": "trustboundaries", - "depends_on": "", - "related_to": "", - "notes": "CAF Security Epic: IAM", - "compliance": {} - }, - "activity_name": "Create", - "activity_id": 1, - "finding_info": { - "created_time": "2024-06-03T14:15:19.382075", - "desc": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", - "product_uid": "prowler", - "title": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", - "uid": "prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole" - }, - "resources": [ - { - "cloud_partition": "aws", - "region": "us-east-1", - "data": { - "details": "" - }, - "group": { - "name": "iam" - }, - "labels": [], - "name": "myAdministratorExecutionRole", - "type": "AwsIamRole", - "uid": "arn:aws:iam::123456789012:role/myAdministratorExecutionRole" - } - ], - "category_name": "Findings", - "category_uid": 2, - "class_name": "DetectionFinding", - "class_uid": 2004, - "cloud": { - "account": { - "name": "", - "type": "AWS_Account", - "type_id": 10, - "uid": "123456789012", - "labels": [] - }, - "org": { - "name": "", - "uid": "" - }, - "provider": "aws", - "region": "us-east-1" - }, - "event_time": "2024-06-03T14:15:19.382075", - "remediation": { - "desc": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege. If a role really needs AdministratorAccess, the trust relationship must be well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", - "references": [ - "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" - ] - }, - "risk_details": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It is therefore particularly important that the trust relationship is well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", - "type_uid": 200401, - "type_name": "Create" -}] - -~~~ - -### Sample Scan Data -Unit tests of AWS Prowler V4 JSON-OCSF can be found at https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/aws_prowler_v4. \ No newline at end of file From d66ee7651e31706094a69f922a299a2a674bf4b4 Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 14:47:43 +0200 Subject: [PATCH 08/27] revert settings --- dojo/settings/.settings.dist.py.sha256sum | 2 +- dojo/settings/settings.dist.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dojo/settings/.settings.dist.py.sha256sum b/dojo/settings/.settings.dist.py.sha256sum index 8870b37793c..038b2cc762a 100644 --- a/dojo/settings/.settings.dist.py.sha256sum +++ b/dojo/settings/.settings.dist.py.sha256sum @@ -1 +1 @@ -a7b0e9897d9fe41abaad45297dab541ac687715ac9fd44a8b01805c237637c15 +8e116c33be5aacdf7d2712ce0d129348c91d25e464a139001d79ed0aa54465b4 diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index 7363505a60e..6c8da7074fe 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -1293,7 +1293,6 @@ def saml2_attrib_map_format(dict): 'Anchore Grype': True, 'AWS Prowler Scan': True, 'AWS Prowler V3': True, - 'AWS Prowler V4': True, 'Checkmarx Scan': False, 'Checkmarx OSA': True, 'Cloudsploit Scan': True, @@ -1378,8 +1377,7 @@ def saml2_attrib_map_format(dict): 'Aqua Scan': DEDUPE_ALGO_HASH_CODE, 'AuditJS Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'AWS Prowler Scan': DEDUPE_ALGO_HASH_CODE, - 'AWS Prowler V3': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, - 'AWS Prowler V4': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, + 'AWS Prowler V3': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL "AWS Security Finding Format (ASFF) Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Burp REST API': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Bandit Scan': DEDUPE_ALGO_HASH_CODE, From 6cbf8d6a770be4c12f6a6fba8b33030eb6108c60 Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 14:51:16 +0200 Subject: [PATCH 09/27] add modified .settings.dist.py.sha256sum --- dojo/settings/.settings.dist.py.sha256sum | 2 +- dojo/settings/settings.dist.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dojo/settings/.settings.dist.py.sha256sum b/dojo/settings/.settings.dist.py.sha256sum index 038b2cc762a..c0de37b5fe3 100644 --- a/dojo/settings/.settings.dist.py.sha256sum +++ b/dojo/settings/.settings.dist.py.sha256sum @@ -1 +1 @@ -8e116c33be5aacdf7d2712ce0d129348c91d25e464a139001d79ed0aa54465b4 +c0f6db3774e94fcfd22a5861d47e42cdd52839d01dd99ce361ecfd44f42b221c diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index 6c8da7074fe..b63bdc0f705 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -1377,7 +1377,7 @@ def saml2_attrib_map_format(dict): 'Aqua Scan': DEDUPE_ALGO_HASH_CODE, 'AuditJS Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'AWS Prowler Scan': DEDUPE_ALGO_HASH_CODE, - 'AWS Prowler V3': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL + 'AWS Prowler V3': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, "AWS Security Finding Format (ASFF) Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Burp REST API': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Bandit Scan': DEDUPE_ALGO_HASH_CODE, From b214fd527cefce0c26d40fbd1206477d325795d5 Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 15:17:12 +0200 Subject: [PATCH 10/27] revert docker-compose.yml --- docker-compose.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6e4a6c22f47..80e2d783f7c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -54,9 +54,6 @@ services: DD_CELERY_BROKER_URL: ${DD_CELERY_BROKER_URL} DD_SECRET_KEY: "${DD_SECRET_KEY:-hhZCp@D28z!n@NED*yB!ROMt+WzsY*iq}" DD_CREDENTIAL_AES_256_KEY: "${DD_CREDENTIAL_AES_256_KEY:-&91a*agLqesc*0DJ+2*bAbsUZfR*4nLw}" - DD_HASHCODE_FIELDS_PER_SCANNER: '{"AWS Prowler Scan": [ "cwe", "severity", "description" ], "AWS Prowler V3": [ "unique_id_from_tool" ]}' - DD_DEDUPLICATION_ALGORITHM_PER_PARSER: '{ "AWS Prowler V3": "unique_id_from_tool"}' - volumes: - type: bind source: ./docker/extra_settings @@ -78,8 +75,6 @@ services: DD_CELERY_BROKER_URL: ${DD_CELERY_BROKER_URL} DD_SECRET_KEY: "${DD_SECRET_KEY:-hhZCp@D28z!n@NED*yB!ROMt+WzsY*iq}" DD_CREDENTIAL_AES_256_KEY: "${DD_CREDENTIAL_AES_256_KEY:-&91a*agLqesc*0DJ+2*bAbsUZfR*4nLw}" - DD_HASHCODE_FIELDS_PER_SCANNER: '{"AWS Prowler Scan": [ "cwe", "severity", "description" ], "AWS Prowler V3": [ "unique_id_from_tool" ] }' - DD_DEDUPLICATION_ALGORITHM_PER_PARSER: '{ "AWS Prowler V3": "unique_id_from_tool" }' volumes: - type: bind source: ./docker/extra_settings @@ -100,8 +95,6 @@ services: DD_CELERY_BROKER_URL: ${DD_CELERY_BROKER_URL} DD_SECRET_KEY: "${DD_SECRET_KEY:-hhZCp@D28z!n@NED*yB!ROMt+WzsY*iq}" DD_CREDENTIAL_AES_256_KEY: "${DD_CREDENTIAL_AES_256_KEY:-&91a*agLqesc*0DJ+2*bAbsUZfR*4nLw}" - DD_HASHCODE_FIELDS_PER_SCANNER: '{"AWS Prowler Scan": [ "cwe", "severity", "description" ], "AWS Prowler V3": [ "unique_id_from_tool" ] }' - DD_DEDUPLICATION_ALGORITHM_PER_PARSER: '{ "AWS Prowler V3": "unique_id_from_tool" }' volumes: - type: bind source: ./docker/extra_settings From 9fbab42830c9da5669fa0356960f075e2e3e8fe6 Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 19:40:46 +0200 Subject: [PATCH 11/27] make ruff happy --- dojo/tools/aws_prowler_v3/parser.py | 1 - unittests/tools/test_aws_prowler_v3_parser.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/dojo/tools/aws_prowler_v3/parser.py b/dojo/tools/aws_prowler_v3/parser.py index 5796677766f..746ad839255 100644 --- a/dojo/tools/aws_prowler_v3/parser.py +++ b/dojo/tools/aws_prowler_v3/parser.py @@ -28,7 +28,6 @@ def get_findings(self, file, test): msg = 'Unknown file format' raise ValueError(msg) - def process_ocsf_json(self, file, test): dupes = {} diff --git a/unittests/tools/test_aws_prowler_v3_parser.py b/unittests/tools/test_aws_prowler_v3_parser.py index 5d3079d8b79..87d520411a5 100644 --- a/unittests/tools/test_aws_prowler_v3_parser.py +++ b/unittests/tools/test_aws_prowler_v3_parser.py @@ -65,4 +65,4 @@ def test_aws_prowler_parser_with_many_vuln_has_many_findings_ocsf_json(self): self.assertIn('Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts', findings[1].description) with self.subTest(i=3): self.assertEqual("prowler-aws-iam_role_permissive_trust_relationship-123456789012-us-east-1-CrossAccountResourceAccessRole", findings[2].unique_id_from_tool) - self.assertIn('Ensure IAM Roles do not allow assume role from any role of a cross account', findings[2].description) \ No newline at end of file + self.assertIn('Ensure IAM Roles do not allow assume role from any role of a cross account', findings[2].description) From 8c876e16a0ac71abb8e343753eebdc91524f2a41 Mon Sep 17 00:00:00 2001 From: kagahd Date: Tue, 11 Jun 2024 09:29:37 +0200 Subject: [PATCH 12/27] separate prowler v3 and v4 parsers --- dojo/tools/aws_prowler_v3/parser.py | 181 ------------------ .../__init__.py | 0 dojo/tools/aws_prowler_v3plus/parser.py | 24 +++ dojo/tools/aws_prowler_v3plus/prowler_v3.py | 79 ++++++++ dojo/tools/aws_prowler_v3plus/prowler_v4.py | 83 ++++++++ ...r.py => test_aws_prowler_v3plus_parser.py} | 6 +- 6 files changed, 189 insertions(+), 184 deletions(-) delete mode 100644 dojo/tools/aws_prowler_v3/parser.py rename dojo/tools/{aws_prowler_v3 => aws_prowler_v3plus}/__init__.py (100%) create mode 100644 dojo/tools/aws_prowler_v3plus/parser.py create mode 100644 dojo/tools/aws_prowler_v3plus/prowler_v3.py create mode 100644 dojo/tools/aws_prowler_v3plus/prowler_v4.py rename unittests/tools/{test_aws_prowler_v3_parser.py => test_aws_prowler_v3plus_parser.py} (96%) diff --git a/dojo/tools/aws_prowler_v3/parser.py b/dojo/tools/aws_prowler_v3/parser.py deleted file mode 100644 index 746ad839255..00000000000 --- a/dojo/tools/aws_prowler_v3/parser.py +++ /dev/null @@ -1,181 +0,0 @@ - -import hashlib -import json -import textwrap -from datetime import date - -from dojo.models import Finding - - -class AWSProwlerV3Parser: - SCAN_TYPE = ["AWS Prowler V3"] - - def get_scan_types(self): - return AWSProwlerV3Parser.SCAN_TYPE - - def get_label_for_scan_types(self, scan_type): - return AWSProwlerV3Parser.SCAN_TYPE[0] - - def get_description_for_scan_types(self, scan_type): - return "Exports from AWS Prowler v3 in JSON format or from Prowler v4 in OCSF-JSON format." - - def get_findings(self, file, test): - if file.name.lower().endswith('.ocsf.json'): - return self.process_ocsf_json(file, test) - elif file.name.lower().endswith('.json'): - return self.process_json(file, test) - else: - msg = 'Unknown file format' - raise ValueError(msg) - - def process_ocsf_json(self, file, test): - dupes = {} - - data = json.load(file) - # mapping of json fields between Prowler v3 and v4: - # https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/reporting/#json - for deserialized in data: - - status = deserialized.get('status_code') - if status.upper() != 'FAIL': - continue - - account_id = deserialized.get('cloud', {}).get('account', {}).get("uid", '') - region = deserialized.get('resources', [{}])[0].get('region', '') - provider = deserialized.get('cloud', {}).get('provider', '') - compliance = '' - compliance_field = deserialized.get('unmapped', {}).get("compliance", {}) - if compliance_field: - compliance = ' | '.join([f"{key}:{','.join(value)}" for key, value in compliance_field.items()]) - result_extended = deserialized.get('status_detail') - general_description = deserialized.get('finding_info', {}).get('desc', '') - asff_compliance_type = deserialized.get('unmapped', {}).get('check_type', '') - severity = deserialized.get('severity', 'Info').capitalize() - aws_service_name = deserialized.get('resources', [{}])[0].get('group', {}).get('name', '') - impact = deserialized.get('risk_details') - mitigation = deserialized.get('remediation', {}).get("desc", '') - documentation = deserialized.get('remediation', {}).get("references", '') - documentation = str(documentation) + "\n" + str(deserialized.get('unmapped', {}).get('related_url', '')) - security_domain = deserialized.get('resources', [{}])[0].get('type', '') - timestamp = deserialized.get("event_time") - resource_arn = deserialized.get('resources', [{}])[0].get('uid', '') - resource_id = deserialized.get('resources', [{}])[0].get('name', '') - unique_id_from_tool = deserialized.get('finding_info', {}).get('uid', '') - if not resource_arn or resource_arn == "": - component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) - else: - component_name = resource_arn - - description = "**Issue:** " + str(result_extended) + \ - "\n**Description:** " + str(general_description) + \ - "\n**AWS Account:** " + str(account_id) + \ - "\n**Region:** " + str(region) + \ - "\n**AWS Service:** " + str(aws_service_name) + \ - "\n**Security Domain:** " + str(security_domain) + \ - "\n**Compliance:** " + str(compliance) + \ - "\n**ASFF Compliance Type:** " + str(asff_compliance_type) - - # improving key to get duplicates - dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() - if dupe_key in dupes: - find = dupes[dupe_key] - if description is not None: - find.description += description + "\n\n" - find.nb_occurences += 1 - else: - find = Finding( - title=textwrap.shorten(result_extended, 150), - cwe=1032, # Security Configuration Weaknesses, would like to fine tune - test=test, - description=description, - component_name=component_name, - unique_id_from_tool=unique_id_from_tool, - severity=severity, - references=documentation, - date=date.fromisoformat(timestamp[:10]), - static_finding=True, - dynamic_finding=False, - nb_occurences=1, - mitigation=mitigation, - impact=impact, - ) - dupes[dupe_key] = find - - return list(dupes.values()) - - def process_json(self, file, test): - dupes = {} - - data = json.load(file) - for deserialized in data: - - status = deserialized.get('Status') - if status.upper() != 'FAIL': - continue - - account = deserialized.get('AccountId') - region = deserialized.get('Region') - provider = deserialized.get('Provider') - compliance = str(deserialized.get('Compliance')) - result_extended = deserialized.get('StatusExtended') - general_description = deserialized.get('Description') - asff_compliance_type = " / ".join(deserialized.get('CheckType')) - severity = deserialized.get('Severity', 'Info').capitalize() - aws_service_name = deserialized.get('ServiceName') - impact = deserialized.get('Risk') - mitigation = deserialized.get('Remediation', {}).get('Recommendation', {}).get("Text", '') - mitigation = str(mitigation) + "\n" + str(deserialized.get('Remediation', {}).get('Code')) - documentation = deserialized.get('Remediation', {}).get('Recommendation', {}).get("Url") - documentation = str(documentation) + "\n" + str(deserialized.get('RelatedUrl')) - security_domain = deserialized.get('ResourceType') - timestamp = deserialized.get('AssessmentStartTime') - resource_arn = deserialized.get('ResourceArn') - account_id = deserialized.get('AccountId') - resource_id = deserialized.get('ResourceId') - unique_id_from_tool = deserialized.get('FindingUniqueId') - if not resource_arn or resource_arn == "": - component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) - else: - component_name = resource_arn - - description = "**Issue:** " + str(result_extended) + \ - "\n**Description:** " + str(general_description) + \ - "\n**AWS Account:** " + str(account) + " | **Region:** " + str(region) + \ - "\n**Compliance:** " + str(compliance) + \ - "\n**AWS Service:** " + str(aws_service_name) + \ - "\n**ASFF Compliance Type:** " + str(asff_compliance_type) + \ - "\n**Security Domain:** " + str(security_domain) - - # improving key to get duplicates - dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() - if dupe_key in dupes: - find = dupes[dupe_key] - if description is not None: - find.description += description + "\n\n" - find.nb_occurences += 1 - else: - find = Finding( - title=textwrap.shorten(result_extended, 150), - cwe=1032, # Security Configuration Weaknesses, would like to fine tune - test=test, - description=description, - component_name=component_name, - unique_id_from_tool=unique_id_from_tool, - severity=severity, - references=documentation, - date=date.fromisoformat(timestamp[:10]), - static_finding=True, - dynamic_finding=False, - nb_occurences=1, - mitigation=mitigation, - impact=impact, - ) - dupes[dupe_key] = find - - return list(dupes.values()) - - def formatview(self, depth): - if depth > 1: - return "* " - else: - return "" diff --git a/dojo/tools/aws_prowler_v3/__init__.py b/dojo/tools/aws_prowler_v3plus/__init__.py similarity index 100% rename from dojo/tools/aws_prowler_v3/__init__.py rename to dojo/tools/aws_prowler_v3plus/__init__.py diff --git a/dojo/tools/aws_prowler_v3plus/parser.py b/dojo/tools/aws_prowler_v3plus/parser.py new file mode 100644 index 00000000000..8e62047ac86 --- /dev/null +++ b/dojo/tools/aws_prowler_v3plus/parser.py @@ -0,0 +1,24 @@ +from dojo.tools.aws_prowler_v3plus.prowler_v3 import AWSProwlerV3Parser +from dojo.tools.aws_prowler_v3plus.prowler_v4 import AWSProwlerV4Parser + + +class AWSProwlerV3plusParser: + SCAN_TYPE = ["AWS Prowler V3"] + + def get_scan_types(self): + return AWSProwlerV3plusParser.SCAN_TYPE + + def get_label_for_scan_types(self, scan_type): + return AWSProwlerV3plusParser.SCAN_TYPE[0] + + def get_description_for_scan_types(self, scan_type): + return "Exports from AWS Prowler v3 in JSON format or from Prowler v4 in OCSF-JSON format." + + def get_findings(self, file, test): + if file.name.lower().endswith('.ocsf.json'): + return AWSProwlerV4Parser().process_ocsf_json(file, test) + elif file.name.lower().endswith('.json'): + return AWSProwlerV3Parser().process_json(file, test) + else: + msg = 'Unknown file format' + raise ValueError(msg) diff --git a/dojo/tools/aws_prowler_v3plus/prowler_v3.py b/dojo/tools/aws_prowler_v3plus/prowler_v3.py new file mode 100644 index 00000000000..cce0472b67d --- /dev/null +++ b/dojo/tools/aws_prowler_v3plus/prowler_v3.py @@ -0,0 +1,79 @@ +import hashlib +import json +import textwrap +from datetime import date + +from dojo.models import Finding + + +class AWSProwlerV3Parser: + def process_json(self, file, test): + dupes = {} + + data = json.load(file) + for deserialized in data: + + status = deserialized.get('Status') + if status.upper() != 'FAIL': + continue + + account = deserialized.get('AccountId') + region = deserialized.get('Region') + provider = deserialized.get('Provider') + compliance = str(deserialized.get('Compliance')) + result_extended = deserialized.get('StatusExtended') + general_description = deserialized.get('Description') + asff_compliance_type = " / ".join(deserialized.get('CheckType')) + severity = deserialized.get('Severity', 'Info').capitalize() + aws_service_name = deserialized.get('ServiceName') + impact = deserialized.get('Risk') + mitigation = deserialized.get('Remediation', {}).get('Recommendation', {}).get("Text", '') + mitigation = str(mitigation) + "\n" + str(deserialized.get('Remediation', {}).get('Code')) + documentation = deserialized.get('Remediation', {}).get('Recommendation', {}).get("Url") + documentation = str(documentation) + "\n" + str(deserialized.get('RelatedUrl')) + security_domain = deserialized.get('ResourceType') + timestamp = deserialized.get('AssessmentStartTime') + resource_arn = deserialized.get('ResourceArn') + account_id = deserialized.get('AccountId') + resource_id = deserialized.get('ResourceId') + unique_id_from_tool = deserialized.get('FindingUniqueId') + if not resource_arn or resource_arn == "": + component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) + else: + component_name = resource_arn + + description = "**Issue:** " + str(result_extended) + \ + "\n**Description:** " + str(general_description) + \ + "\n**AWS Account:** " + str(account) + " | **Region:** " + str(region) + \ + "\n**Compliance:** " + str(compliance) + \ + "\n**AWS Service:** " + str(aws_service_name) + \ + "\n**ASFF Compliance Type:** " + str(asff_compliance_type) + \ + "\n**Security Domain:** " + str(security_domain) + + # improving key to get duplicates + dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() + if dupe_key in dupes: + find = dupes[dupe_key] + if description is not None: + find.description += description + "\n\n" + find.nb_occurences += 1 + else: + find = Finding( + title=textwrap.shorten(result_extended, 150), + cwe=1032, # Security Configuration Weaknesses, would like to fine tune + test=test, + description=description, + component_name=component_name, + unique_id_from_tool=unique_id_from_tool, + severity=severity, + references=documentation, + date=date.fromisoformat(timestamp[:10]), + static_finding=True, + dynamic_finding=False, + nb_occurences=1, + mitigation=mitigation, + impact=impact, + ) + dupes[dupe_key] = find + + return list(dupes.values()) diff --git a/dojo/tools/aws_prowler_v3plus/prowler_v4.py b/dojo/tools/aws_prowler_v3plus/prowler_v4.py new file mode 100644 index 00000000000..ac3c4a99e6c --- /dev/null +++ b/dojo/tools/aws_prowler_v3plus/prowler_v4.py @@ -0,0 +1,83 @@ +import hashlib +import json +import textwrap +from datetime import date + +from dojo.models import Finding + + +class AWSProwlerV4Parser: + def process_ocsf_json(self, file, test): + dupes = {} + + data = json.load(file) + # mapping of json fields between Prowler v3 and v4: + # https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/reporting/#json + for deserialized in data: + + status = deserialized.get('status_code') + if status.upper() != 'FAIL': + continue + + account_id = deserialized.get('cloud', {}).get('account', {}).get("uid", '') + region = deserialized.get('resources', [{}])[0].get('region', '') + provider = deserialized.get('cloud', {}).get('provider', '') + compliance = '' + compliance_field = deserialized.get('unmapped', {}).get("compliance", {}) + if compliance_field: + compliance = ' | '.join([f"{key}:{','.join(value)}" for key, value in compliance_field.items()]) + result_extended = deserialized.get('status_detail') + general_description = deserialized.get('finding_info', {}).get('desc', '') + asff_compliance_type = deserialized.get('unmapped', {}).get('check_type', '') + severity = deserialized.get('severity', 'Info').capitalize() + aws_service_name = deserialized.get('resources', [{}])[0].get('group', {}).get('name', '') + impact = deserialized.get('risk_details') + mitigation = deserialized.get('remediation', {}).get("desc", '') + documentation = deserialized.get('remediation', {}).get("references", '') + documentation = str(documentation) + "\n" + str(deserialized.get('unmapped', {}).get('related_url', '')) + security_domain = deserialized.get('resources', [{}])[0].get('type', '') + timestamp = deserialized.get("event_time") + resource_arn = deserialized.get('resources', [{}])[0].get('uid', '') + resource_id = deserialized.get('resources', [{}])[0].get('name', '') + unique_id_from_tool = deserialized.get('finding_info', {}).get('uid', '') + if not resource_arn or resource_arn == "": + component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) + else: + component_name = resource_arn + + description = "**Issue:** " + str(result_extended) + \ + "\n**Description:** " + str(general_description) + \ + "\n**AWS Account:** " + str(account_id) + \ + "\n**Region:** " + str(region) + \ + "\n**AWS Service:** " + str(aws_service_name) + \ + "\n**Security Domain:** " + str(security_domain) + \ + "\n**Compliance:** " + str(compliance) + \ + "\n**ASFF Compliance Type:** " + str(asff_compliance_type) + + # improving key to get duplicates + dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() + if dupe_key in dupes: + find = dupes[dupe_key] + if description is not None: + find.description += description + "\n\n" + find.nb_occurences += 1 + else: + find = Finding( + title=textwrap.shorten(result_extended, 150), + cwe=1032, # Security Configuration Weaknesses, would like to fine tune + test=test, + description=description, + component_name=component_name, + unique_id_from_tool=unique_id_from_tool, + severity=severity, + references=documentation, + date=date.fromisoformat(timestamp[:10]), + static_finding=True, + dynamic_finding=False, + nb_occurences=1, + mitigation=mitigation, + impact=impact, + ) + dupes[dupe_key] = find + + return list(dupes.values()) diff --git a/unittests/tools/test_aws_prowler_v3_parser.py b/unittests/tools/test_aws_prowler_v3plus_parser.py similarity index 96% rename from unittests/tools/test_aws_prowler_v3_parser.py rename to unittests/tools/test_aws_prowler_v3plus_parser.py index 87d520411a5..c79582677d6 100644 --- a/unittests/tools/test_aws_prowler_v3_parser.py +++ b/unittests/tools/test_aws_prowler_v3plus_parser.py @@ -1,12 +1,12 @@ from dojo.models import Test -from dojo.tools.aws_prowler_v3.parser import AWSProwlerV3Parser +from dojo.tools.aws_prowler_v3plus.parser import AWSProwlerV3plusParser from ..dojo_test_case import DojoTestCase -class TestAwsProwlerV3Parser(DojoTestCase): +class TestAwsProwlerV3plusParser(DojoTestCase): def setup(self, testfile): - parser = AWSProwlerV3Parser() + parser = AWSProwlerV3plusParser() findings = parser.get_findings(testfile, Test()) testfile.close() return findings From 77a6bbce1162285601b0c4bc2c44f07212b68c55 Mon Sep 17 00:00:00 2001 From: kagahd Date: Tue, 11 Jun 2024 10:06:58 +0200 Subject: [PATCH 13/27] renaming --- .../{aws_prowler_v3.md => aws_prowler_v3plus.md} | 0 .../many_vuln.json | 0 .../many_vuln.ocsf.json | 0 .../no_vuln.json | 0 .../no_vuln.ocsf.json | 0 .../one_vuln.json | 0 .../one_vuln.ocsf.json | 0 unittests/tools/test_aws_prowler_v3plus_parser.py | 12 ++++++------ 8 files changed, 6 insertions(+), 6 deletions(-) rename docs/content/en/integrations/parsers/file/{aws_prowler_v3.md => aws_prowler_v3plus.md} (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/many_vuln.json (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/many_vuln.ocsf.json (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/no_vuln.json (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/no_vuln.ocsf.json (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/one_vuln.json (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/one_vuln.ocsf.json (100%) diff --git a/docs/content/en/integrations/parsers/file/aws_prowler_v3.md b/docs/content/en/integrations/parsers/file/aws_prowler_v3plus.md similarity index 100% rename from docs/content/en/integrations/parsers/file/aws_prowler_v3.md rename to docs/content/en/integrations/parsers/file/aws_prowler_v3plus.md diff --git a/unittests/scans/aws_prowler_v3/many_vuln.json b/unittests/scans/aws_prowler_v3plus/many_vuln.json similarity index 100% rename from unittests/scans/aws_prowler_v3/many_vuln.json rename to unittests/scans/aws_prowler_v3plus/many_vuln.json diff --git a/unittests/scans/aws_prowler_v3/many_vuln.ocsf.json b/unittests/scans/aws_prowler_v3plus/many_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v3/many_vuln.ocsf.json rename to unittests/scans/aws_prowler_v3plus/many_vuln.ocsf.json diff --git a/unittests/scans/aws_prowler_v3/no_vuln.json b/unittests/scans/aws_prowler_v3plus/no_vuln.json similarity index 100% rename from unittests/scans/aws_prowler_v3/no_vuln.json rename to unittests/scans/aws_prowler_v3plus/no_vuln.json diff --git a/unittests/scans/aws_prowler_v3/no_vuln.ocsf.json b/unittests/scans/aws_prowler_v3plus/no_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v3/no_vuln.ocsf.json rename to unittests/scans/aws_prowler_v3plus/no_vuln.ocsf.json diff --git a/unittests/scans/aws_prowler_v3/one_vuln.json b/unittests/scans/aws_prowler_v3plus/one_vuln.json similarity index 100% rename from unittests/scans/aws_prowler_v3/one_vuln.json rename to unittests/scans/aws_prowler_v3plus/one_vuln.json diff --git a/unittests/scans/aws_prowler_v3/one_vuln.ocsf.json b/unittests/scans/aws_prowler_v3plus/one_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v3/one_vuln.ocsf.json rename to unittests/scans/aws_prowler_v3plus/one_vuln.ocsf.json diff --git a/unittests/tools/test_aws_prowler_v3plus_parser.py b/unittests/tools/test_aws_prowler_v3plus_parser.py index c79582677d6..37d7c418854 100644 --- a/unittests/tools/test_aws_prowler_v3plus_parser.py +++ b/unittests/tools/test_aws_prowler_v3plus_parser.py @@ -13,12 +13,12 @@ def setup(self, testfile): def test_aws_prowler_parser_with_no_vuln_has_no_findings_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/no_vuln.json")) + open("unittests/scans/aws_prowler_v3plus/no_vuln.json")) self.assertEqual(0, len(findings)) def test_aws_prowler_parser_with_critical_vuln_has_one_findings_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/one_vuln.json")) + open("unittests/scans/aws_prowler_v3plus/one_vuln.json")) self.assertEqual(1, len(findings)) self.assertEqual("prowler-aws-acm_certificates_expiration_check-999999999999-us-east-1-api.sandbox.partner.teste.com", findings[0].unique_id_from_tool) self.assertIn('Check if ACM Certificates are about to expire in specific days or less', findings[0].description) @@ -27,7 +27,7 @@ def test_aws_prowler_parser_with_critical_vuln_has_one_findings_json(self): def test_aws_prowler_parser_with_many_vuln_has_many_findings_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/many_vuln.json")) + open("unittests/scans/aws_prowler_v3plus/many_vuln.json")) self.assertEqual(3, len(findings)) with self.subTest(i=0): self.assertEqual("prowler-aws-acm_certificates_expiration_check-999999999999-us-east-1-api.teste.teste.com", findings[0].unique_id_from_tool) @@ -41,12 +41,12 @@ def test_aws_prowler_parser_with_many_vuln_has_many_findings_json(self): def test_aws_prowler_parser_with_no_vuln_has_no_findings_ocsf_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/no_vuln.ocsf.json")) + open("unittests/scans/aws_prowler_v3plus/no_vuln.ocsf.json")) self.assertEqual(0, len(findings)) def test_aws_prowler_parser_with_critical_vuln_has_one_findings_ocsf_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/one_vuln.ocsf.json")) + open("unittests/scans/aws_prowler_v3plus/one_vuln.ocsf.json")) self.assertEqual(1, len(findings)) self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) @@ -55,7 +55,7 @@ def test_aws_prowler_parser_with_critical_vuln_has_one_findings_ocsf_json(self): def test_aws_prowler_parser_with_many_vuln_has_many_findings_ocsf_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/many_vuln.ocsf.json")) + open("unittests/scans/aws_prowler_v3plus/many_vuln.ocsf.json")) self.assertEqual(3, len(findings)) with self.subTest(i=0): self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) From b2d3b2b58e6d5cb75651312b497450fb476f0f94 Mon Sep 17 00:00:00 2001 From: kagahd Date: Wed, 5 Jun 2024 10:51:16 +0200 Subject: [PATCH 14/27] add prowler v4 parser --- .../parsers/file/aws_prowler_v4.md | 102 ++++++++ dojo/tools/aws_prowler_v4/__init__.py | 0 dojo/tools/aws_prowler_v4/parser.py | 109 ++++++++ unittests/scans/aws_prowler_v4/many_vuln.json | 247 ++++++++++++++++++ unittests/scans/aws_prowler_v4/no_vuln.json | 1 + unittests/scans/aws_prowler_v4/one_vuln.json | 80 ++++++ unittests/tools/test_aws_prowler_v4_parser.py | 40 +++ 7 files changed, 579 insertions(+) create mode 100644 docs/content/en/integrations/parsers/file/aws_prowler_v4.md create mode 100644 dojo/tools/aws_prowler_v4/__init__.py create mode 100644 dojo/tools/aws_prowler_v4/parser.py create mode 100644 unittests/scans/aws_prowler_v4/many_vuln.json create mode 100644 unittests/scans/aws_prowler_v4/no_vuln.json create mode 100644 unittests/scans/aws_prowler_v4/one_vuln.json create mode 100644 unittests/tools/test_aws_prowler_v4_parser.py diff --git a/docs/content/en/integrations/parsers/file/aws_prowler_v4.md b/docs/content/en/integrations/parsers/file/aws_prowler_v4.md new file mode 100644 index 00000000000..77f8ff10aad --- /dev/null +++ b/docs/content/en/integrations/parsers/file/aws_prowler_v4.md @@ -0,0 +1,102 @@ +--- +title: "AWS Prowler V3" +toc_hide: true +--- + +### File Types +DefectDojo parser accepts a .json-ocsf file. Please note: earlier versions of AWS Prowler create output data in a different format. See our other documentation if you are using an earlier version of AWS Prowler: +* [Prowler v2](https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler/) +* [Prowler v3](https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler_v3/) + +JSON reports can be created from the [AWS Prowler V4 CLI](https://docs.prowler.cloud/en/latest/tutorials/reporting/#json) using the following command: `prowler -M json-ocsf` + +### Acceptable JSON Format +The parser expects an array of assessments. All properties are strings and are required by the parser. + +~~~ + +[{ + "metadata": { + "event_code": "iam_role_administratoraccess_policy_permissive_trust_relationship", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role myAdministratorExecutionRole has AdministratorAccess policy attached that has too permissive trust relationship.", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": {} + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "product_uid": "prowler", + "title": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "uid": "prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [], + "name": "myAdministratorExecutionRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/myAdministratorExecutionRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege. If a role really needs AdministratorAccess, the trust relationship must be well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "references": [ + "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" + ] + }, + "risk_details": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It is therefore particularly important that the trust relationship is well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "type_uid": 200401, + "type_name": "Create" +}] + +~~~ + +### Sample Scan Data +Unit tests of AWS Prowler V4 JSON-OCSF can be found at https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/aws_prowler_v4. \ No newline at end of file diff --git a/dojo/tools/aws_prowler_v4/__init__.py b/dojo/tools/aws_prowler_v4/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dojo/tools/aws_prowler_v4/parser.py b/dojo/tools/aws_prowler_v4/parser.py new file mode 100644 index 00000000000..f8298104cc1 --- /dev/null +++ b/dojo/tools/aws_prowler_v4/parser.py @@ -0,0 +1,109 @@ + +import hashlib +import json +import textwrap +from datetime import date + +from dojo.models import Finding + + +class AWSProwlerV4Parser: + SCAN_TYPE = ["AWS Prowler V4"] + + def get_scan_types(self): + return AWSProwlerV4Parser.SCAN_TYPE + + def get_label_for_scan_types(self, scan_type): + return AWSProwlerV4Parser.SCAN_TYPE[0] + + def get_description_for_scan_types(self, scan_type): + return "Export of AWS Prowler V4 JSON OCSF v1.1.0 format." + + def get_findings(self, file, test): + if file.name.lower().endswith('.json'): + return self.process_json(file, test) + else: + msg = 'Unknown file format' + raise ValueError(msg) + + def process_json(self, file, test): + dupes = {} + + data = json.load(file) + # mapping of json fields between Prowler v3 and v4: + # https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/reporting/#json + for deserialized in data: + + status = deserialized.get('status_code') + if status.upper() != 'FAIL': + continue + + account_id = deserialized.get('cloud', {}).get('account', {}).get("uid", '') + region = deserialized.get('resources', [{}])[0].get('region', '') + provider = deserialized.get('cloud', {}).get('provider', '') + compliance = '' + compliance_field = deserialized.get('unmapped', {}).get("compliance", {}) + if compliance_field: + compliance = ' | '.join([f"{key}:{','.join(value)}" for key, value in compliance_field.items()]) + result_extended = deserialized.get('status_detail') + general_description = deserialized.get('finding_info', {}).get('desc', '') + asff_compliance_type = deserialized.get('unmapped', {}).get('check_type', '') + severity = deserialized.get('severity', 'Info').capitalize() + aws_service_name = deserialized.get('resources', [{}])[0].get('group', {}).get('name', '') + impact = deserialized.get('risk_details') + mitigation = deserialized.get('remediation', {}).get("desc", '') + documentation = deserialized.get('remediation', {}).get("references", '') + documentation = str(documentation) + "\n" + str(deserialized.get('unmapped', {}).get('related_url', '')) + security_domain = deserialized.get('resources', [{}])[0].get('type', '') + timestamp = deserialized.get("event_time") + resource_arn = deserialized.get('resources', [{}])[0].get('uid', '') + resource_id = deserialized.get('resources', [{}])[0].get('name', '') + unique_id_from_tool = deserialized.get('finding_info', {}).get('uid', '') + if not resource_arn or resource_arn == "": + component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) + else: + component_name = resource_arn + + description = "**Issue:** " + str(result_extended) + \ + "\n**Description:** " + str(general_description) + \ + "\n**AWS Account:** " + str(account_id) + \ + "\n**Region:** " + str(region) + \ + "\n**AWS Service:** " + str(aws_service_name) + \ + "\n**Security Domain:** " + str(security_domain) + \ + "\n**Compliance:** " + str(compliance) + \ + "\n**ASFF Compliance Type:** " + str(asff_compliance_type) + + + # improving key to get duplicates + dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() + if dupe_key in dupes: + find = dupes[dupe_key] + if description is not None: + find.description += description + "\n\n" + find.nb_occurences += 1 + else: + find = Finding( + title=textwrap.shorten(result_extended, 150), + cwe=1032, # Security Configuration Weaknesses, would like to fine tune + test=test, + description=description, + component_name=component_name, + unique_id_from_tool=unique_id_from_tool, + severity=severity, + references=documentation, + date=date.fromisoformat(timestamp[:10]), + static_finding=True, + dynamic_finding=False, + nb_occurences=1, + mitigation=mitigation, + impact=impact, + ) + dupes[dupe_key] = find + + return list(dupes.values()) + + def formatview(self, depth): + if depth > 1: + return "* " + else: + return "" diff --git a/unittests/scans/aws_prowler_v4/many_vuln.json b/unittests/scans/aws_prowler_v4/many_vuln.json new file mode 100644 index 00000000000..e0f563c2a05 --- /dev/null +++ b/unittests/scans/aws_prowler_v4/many_vuln.json @@ -0,0 +1,247 @@ +[{ + "metadata": { + "event_code": "iam_role_administratoraccess_policy_permissive_trust_relationship", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role myAdministratorExecutionRole has AdministratorAccess policy attached that has too permissive trust relationship.", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": {} + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "product_uid": "prowler", + "title": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "uid": "prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [], + "name": "myAdministratorExecutionRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/myAdministratorExecutionRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege. If a role really needs AdministratorAccess, the trust relationship must be well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "references": [ + "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" + ] + }, + "risk_details": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It is therefore particularly important that the trust relationship is well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "type_uid": 200401, + "type_name": "Create" +},{ + "metadata": { + "event_code": "iam_role_cross_account_readonlyaccess_policy", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role AuditRole gives cross account read-only access.", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#awsmp_readonlyaccess", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": { + "MITRE-ATTACK": [ + "T1078" + ], + "AWS-Foundational-Technical-Review": [ + "IAM-0012" + ] + } + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts", + "product_uid": "prowler", + "title": "Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts", + "uid": "prowler-aws-iam_role_cross_account_readonlyaccess_policy-123456789012-us-east-1-AuditRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [ + "some-label=some value" + ], + "name": "AuditRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/AuditRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Remove the AWS-managed ReadOnlyAccess policy from all roles that have a trust policy, including third-party cloud accounts, or remove third-party cloud accounts from the trust policy of all roles that need the ReadOnlyAccess policy.", + "references": [ + "https://docs.securestate.vmware.com/rule-docs/aws-iam-role-cross-account-readonlyaccess-policy" + ] + }, + "risk_details": "The AWS-managed ReadOnlyAccess policy is highly potent and exposes the customer to a significant data leakage threat. It should be granted very conservatively. For granting access to 3rd party vendors, consider using alternative managed policies, such as ViewOnlyAccess or SecurityAudit.", + "type_uid": 200401, + "type_name": "Create" +},{ + "metadata": { + "event_code": "iam_role_permissive_trust_relationship", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role CrossAccountResourceAccessRole has permissive trust relationship to other accounts", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html#principal-accounts", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": {} + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles do not allow assume role from any role of a cross account", + "product_uid": "prowler", + "title": "Ensure IAM Roles do not allow assume role from any role of a cross account", + "uid": "prowler-aws-iam_role_permissive_trust_relationship-123456789012-us-east-1-CrossAccountResourceAccessRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [], + "name": "CrossAccountResourceAccessRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/CrossAccountResourceAccessRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Ensure IAM Roles do not allow assume role from any role of a cross account but only from specific roles of specific accounts.", + "references": [ + "https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html#principal-roles" + ] + }, + "risk_details": "If an IAM role allows assume role from any role of a cross account, it can lead to privilege escalation.", + "type_uid": 200401, + "type_name": "Create" +}] \ No newline at end of file diff --git a/unittests/scans/aws_prowler_v4/no_vuln.json b/unittests/scans/aws_prowler_v4/no_vuln.json new file mode 100644 index 00000000000..0637a088a01 --- /dev/null +++ b/unittests/scans/aws_prowler_v4/no_vuln.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/unittests/scans/aws_prowler_v4/one_vuln.json b/unittests/scans/aws_prowler_v4/one_vuln.json new file mode 100644 index 00000000000..5e45f2077e3 --- /dev/null +++ b/unittests/scans/aws_prowler_v4/one_vuln.json @@ -0,0 +1,80 @@ +[{ + "metadata": { + "event_code": "iam_role_administratoraccess_policy_permissive_trust_relationship", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role myAdministratorExecutionRole has AdministratorAccess policy attached that has too permissive trust relationship.", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": {} + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "product_uid": "prowler", + "title": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "uid": "prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [], + "name": "myAdministratorExecutionRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/myAdministratorExecutionRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege. If a role really needs AdministratorAccess, the trust relationship must be well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "references": [ + "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" + ] + }, + "risk_details": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It is therefore particularly important that the trust relationship is well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "type_uid": 200401, + "type_name": "Create" +}] \ No newline at end of file diff --git a/unittests/tools/test_aws_prowler_v4_parser.py b/unittests/tools/test_aws_prowler_v4_parser.py new file mode 100644 index 00000000000..74e97050f6e --- /dev/null +++ b/unittests/tools/test_aws_prowler_v4_parser.py @@ -0,0 +1,40 @@ +from dojo.models import Test +from dojo.tools.aws_prowler_v4.parser import AWSProwlerV4Parser + +from ..dojo_test_case import DojoTestCase + + +class TestAwsProwlerV4Parser(DojoTestCase): + def setup(self, testfile): + parser = AWSProwlerV4Parser() + findings = parser.get_findings(testfile, Test()) + testfile.close() + return findings + + def test_aws_prowler_parser_with_no_vuln_has_no_findings_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v4/no_vuln.json")) + self.assertEqual(0, len(findings)) + + def test_aws_prowler_parser_with_critical_vuln_has_one_findings_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v4/one_vuln.json")) + self.assertEqual(1, len(findings)) + self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) + self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) + self.assertEqual("arn:aws:iam::123456789012:role/myAdministratorExecutionRole", findings[0].component_name) + self.assertIn('https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege', findings[0].references) + + def test_aws_prowler_parser_with_many_vuln_has_many_findings_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v4/many_vuln.json")) + self.assertEqual(3, len(findings)) + with self.subTest(i=0): + self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) + self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) + with self.subTest(i=1): + self.assertEqual("prowler-aws-iam_role_cross_account_readonlyaccess_policy-123456789012-us-east-1-AuditRole", findings[1].unique_id_from_tool) + self.assertIn('Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts', findings[1].description) + with self.subTest(i=3): + self.assertEqual("prowler-aws-iam_role_permissive_trust_relationship-123456789012-us-east-1-CrossAccountResourceAccessRole", findings[2].unique_id_from_tool) + self.assertIn('Ensure IAM Roles do not allow assume role from any role of a cross account', findings[2].description) From c708b06d27e3bc7806a2b69a6e7c70236cd82a9f Mon Sep 17 00:00:00 2001 From: kagahd Date: Wed, 5 Jun 2024 10:55:03 +0200 Subject: [PATCH 15/27] remove line --- dojo/tools/aws_prowler_v4/parser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dojo/tools/aws_prowler_v4/parser.py b/dojo/tools/aws_prowler_v4/parser.py index f8298104cc1..54ae4e092b0 100644 --- a/dojo/tools/aws_prowler_v4/parser.py +++ b/dojo/tools/aws_prowler_v4/parser.py @@ -73,7 +73,6 @@ def process_json(self, file, test): "\n**Compliance:** " + str(compliance) + \ "\n**ASFF Compliance Type:** " + str(asff_compliance_type) - # improving key to get duplicates dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() if dupe_key in dupes: From 1c5b39d4dd112a7edb6dd5dec3a142894d699577 Mon Sep 17 00:00:00 2001 From: kagahd Date: Wed, 5 Jun 2024 14:24:14 +0200 Subject: [PATCH 16/27] fix typo --- docs/content/en/integrations/parsers/file/aws_prowler_v4.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/en/integrations/parsers/file/aws_prowler_v4.md b/docs/content/en/integrations/parsers/file/aws_prowler_v4.md index 77f8ff10aad..aada1887ae1 100644 --- a/docs/content/en/integrations/parsers/file/aws_prowler_v4.md +++ b/docs/content/en/integrations/parsers/file/aws_prowler_v4.md @@ -1,5 +1,5 @@ --- -title: "AWS Prowler V3" +title: "AWS Prowler V4" toc_hide: true --- From d37e0e401bb069deed3b8e6e968bde0f1052a12e Mon Sep 17 00:00:00 2001 From: kagahd Date: Wed, 5 Jun 2024 16:30:16 +0200 Subject: [PATCH 17/27] add settings.dist.py although it's written that one should not touch it but use env vars --- dojo/settings/settings.dist.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index b63bdc0f705..7363505a60e 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -1293,6 +1293,7 @@ def saml2_attrib_map_format(dict): 'Anchore Grype': True, 'AWS Prowler Scan': True, 'AWS Prowler V3': True, + 'AWS Prowler V4': True, 'Checkmarx Scan': False, 'Checkmarx OSA': True, 'Cloudsploit Scan': True, @@ -1378,6 +1379,7 @@ def saml2_attrib_map_format(dict): 'AuditJS Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'AWS Prowler Scan': DEDUPE_ALGO_HASH_CODE, 'AWS Prowler V3': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, + 'AWS Prowler V4': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, "AWS Security Finding Format (ASFF) Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Burp REST API': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Bandit Scan': DEDUPE_ALGO_HASH_CODE, From 090aded140167eaa5b91b01d20616ffb74d9d548 Mon Sep 17 00:00:00 2001 From: kagahd Date: Thu, 6 Jun 2024 09:37:13 +0200 Subject: [PATCH 18/27] add modified .settings.dist.py.sha256sum --- dojo/settings/.settings.dist.py.sha256sum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dojo/settings/.settings.dist.py.sha256sum b/dojo/settings/.settings.dist.py.sha256sum index c0de37b5fe3..8870b37793c 100644 --- a/dojo/settings/.settings.dist.py.sha256sum +++ b/dojo/settings/.settings.dist.py.sha256sum @@ -1 +1 @@ -c0f6db3774e94fcfd22a5861d47e42cdd52839d01dd99ce361ecfd44f42b221c +a7b0e9897d9fe41abaad45297dab541ac687715ac9fd44a8b01805c237637c15 From e56df826ff11c85026e71cfc1b2857a1215d7fa2 Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 14:22:40 +0200 Subject: [PATCH 19/27] extend prowler v3 parser to parse also prowler v4 reports in oscf-json format --- dojo/tools/aws_prowler_v3/parser.py | 82 ++++++++++++- dojo/tools/aws_prowler_v4/__init__.py | 0 dojo/tools/aws_prowler_v4/parser.py | 108 ------------------ .../many_vuln.ocsf.json} | 0 .../no_vuln.ocsf.json} | 0 .../one_vuln.ocsf.json} | 0 unittests/tools/test_aws_prowler_v3_parser.py | 28 +++++ unittests/tools/test_aws_prowler_v4_parser.py | 40 ------- 8 files changed, 108 insertions(+), 150 deletions(-) delete mode 100644 dojo/tools/aws_prowler_v4/__init__.py delete mode 100644 dojo/tools/aws_prowler_v4/parser.py rename unittests/scans/{aws_prowler_v4/many_vuln.json => aws_prowler_v3/many_vuln.ocsf.json} (100%) rename unittests/scans/{aws_prowler_v4/no_vuln.json => aws_prowler_v3/no_vuln.ocsf.json} (100%) rename unittests/scans/{aws_prowler_v4/one_vuln.json => aws_prowler_v3/one_vuln.ocsf.json} (100%) delete mode 100644 unittests/tools/test_aws_prowler_v4_parser.py diff --git a/dojo/tools/aws_prowler_v3/parser.py b/dojo/tools/aws_prowler_v3/parser.py index 60f7a5dc7e8..5796677766f 100644 --- a/dojo/tools/aws_prowler_v3/parser.py +++ b/dojo/tools/aws_prowler_v3/parser.py @@ -17,15 +17,93 @@ def get_label_for_scan_types(self, scan_type): return AWSProwlerV3Parser.SCAN_TYPE[0] def get_description_for_scan_types(self, scan_type): - return "Export of AWS Prowler JSON V3 format." + return "Exports from AWS Prowler v3 in JSON format or from Prowler v4 in OCSF-JSON format." def get_findings(self, file, test): - if file.name.lower().endswith('.json'): + if file.name.lower().endswith('.ocsf.json'): + return self.process_ocsf_json(file, test) + elif file.name.lower().endswith('.json'): return self.process_json(file, test) else: msg = 'Unknown file format' raise ValueError(msg) + + def process_ocsf_json(self, file, test): + dupes = {} + + data = json.load(file) + # mapping of json fields between Prowler v3 and v4: + # https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/reporting/#json + for deserialized in data: + + status = deserialized.get('status_code') + if status.upper() != 'FAIL': + continue + + account_id = deserialized.get('cloud', {}).get('account', {}).get("uid", '') + region = deserialized.get('resources', [{}])[0].get('region', '') + provider = deserialized.get('cloud', {}).get('provider', '') + compliance = '' + compliance_field = deserialized.get('unmapped', {}).get("compliance", {}) + if compliance_field: + compliance = ' | '.join([f"{key}:{','.join(value)}" for key, value in compliance_field.items()]) + result_extended = deserialized.get('status_detail') + general_description = deserialized.get('finding_info', {}).get('desc', '') + asff_compliance_type = deserialized.get('unmapped', {}).get('check_type', '') + severity = deserialized.get('severity', 'Info').capitalize() + aws_service_name = deserialized.get('resources', [{}])[0].get('group', {}).get('name', '') + impact = deserialized.get('risk_details') + mitigation = deserialized.get('remediation', {}).get("desc", '') + documentation = deserialized.get('remediation', {}).get("references", '') + documentation = str(documentation) + "\n" + str(deserialized.get('unmapped', {}).get('related_url', '')) + security_domain = deserialized.get('resources', [{}])[0].get('type', '') + timestamp = deserialized.get("event_time") + resource_arn = deserialized.get('resources', [{}])[0].get('uid', '') + resource_id = deserialized.get('resources', [{}])[0].get('name', '') + unique_id_from_tool = deserialized.get('finding_info', {}).get('uid', '') + if not resource_arn or resource_arn == "": + component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) + else: + component_name = resource_arn + + description = "**Issue:** " + str(result_extended) + \ + "\n**Description:** " + str(general_description) + \ + "\n**AWS Account:** " + str(account_id) + \ + "\n**Region:** " + str(region) + \ + "\n**AWS Service:** " + str(aws_service_name) + \ + "\n**Security Domain:** " + str(security_domain) + \ + "\n**Compliance:** " + str(compliance) + \ + "\n**ASFF Compliance Type:** " + str(asff_compliance_type) + + # improving key to get duplicates + dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() + if dupe_key in dupes: + find = dupes[dupe_key] + if description is not None: + find.description += description + "\n\n" + find.nb_occurences += 1 + else: + find = Finding( + title=textwrap.shorten(result_extended, 150), + cwe=1032, # Security Configuration Weaknesses, would like to fine tune + test=test, + description=description, + component_name=component_name, + unique_id_from_tool=unique_id_from_tool, + severity=severity, + references=documentation, + date=date.fromisoformat(timestamp[:10]), + static_finding=True, + dynamic_finding=False, + nb_occurences=1, + mitigation=mitigation, + impact=impact, + ) + dupes[dupe_key] = find + + return list(dupes.values()) + def process_json(self, file, test): dupes = {} diff --git a/dojo/tools/aws_prowler_v4/__init__.py b/dojo/tools/aws_prowler_v4/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/dojo/tools/aws_prowler_v4/parser.py b/dojo/tools/aws_prowler_v4/parser.py deleted file mode 100644 index 54ae4e092b0..00000000000 --- a/dojo/tools/aws_prowler_v4/parser.py +++ /dev/null @@ -1,108 +0,0 @@ - -import hashlib -import json -import textwrap -from datetime import date - -from dojo.models import Finding - - -class AWSProwlerV4Parser: - SCAN_TYPE = ["AWS Prowler V4"] - - def get_scan_types(self): - return AWSProwlerV4Parser.SCAN_TYPE - - def get_label_for_scan_types(self, scan_type): - return AWSProwlerV4Parser.SCAN_TYPE[0] - - def get_description_for_scan_types(self, scan_type): - return "Export of AWS Prowler V4 JSON OCSF v1.1.0 format." - - def get_findings(self, file, test): - if file.name.lower().endswith('.json'): - return self.process_json(file, test) - else: - msg = 'Unknown file format' - raise ValueError(msg) - - def process_json(self, file, test): - dupes = {} - - data = json.load(file) - # mapping of json fields between Prowler v3 and v4: - # https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/reporting/#json - for deserialized in data: - - status = deserialized.get('status_code') - if status.upper() != 'FAIL': - continue - - account_id = deserialized.get('cloud', {}).get('account', {}).get("uid", '') - region = deserialized.get('resources', [{}])[0].get('region', '') - provider = deserialized.get('cloud', {}).get('provider', '') - compliance = '' - compliance_field = deserialized.get('unmapped', {}).get("compliance", {}) - if compliance_field: - compliance = ' | '.join([f"{key}:{','.join(value)}" for key, value in compliance_field.items()]) - result_extended = deserialized.get('status_detail') - general_description = deserialized.get('finding_info', {}).get('desc', '') - asff_compliance_type = deserialized.get('unmapped', {}).get('check_type', '') - severity = deserialized.get('severity', 'Info').capitalize() - aws_service_name = deserialized.get('resources', [{}])[0].get('group', {}).get('name', '') - impact = deserialized.get('risk_details') - mitigation = deserialized.get('remediation', {}).get("desc", '') - documentation = deserialized.get('remediation', {}).get("references", '') - documentation = str(documentation) + "\n" + str(deserialized.get('unmapped', {}).get('related_url', '')) - security_domain = deserialized.get('resources', [{}])[0].get('type', '') - timestamp = deserialized.get("event_time") - resource_arn = deserialized.get('resources', [{}])[0].get('uid', '') - resource_id = deserialized.get('resources', [{}])[0].get('name', '') - unique_id_from_tool = deserialized.get('finding_info', {}).get('uid', '') - if not resource_arn or resource_arn == "": - component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) - else: - component_name = resource_arn - - description = "**Issue:** " + str(result_extended) + \ - "\n**Description:** " + str(general_description) + \ - "\n**AWS Account:** " + str(account_id) + \ - "\n**Region:** " + str(region) + \ - "\n**AWS Service:** " + str(aws_service_name) + \ - "\n**Security Domain:** " + str(security_domain) + \ - "\n**Compliance:** " + str(compliance) + \ - "\n**ASFF Compliance Type:** " + str(asff_compliance_type) - - # improving key to get duplicates - dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() - if dupe_key in dupes: - find = dupes[dupe_key] - if description is not None: - find.description += description + "\n\n" - find.nb_occurences += 1 - else: - find = Finding( - title=textwrap.shorten(result_extended, 150), - cwe=1032, # Security Configuration Weaknesses, would like to fine tune - test=test, - description=description, - component_name=component_name, - unique_id_from_tool=unique_id_from_tool, - severity=severity, - references=documentation, - date=date.fromisoformat(timestamp[:10]), - static_finding=True, - dynamic_finding=False, - nb_occurences=1, - mitigation=mitigation, - impact=impact, - ) - dupes[dupe_key] = find - - return list(dupes.values()) - - def formatview(self, depth): - if depth > 1: - return "* " - else: - return "" diff --git a/unittests/scans/aws_prowler_v4/many_vuln.json b/unittests/scans/aws_prowler_v3/many_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v4/many_vuln.json rename to unittests/scans/aws_prowler_v3/many_vuln.ocsf.json diff --git a/unittests/scans/aws_prowler_v4/no_vuln.json b/unittests/scans/aws_prowler_v3/no_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v4/no_vuln.json rename to unittests/scans/aws_prowler_v3/no_vuln.ocsf.json diff --git a/unittests/scans/aws_prowler_v4/one_vuln.json b/unittests/scans/aws_prowler_v3/one_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v4/one_vuln.json rename to unittests/scans/aws_prowler_v3/one_vuln.ocsf.json diff --git a/unittests/tools/test_aws_prowler_v3_parser.py b/unittests/tools/test_aws_prowler_v3_parser.py index abbecc578ed..5d3079d8b79 100644 --- a/unittests/tools/test_aws_prowler_v3_parser.py +++ b/unittests/tools/test_aws_prowler_v3_parser.py @@ -38,3 +38,31 @@ def test_aws_prowler_parser_with_many_vuln_has_many_findings_json(self): with self.subTest(i=3): self.assertEqual("prowler-aws-account_maintain_current_contact_details-999999999999-us-east-1-999999999999", findings[2].unique_id_from_tool) self.assertIn('Maintain current contact details.', findings[2].description) + + def test_aws_prowler_parser_with_no_vuln_has_no_findings_ocsf_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v3/no_vuln.ocsf.json")) + self.assertEqual(0, len(findings)) + + def test_aws_prowler_parser_with_critical_vuln_has_one_findings_ocsf_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v3/one_vuln.ocsf.json")) + self.assertEqual(1, len(findings)) + self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) + self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) + self.assertEqual("arn:aws:iam::123456789012:role/myAdministratorExecutionRole", findings[0].component_name) + self.assertIn('https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege', findings[0].references) + + def test_aws_prowler_parser_with_many_vuln_has_many_findings_ocsf_json(self): + findings = self.setup( + open("unittests/scans/aws_prowler_v3/many_vuln.ocsf.json")) + self.assertEqual(3, len(findings)) + with self.subTest(i=0): + self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) + self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) + with self.subTest(i=1): + self.assertEqual("prowler-aws-iam_role_cross_account_readonlyaccess_policy-123456789012-us-east-1-AuditRole", findings[1].unique_id_from_tool) + self.assertIn('Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts', findings[1].description) + with self.subTest(i=3): + self.assertEqual("prowler-aws-iam_role_permissive_trust_relationship-123456789012-us-east-1-CrossAccountResourceAccessRole", findings[2].unique_id_from_tool) + self.assertIn('Ensure IAM Roles do not allow assume role from any role of a cross account', findings[2].description) \ No newline at end of file diff --git a/unittests/tools/test_aws_prowler_v4_parser.py b/unittests/tools/test_aws_prowler_v4_parser.py deleted file mode 100644 index 74e97050f6e..00000000000 --- a/unittests/tools/test_aws_prowler_v4_parser.py +++ /dev/null @@ -1,40 +0,0 @@ -from dojo.models import Test -from dojo.tools.aws_prowler_v4.parser import AWSProwlerV4Parser - -from ..dojo_test_case import DojoTestCase - - -class TestAwsProwlerV4Parser(DojoTestCase): - def setup(self, testfile): - parser = AWSProwlerV4Parser() - findings = parser.get_findings(testfile, Test()) - testfile.close() - return findings - - def test_aws_prowler_parser_with_no_vuln_has_no_findings_json(self): - findings = self.setup( - open("unittests/scans/aws_prowler_v4/no_vuln.json")) - self.assertEqual(0, len(findings)) - - def test_aws_prowler_parser_with_critical_vuln_has_one_findings_json(self): - findings = self.setup( - open("unittests/scans/aws_prowler_v4/one_vuln.json")) - self.assertEqual(1, len(findings)) - self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) - self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) - self.assertEqual("arn:aws:iam::123456789012:role/myAdministratorExecutionRole", findings[0].component_name) - self.assertIn('https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege', findings[0].references) - - def test_aws_prowler_parser_with_many_vuln_has_many_findings_json(self): - findings = self.setup( - open("unittests/scans/aws_prowler_v4/many_vuln.json")) - self.assertEqual(3, len(findings)) - with self.subTest(i=0): - self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) - self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) - with self.subTest(i=1): - self.assertEqual("prowler-aws-iam_role_cross_account_readonlyaccess_policy-123456789012-us-east-1-AuditRole", findings[1].unique_id_from_tool) - self.assertIn('Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts', findings[1].description) - with self.subTest(i=3): - self.assertEqual("prowler-aws-iam_role_permissive_trust_relationship-123456789012-us-east-1-CrossAccountResourceAccessRole", findings[2].unique_id_from_tool) - self.assertIn('Ensure IAM Roles do not allow assume role from any role of a cross account', findings[2].description) From 5109eaa532929bf1a7d3e7dbbce05d34d1578b31 Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 14:44:55 +0200 Subject: [PATCH 20/27] update aws_prowler_v3.md --- .../parsers/file/aws_prowler_v3.md | 101 ++++++++++++++++- .../parsers/file/aws_prowler_v4.md | 102 ------------------ 2 files changed, 96 insertions(+), 107 deletions(-) delete mode 100644 docs/content/en/integrations/parsers/file/aws_prowler_v4.md diff --git a/docs/content/en/integrations/parsers/file/aws_prowler_v3.md b/docs/content/en/integrations/parsers/file/aws_prowler_v3.md index 17dcf9698ae..687d9faf586 100644 --- a/docs/content/en/integrations/parsers/file/aws_prowler_v3.md +++ b/docs/content/en/integrations/parsers/file/aws_prowler_v3.md @@ -4,12 +4,16 @@ toc_hide: true --- ### File Types -DefectDojo parser accepts a .json file. Please note: earlier versions of AWS Prowler create output data in a different format. See our other documentation if you are using an earlier version of AWS Prowler: https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler/ +DefectDojo parser accepts a native `json` file produced by prowler v3 with file extension `.json` or a `ocsf-json` file produced by prowler v4 with file extension `.ocsf.json`. +Please note: earlier versions of AWS Prowler create output data in a different format. See our other [prowler parser documentation](https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler/) if you are using an earlier version of AWS Prowler. -JSON reports can be created from the [AWS Prowler V3 CLI](https://docs.prowler.cloud/en/latest/tutorials/reporting/#json) using the following command: `prowler -M json` +JSON reports can be created from the [AWS Prowler v3 CLI](https://docs.prowler.com/projects/prowler-open-source/en/v3/tutorials/reporting/#json) using the following command: `prowler -M json` -### Acceptable JSON Format -Parser expects an array of assessments. All properties are strings and are required by the parser. +JSON-OCSF reports can be created from the [AWS Prowler v4 CLI](https://docs.prowler.cloud/en/latest/tutorials/reporting/#json) using the following command: `prowler -M json-ocsf` + + +### Acceptable Prowler v3 JSON format +Parser expects an array of assessments. All properties are strings and are required by the parser. ~~~ @@ -68,5 +72,92 @@ Parser expects an array of assessments. All properties are strings and are requ ~~~ +### Acceptable Prowler v4 JSON-OCSF format +The parser expects an array of assessments. All properties are strings and are required by the parser. + +~~~ +[{ + "metadata": { + "event_code": "iam_role_administratoraccess_policy_permissive_trust_relationship", + "product": { + "name": "Prowler", + "vendor_name": "Prowler", + "version": "4.2.1" + }, + "version": "1.2.0" + }, + "severity_id": 4, + "severity": "High", + "status": "Suppressed", + "status_code": "FAIL", + "status_detail": "IAM Role myAdministratorExecutionRole has AdministratorAccess policy attached that has too permissive trust relationship.", + "status_id": 3, + "unmapped": { + "check_type": "", + "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator", + "categories": "trustboundaries", + "depends_on": "", + "related_to": "", + "notes": "CAF Security Epic: IAM", + "compliance": {} + }, + "activity_name": "Create", + "activity_id": 1, + "finding_info": { + "created_time": "2024-06-03T14:15:19.382075", + "desc": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "product_uid": "prowler", + "title": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", + "uid": "prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole" + }, + "resources": [ + { + "cloud_partition": "aws", + "region": "us-east-1", + "data": { + "details": "" + }, + "group": { + "name": "iam" + }, + "labels": [], + "name": "myAdministratorExecutionRole", + "type": "AwsIamRole", + "uid": "arn:aws:iam::123456789012:role/myAdministratorExecutionRole" + } + ], + "category_name": "Findings", + "category_uid": 2, + "class_name": "DetectionFinding", + "class_uid": 2004, + "cloud": { + "account": { + "name": "", + "type": "AWS_Account", + "type_id": 10, + "uid": "123456789012", + "labels": [] + }, + "org": { + "name": "", + "uid": "" + }, + "provider": "aws", + "region": "us-east-1" + }, + "event_time": "2024-06-03T14:15:19.382075", + "remediation": { + "desc": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege. If a role really needs AdministratorAccess, the trust relationship must be well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "references": [ + "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" + ] + }, + "risk_details": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It is therefore particularly important that the trust relationship is well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", + "type_uid": 200401, + "type_name": "Create" +}] + +~~~ + ### Sample Scan Data -Unit tests of AWS Prowler V3 JSON can be found at https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/aws_prowler_v3. \ No newline at end of file +Unit tests of AWS Prowler v3 JSON and Prowler v4 JSON-OCSF can be found at https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/aws_prowler_v3. \ No newline at end of file diff --git a/docs/content/en/integrations/parsers/file/aws_prowler_v4.md b/docs/content/en/integrations/parsers/file/aws_prowler_v4.md deleted file mode 100644 index aada1887ae1..00000000000 --- a/docs/content/en/integrations/parsers/file/aws_prowler_v4.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: "AWS Prowler V4" -toc_hide: true ---- - -### File Types -DefectDojo parser accepts a .json-ocsf file. Please note: earlier versions of AWS Prowler create output data in a different format. See our other documentation if you are using an earlier version of AWS Prowler: -* [Prowler v2](https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler/) -* [Prowler v3](https://documentation.defectdojo.com/integrations/parsers/file/aws_prowler_v3/) - -JSON reports can be created from the [AWS Prowler V4 CLI](https://docs.prowler.cloud/en/latest/tutorials/reporting/#json) using the following command: `prowler -M json-ocsf` - -### Acceptable JSON Format -The parser expects an array of assessments. All properties are strings and are required by the parser. - -~~~ - -[{ - "metadata": { - "event_code": "iam_role_administratoraccess_policy_permissive_trust_relationship", - "product": { - "name": "Prowler", - "vendor_name": "Prowler", - "version": "4.2.1" - }, - "version": "1.2.0" - }, - "severity_id": 4, - "severity": "High", - "status": "Suppressed", - "status_code": "FAIL", - "status_detail": "IAM Role myAdministratorExecutionRole has AdministratorAccess policy attached that has too permissive trust relationship.", - "status_id": 3, - "unmapped": { - "check_type": "", - "related_url": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_job-functions.html#jf_administrator", - "categories": "trustboundaries", - "depends_on": "", - "related_to": "", - "notes": "CAF Security Epic: IAM", - "compliance": {} - }, - "activity_name": "Create", - "activity_id": 1, - "finding_info": { - "created_time": "2024-06-03T14:15:19.382075", - "desc": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", - "product_uid": "prowler", - "title": "Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship", - "uid": "prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole" - }, - "resources": [ - { - "cloud_partition": "aws", - "region": "us-east-1", - "data": { - "details": "" - }, - "group": { - "name": "iam" - }, - "labels": [], - "name": "myAdministratorExecutionRole", - "type": "AwsIamRole", - "uid": "arn:aws:iam::123456789012:role/myAdministratorExecutionRole" - } - ], - "category_name": "Findings", - "category_uid": 2, - "class_name": "DetectionFinding", - "class_uid": 2004, - "cloud": { - "account": { - "name": "", - "type": "AWS_Account", - "type_id": 10, - "uid": "123456789012", - "labels": [] - }, - "org": { - "name": "", - "uid": "" - }, - "provider": "aws", - "region": "us-east-1" - }, - "event_time": "2024-06-03T14:15:19.382075", - "remediation": { - "desc": "Apply the principle of least privilege. Instead of AdministratorAccess, assign only the permissions necessary for specific roles and tasks. Create custom IAM policies with minimal permissions based on the principle of least privilege. If a role really needs AdministratorAccess, the trust relationship must be well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", - "references": [ - "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege" - ] - }, - "risk_details": "The AWS-managed AdministratorAccess policy grants all actions for all AWS services and for all resources in the account and as such exposes the customer to a significant data leakage threat. It is therefore particularly important that the trust relationship is well defined to restrict it usage only to the Principal, Action, Audience and Subject intended for it.", - "type_uid": 200401, - "type_name": "Create" -}] - -~~~ - -### Sample Scan Data -Unit tests of AWS Prowler V4 JSON-OCSF can be found at https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/aws_prowler_v4. \ No newline at end of file From acb507751cd456f3e27df391795d0201fd91e3b4 Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 14:47:43 +0200 Subject: [PATCH 21/27] revert settings --- dojo/settings/.settings.dist.py.sha256sum | 2 +- dojo/settings/settings.dist.py | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/dojo/settings/.settings.dist.py.sha256sum b/dojo/settings/.settings.dist.py.sha256sum index 8870b37793c..038b2cc762a 100644 --- a/dojo/settings/.settings.dist.py.sha256sum +++ b/dojo/settings/.settings.dist.py.sha256sum @@ -1 +1 @@ -a7b0e9897d9fe41abaad45297dab541ac687715ac9fd44a8b01805c237637c15 +8e116c33be5aacdf7d2712ce0d129348c91d25e464a139001d79ed0aa54465b4 diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index 7363505a60e..6c8da7074fe 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -1293,7 +1293,6 @@ def saml2_attrib_map_format(dict): 'Anchore Grype': True, 'AWS Prowler Scan': True, 'AWS Prowler V3': True, - 'AWS Prowler V4': True, 'Checkmarx Scan': False, 'Checkmarx OSA': True, 'Cloudsploit Scan': True, @@ -1378,8 +1377,7 @@ def saml2_attrib_map_format(dict): 'Aqua Scan': DEDUPE_ALGO_HASH_CODE, 'AuditJS Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'AWS Prowler Scan': DEDUPE_ALGO_HASH_CODE, - 'AWS Prowler V3': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, - 'AWS Prowler V4': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, + 'AWS Prowler V3': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL "AWS Security Finding Format (ASFF) Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Burp REST API': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Bandit Scan': DEDUPE_ALGO_HASH_CODE, From feb8e76552874e670bfdd42e832d3dbce9c7a7e2 Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 14:51:16 +0200 Subject: [PATCH 22/27] add modified .settings.dist.py.sha256sum --- dojo/settings/.settings.dist.py.sha256sum | 2 +- dojo/settings/settings.dist.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dojo/settings/.settings.dist.py.sha256sum b/dojo/settings/.settings.dist.py.sha256sum index 038b2cc762a..c0de37b5fe3 100644 --- a/dojo/settings/.settings.dist.py.sha256sum +++ b/dojo/settings/.settings.dist.py.sha256sum @@ -1 +1 @@ -8e116c33be5aacdf7d2712ce0d129348c91d25e464a139001d79ed0aa54465b4 +c0f6db3774e94fcfd22a5861d47e42cdd52839d01dd99ce361ecfd44f42b221c diff --git a/dojo/settings/settings.dist.py b/dojo/settings/settings.dist.py index 6c8da7074fe..b63bdc0f705 100644 --- a/dojo/settings/settings.dist.py +++ b/dojo/settings/settings.dist.py @@ -1377,7 +1377,7 @@ def saml2_attrib_map_format(dict): 'Aqua Scan': DEDUPE_ALGO_HASH_CODE, 'AuditJS Scan': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'AWS Prowler Scan': DEDUPE_ALGO_HASH_CODE, - 'AWS Prowler V3': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL + 'AWS Prowler V3': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, "AWS Security Finding Format (ASFF) Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Burp REST API': DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL, 'Bandit Scan': DEDUPE_ALGO_HASH_CODE, From 891081bb5a6bd7849fa359dac2d51e8ced282d8f Mon Sep 17 00:00:00 2001 From: kagahd Date: Mon, 10 Jun 2024 19:40:46 +0200 Subject: [PATCH 23/27] make ruff happy --- dojo/tools/aws_prowler_v3/parser.py | 1 - unittests/tools/test_aws_prowler_v3_parser.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/dojo/tools/aws_prowler_v3/parser.py b/dojo/tools/aws_prowler_v3/parser.py index 5796677766f..746ad839255 100644 --- a/dojo/tools/aws_prowler_v3/parser.py +++ b/dojo/tools/aws_prowler_v3/parser.py @@ -28,7 +28,6 @@ def get_findings(self, file, test): msg = 'Unknown file format' raise ValueError(msg) - def process_ocsf_json(self, file, test): dupes = {} diff --git a/unittests/tools/test_aws_prowler_v3_parser.py b/unittests/tools/test_aws_prowler_v3_parser.py index 5d3079d8b79..87d520411a5 100644 --- a/unittests/tools/test_aws_prowler_v3_parser.py +++ b/unittests/tools/test_aws_prowler_v3_parser.py @@ -65,4 +65,4 @@ def test_aws_prowler_parser_with_many_vuln_has_many_findings_ocsf_json(self): self.assertIn('Ensure IAM Roles do not have ReadOnlyAccess access for external AWS accounts', findings[1].description) with self.subTest(i=3): self.assertEqual("prowler-aws-iam_role_permissive_trust_relationship-123456789012-us-east-1-CrossAccountResourceAccessRole", findings[2].unique_id_from_tool) - self.assertIn('Ensure IAM Roles do not allow assume role from any role of a cross account', findings[2].description) \ No newline at end of file + self.assertIn('Ensure IAM Roles do not allow assume role from any role of a cross account', findings[2].description) From c5e1e1958d3099d3592714f7a50712ba131ba84c Mon Sep 17 00:00:00 2001 From: kagahd Date: Tue, 11 Jun 2024 09:29:37 +0200 Subject: [PATCH 24/27] separate prowler v3 and v4 parsers --- dojo/tools/aws_prowler_v3/parser.py | 181 ------------------ .../__init__.py | 0 dojo/tools/aws_prowler_v3plus/parser.py | 24 +++ dojo/tools/aws_prowler_v3plus/prowler_v3.py | 79 ++++++++ dojo/tools/aws_prowler_v3plus/prowler_v4.py | 83 ++++++++ ...r.py => test_aws_prowler_v3plus_parser.py} | 6 +- 6 files changed, 189 insertions(+), 184 deletions(-) delete mode 100644 dojo/tools/aws_prowler_v3/parser.py rename dojo/tools/{aws_prowler_v3 => aws_prowler_v3plus}/__init__.py (100%) create mode 100644 dojo/tools/aws_prowler_v3plus/parser.py create mode 100644 dojo/tools/aws_prowler_v3plus/prowler_v3.py create mode 100644 dojo/tools/aws_prowler_v3plus/prowler_v4.py rename unittests/tools/{test_aws_prowler_v3_parser.py => test_aws_prowler_v3plus_parser.py} (96%) diff --git a/dojo/tools/aws_prowler_v3/parser.py b/dojo/tools/aws_prowler_v3/parser.py deleted file mode 100644 index 746ad839255..00000000000 --- a/dojo/tools/aws_prowler_v3/parser.py +++ /dev/null @@ -1,181 +0,0 @@ - -import hashlib -import json -import textwrap -from datetime import date - -from dojo.models import Finding - - -class AWSProwlerV3Parser: - SCAN_TYPE = ["AWS Prowler V3"] - - def get_scan_types(self): - return AWSProwlerV3Parser.SCAN_TYPE - - def get_label_for_scan_types(self, scan_type): - return AWSProwlerV3Parser.SCAN_TYPE[0] - - def get_description_for_scan_types(self, scan_type): - return "Exports from AWS Prowler v3 in JSON format or from Prowler v4 in OCSF-JSON format." - - def get_findings(self, file, test): - if file.name.lower().endswith('.ocsf.json'): - return self.process_ocsf_json(file, test) - elif file.name.lower().endswith('.json'): - return self.process_json(file, test) - else: - msg = 'Unknown file format' - raise ValueError(msg) - - def process_ocsf_json(self, file, test): - dupes = {} - - data = json.load(file) - # mapping of json fields between Prowler v3 and v4: - # https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/reporting/#json - for deserialized in data: - - status = deserialized.get('status_code') - if status.upper() != 'FAIL': - continue - - account_id = deserialized.get('cloud', {}).get('account', {}).get("uid", '') - region = deserialized.get('resources', [{}])[0].get('region', '') - provider = deserialized.get('cloud', {}).get('provider', '') - compliance = '' - compliance_field = deserialized.get('unmapped', {}).get("compliance", {}) - if compliance_field: - compliance = ' | '.join([f"{key}:{','.join(value)}" for key, value in compliance_field.items()]) - result_extended = deserialized.get('status_detail') - general_description = deserialized.get('finding_info', {}).get('desc', '') - asff_compliance_type = deserialized.get('unmapped', {}).get('check_type', '') - severity = deserialized.get('severity', 'Info').capitalize() - aws_service_name = deserialized.get('resources', [{}])[0].get('group', {}).get('name', '') - impact = deserialized.get('risk_details') - mitigation = deserialized.get('remediation', {}).get("desc", '') - documentation = deserialized.get('remediation', {}).get("references", '') - documentation = str(documentation) + "\n" + str(deserialized.get('unmapped', {}).get('related_url', '')) - security_domain = deserialized.get('resources', [{}])[0].get('type', '') - timestamp = deserialized.get("event_time") - resource_arn = deserialized.get('resources', [{}])[0].get('uid', '') - resource_id = deserialized.get('resources', [{}])[0].get('name', '') - unique_id_from_tool = deserialized.get('finding_info', {}).get('uid', '') - if not resource_arn or resource_arn == "": - component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) - else: - component_name = resource_arn - - description = "**Issue:** " + str(result_extended) + \ - "\n**Description:** " + str(general_description) + \ - "\n**AWS Account:** " + str(account_id) + \ - "\n**Region:** " + str(region) + \ - "\n**AWS Service:** " + str(aws_service_name) + \ - "\n**Security Domain:** " + str(security_domain) + \ - "\n**Compliance:** " + str(compliance) + \ - "\n**ASFF Compliance Type:** " + str(asff_compliance_type) - - # improving key to get duplicates - dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() - if dupe_key in dupes: - find = dupes[dupe_key] - if description is not None: - find.description += description + "\n\n" - find.nb_occurences += 1 - else: - find = Finding( - title=textwrap.shorten(result_extended, 150), - cwe=1032, # Security Configuration Weaknesses, would like to fine tune - test=test, - description=description, - component_name=component_name, - unique_id_from_tool=unique_id_from_tool, - severity=severity, - references=documentation, - date=date.fromisoformat(timestamp[:10]), - static_finding=True, - dynamic_finding=False, - nb_occurences=1, - mitigation=mitigation, - impact=impact, - ) - dupes[dupe_key] = find - - return list(dupes.values()) - - def process_json(self, file, test): - dupes = {} - - data = json.load(file) - for deserialized in data: - - status = deserialized.get('Status') - if status.upper() != 'FAIL': - continue - - account = deserialized.get('AccountId') - region = deserialized.get('Region') - provider = deserialized.get('Provider') - compliance = str(deserialized.get('Compliance')) - result_extended = deserialized.get('StatusExtended') - general_description = deserialized.get('Description') - asff_compliance_type = " / ".join(deserialized.get('CheckType')) - severity = deserialized.get('Severity', 'Info').capitalize() - aws_service_name = deserialized.get('ServiceName') - impact = deserialized.get('Risk') - mitigation = deserialized.get('Remediation', {}).get('Recommendation', {}).get("Text", '') - mitigation = str(mitigation) + "\n" + str(deserialized.get('Remediation', {}).get('Code')) - documentation = deserialized.get('Remediation', {}).get('Recommendation', {}).get("Url") - documentation = str(documentation) + "\n" + str(deserialized.get('RelatedUrl')) - security_domain = deserialized.get('ResourceType') - timestamp = deserialized.get('AssessmentStartTime') - resource_arn = deserialized.get('ResourceArn') - account_id = deserialized.get('AccountId') - resource_id = deserialized.get('ResourceId') - unique_id_from_tool = deserialized.get('FindingUniqueId') - if not resource_arn or resource_arn == "": - component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) - else: - component_name = resource_arn - - description = "**Issue:** " + str(result_extended) + \ - "\n**Description:** " + str(general_description) + \ - "\n**AWS Account:** " + str(account) + " | **Region:** " + str(region) + \ - "\n**Compliance:** " + str(compliance) + \ - "\n**AWS Service:** " + str(aws_service_name) + \ - "\n**ASFF Compliance Type:** " + str(asff_compliance_type) + \ - "\n**Security Domain:** " + str(security_domain) - - # improving key to get duplicates - dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() - if dupe_key in dupes: - find = dupes[dupe_key] - if description is not None: - find.description += description + "\n\n" - find.nb_occurences += 1 - else: - find = Finding( - title=textwrap.shorten(result_extended, 150), - cwe=1032, # Security Configuration Weaknesses, would like to fine tune - test=test, - description=description, - component_name=component_name, - unique_id_from_tool=unique_id_from_tool, - severity=severity, - references=documentation, - date=date.fromisoformat(timestamp[:10]), - static_finding=True, - dynamic_finding=False, - nb_occurences=1, - mitigation=mitigation, - impact=impact, - ) - dupes[dupe_key] = find - - return list(dupes.values()) - - def formatview(self, depth): - if depth > 1: - return "* " - else: - return "" diff --git a/dojo/tools/aws_prowler_v3/__init__.py b/dojo/tools/aws_prowler_v3plus/__init__.py similarity index 100% rename from dojo/tools/aws_prowler_v3/__init__.py rename to dojo/tools/aws_prowler_v3plus/__init__.py diff --git a/dojo/tools/aws_prowler_v3plus/parser.py b/dojo/tools/aws_prowler_v3plus/parser.py new file mode 100644 index 00000000000..8e62047ac86 --- /dev/null +++ b/dojo/tools/aws_prowler_v3plus/parser.py @@ -0,0 +1,24 @@ +from dojo.tools.aws_prowler_v3plus.prowler_v3 import AWSProwlerV3Parser +from dojo.tools.aws_prowler_v3plus.prowler_v4 import AWSProwlerV4Parser + + +class AWSProwlerV3plusParser: + SCAN_TYPE = ["AWS Prowler V3"] + + def get_scan_types(self): + return AWSProwlerV3plusParser.SCAN_TYPE + + def get_label_for_scan_types(self, scan_type): + return AWSProwlerV3plusParser.SCAN_TYPE[0] + + def get_description_for_scan_types(self, scan_type): + return "Exports from AWS Prowler v3 in JSON format or from Prowler v4 in OCSF-JSON format." + + def get_findings(self, file, test): + if file.name.lower().endswith('.ocsf.json'): + return AWSProwlerV4Parser().process_ocsf_json(file, test) + elif file.name.lower().endswith('.json'): + return AWSProwlerV3Parser().process_json(file, test) + else: + msg = 'Unknown file format' + raise ValueError(msg) diff --git a/dojo/tools/aws_prowler_v3plus/prowler_v3.py b/dojo/tools/aws_prowler_v3plus/prowler_v3.py new file mode 100644 index 00000000000..cce0472b67d --- /dev/null +++ b/dojo/tools/aws_prowler_v3plus/prowler_v3.py @@ -0,0 +1,79 @@ +import hashlib +import json +import textwrap +from datetime import date + +from dojo.models import Finding + + +class AWSProwlerV3Parser: + def process_json(self, file, test): + dupes = {} + + data = json.load(file) + for deserialized in data: + + status = deserialized.get('Status') + if status.upper() != 'FAIL': + continue + + account = deserialized.get('AccountId') + region = deserialized.get('Region') + provider = deserialized.get('Provider') + compliance = str(deserialized.get('Compliance')) + result_extended = deserialized.get('StatusExtended') + general_description = deserialized.get('Description') + asff_compliance_type = " / ".join(deserialized.get('CheckType')) + severity = deserialized.get('Severity', 'Info').capitalize() + aws_service_name = deserialized.get('ServiceName') + impact = deserialized.get('Risk') + mitigation = deserialized.get('Remediation', {}).get('Recommendation', {}).get("Text", '') + mitigation = str(mitigation) + "\n" + str(deserialized.get('Remediation', {}).get('Code')) + documentation = deserialized.get('Remediation', {}).get('Recommendation', {}).get("Url") + documentation = str(documentation) + "\n" + str(deserialized.get('RelatedUrl')) + security_domain = deserialized.get('ResourceType') + timestamp = deserialized.get('AssessmentStartTime') + resource_arn = deserialized.get('ResourceArn') + account_id = deserialized.get('AccountId') + resource_id = deserialized.get('ResourceId') + unique_id_from_tool = deserialized.get('FindingUniqueId') + if not resource_arn or resource_arn == "": + component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) + else: + component_name = resource_arn + + description = "**Issue:** " + str(result_extended) + \ + "\n**Description:** " + str(general_description) + \ + "\n**AWS Account:** " + str(account) + " | **Region:** " + str(region) + \ + "\n**Compliance:** " + str(compliance) + \ + "\n**AWS Service:** " + str(aws_service_name) + \ + "\n**ASFF Compliance Type:** " + str(asff_compliance_type) + \ + "\n**Security Domain:** " + str(security_domain) + + # improving key to get duplicates + dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() + if dupe_key in dupes: + find = dupes[dupe_key] + if description is not None: + find.description += description + "\n\n" + find.nb_occurences += 1 + else: + find = Finding( + title=textwrap.shorten(result_extended, 150), + cwe=1032, # Security Configuration Weaknesses, would like to fine tune + test=test, + description=description, + component_name=component_name, + unique_id_from_tool=unique_id_from_tool, + severity=severity, + references=documentation, + date=date.fromisoformat(timestamp[:10]), + static_finding=True, + dynamic_finding=False, + nb_occurences=1, + mitigation=mitigation, + impact=impact, + ) + dupes[dupe_key] = find + + return list(dupes.values()) diff --git a/dojo/tools/aws_prowler_v3plus/prowler_v4.py b/dojo/tools/aws_prowler_v3plus/prowler_v4.py new file mode 100644 index 00000000000..ac3c4a99e6c --- /dev/null +++ b/dojo/tools/aws_prowler_v3plus/prowler_v4.py @@ -0,0 +1,83 @@ +import hashlib +import json +import textwrap +from datetime import date + +from dojo.models import Finding + + +class AWSProwlerV4Parser: + def process_ocsf_json(self, file, test): + dupes = {} + + data = json.load(file) + # mapping of json fields between Prowler v3 and v4: + # https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/reporting/#json + for deserialized in data: + + status = deserialized.get('status_code') + if status.upper() != 'FAIL': + continue + + account_id = deserialized.get('cloud', {}).get('account', {}).get("uid", '') + region = deserialized.get('resources', [{}])[0].get('region', '') + provider = deserialized.get('cloud', {}).get('provider', '') + compliance = '' + compliance_field = deserialized.get('unmapped', {}).get("compliance", {}) + if compliance_field: + compliance = ' | '.join([f"{key}:{','.join(value)}" for key, value in compliance_field.items()]) + result_extended = deserialized.get('status_detail') + general_description = deserialized.get('finding_info', {}).get('desc', '') + asff_compliance_type = deserialized.get('unmapped', {}).get('check_type', '') + severity = deserialized.get('severity', 'Info').capitalize() + aws_service_name = deserialized.get('resources', [{}])[0].get('group', {}).get('name', '') + impact = deserialized.get('risk_details') + mitigation = deserialized.get('remediation', {}).get("desc", '') + documentation = deserialized.get('remediation', {}).get("references", '') + documentation = str(documentation) + "\n" + str(deserialized.get('unmapped', {}).get('related_url', '')) + security_domain = deserialized.get('resources', [{}])[0].get('type', '') + timestamp = deserialized.get("event_time") + resource_arn = deserialized.get('resources', [{}])[0].get('uid', '') + resource_id = deserialized.get('resources', [{}])[0].get('name', '') + unique_id_from_tool = deserialized.get('finding_info', {}).get('uid', '') + if not resource_arn or resource_arn == "": + component_name = str(provider) + "-" + str(account_id) + "-" + str(region) + "-" + str(resource_id) + else: + component_name = resource_arn + + description = "**Issue:** " + str(result_extended) + \ + "\n**Description:** " + str(general_description) + \ + "\n**AWS Account:** " + str(account_id) + \ + "\n**Region:** " + str(region) + \ + "\n**AWS Service:** " + str(aws_service_name) + \ + "\n**Security Domain:** " + str(security_domain) + \ + "\n**Compliance:** " + str(compliance) + \ + "\n**ASFF Compliance Type:** " + str(asff_compliance_type) + + # improving key to get duplicates + dupe_key = hashlib.sha256(unique_id_from_tool.encode('utf-8')).hexdigest() + if dupe_key in dupes: + find = dupes[dupe_key] + if description is not None: + find.description += description + "\n\n" + find.nb_occurences += 1 + else: + find = Finding( + title=textwrap.shorten(result_extended, 150), + cwe=1032, # Security Configuration Weaknesses, would like to fine tune + test=test, + description=description, + component_name=component_name, + unique_id_from_tool=unique_id_from_tool, + severity=severity, + references=documentation, + date=date.fromisoformat(timestamp[:10]), + static_finding=True, + dynamic_finding=False, + nb_occurences=1, + mitigation=mitigation, + impact=impact, + ) + dupes[dupe_key] = find + + return list(dupes.values()) diff --git a/unittests/tools/test_aws_prowler_v3_parser.py b/unittests/tools/test_aws_prowler_v3plus_parser.py similarity index 96% rename from unittests/tools/test_aws_prowler_v3_parser.py rename to unittests/tools/test_aws_prowler_v3plus_parser.py index 87d520411a5..c79582677d6 100644 --- a/unittests/tools/test_aws_prowler_v3_parser.py +++ b/unittests/tools/test_aws_prowler_v3plus_parser.py @@ -1,12 +1,12 @@ from dojo.models import Test -from dojo.tools.aws_prowler_v3.parser import AWSProwlerV3Parser +from dojo.tools.aws_prowler_v3plus.parser import AWSProwlerV3plusParser from ..dojo_test_case import DojoTestCase -class TestAwsProwlerV3Parser(DojoTestCase): +class TestAwsProwlerV3plusParser(DojoTestCase): def setup(self, testfile): - parser = AWSProwlerV3Parser() + parser = AWSProwlerV3plusParser() findings = parser.get_findings(testfile, Test()) testfile.close() return findings From 5be8bc85ea5843319e87fcc0c2c692368dde448b Mon Sep 17 00:00:00 2001 From: kagahd Date: Tue, 11 Jun 2024 10:06:58 +0200 Subject: [PATCH 25/27] renaming --- .../{aws_prowler_v3.md => aws_prowler_v3plus.md} | 0 .../many_vuln.json | 0 .../many_vuln.ocsf.json | 0 .../no_vuln.json | 0 .../no_vuln.ocsf.json | 0 .../one_vuln.json | 0 .../one_vuln.ocsf.json | 0 unittests/tools/test_aws_prowler_v3plus_parser.py | 12 ++++++------ 8 files changed, 6 insertions(+), 6 deletions(-) rename docs/content/en/integrations/parsers/file/{aws_prowler_v3.md => aws_prowler_v3plus.md} (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/many_vuln.json (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/many_vuln.ocsf.json (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/no_vuln.json (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/no_vuln.ocsf.json (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/one_vuln.json (100%) rename unittests/scans/{aws_prowler_v3 => aws_prowler_v3plus}/one_vuln.ocsf.json (100%) diff --git a/docs/content/en/integrations/parsers/file/aws_prowler_v3.md b/docs/content/en/integrations/parsers/file/aws_prowler_v3plus.md similarity index 100% rename from docs/content/en/integrations/parsers/file/aws_prowler_v3.md rename to docs/content/en/integrations/parsers/file/aws_prowler_v3plus.md diff --git a/unittests/scans/aws_prowler_v3/many_vuln.json b/unittests/scans/aws_prowler_v3plus/many_vuln.json similarity index 100% rename from unittests/scans/aws_prowler_v3/many_vuln.json rename to unittests/scans/aws_prowler_v3plus/many_vuln.json diff --git a/unittests/scans/aws_prowler_v3/many_vuln.ocsf.json b/unittests/scans/aws_prowler_v3plus/many_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v3/many_vuln.ocsf.json rename to unittests/scans/aws_prowler_v3plus/many_vuln.ocsf.json diff --git a/unittests/scans/aws_prowler_v3/no_vuln.json b/unittests/scans/aws_prowler_v3plus/no_vuln.json similarity index 100% rename from unittests/scans/aws_prowler_v3/no_vuln.json rename to unittests/scans/aws_prowler_v3plus/no_vuln.json diff --git a/unittests/scans/aws_prowler_v3/no_vuln.ocsf.json b/unittests/scans/aws_prowler_v3plus/no_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v3/no_vuln.ocsf.json rename to unittests/scans/aws_prowler_v3plus/no_vuln.ocsf.json diff --git a/unittests/scans/aws_prowler_v3/one_vuln.json b/unittests/scans/aws_prowler_v3plus/one_vuln.json similarity index 100% rename from unittests/scans/aws_prowler_v3/one_vuln.json rename to unittests/scans/aws_prowler_v3plus/one_vuln.json diff --git a/unittests/scans/aws_prowler_v3/one_vuln.ocsf.json b/unittests/scans/aws_prowler_v3plus/one_vuln.ocsf.json similarity index 100% rename from unittests/scans/aws_prowler_v3/one_vuln.ocsf.json rename to unittests/scans/aws_prowler_v3plus/one_vuln.ocsf.json diff --git a/unittests/tools/test_aws_prowler_v3plus_parser.py b/unittests/tools/test_aws_prowler_v3plus_parser.py index c79582677d6..37d7c418854 100644 --- a/unittests/tools/test_aws_prowler_v3plus_parser.py +++ b/unittests/tools/test_aws_prowler_v3plus_parser.py @@ -13,12 +13,12 @@ def setup(self, testfile): def test_aws_prowler_parser_with_no_vuln_has_no_findings_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/no_vuln.json")) + open("unittests/scans/aws_prowler_v3plus/no_vuln.json")) self.assertEqual(0, len(findings)) def test_aws_prowler_parser_with_critical_vuln_has_one_findings_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/one_vuln.json")) + open("unittests/scans/aws_prowler_v3plus/one_vuln.json")) self.assertEqual(1, len(findings)) self.assertEqual("prowler-aws-acm_certificates_expiration_check-999999999999-us-east-1-api.sandbox.partner.teste.com", findings[0].unique_id_from_tool) self.assertIn('Check if ACM Certificates are about to expire in specific days or less', findings[0].description) @@ -27,7 +27,7 @@ def test_aws_prowler_parser_with_critical_vuln_has_one_findings_json(self): def test_aws_prowler_parser_with_many_vuln_has_many_findings_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/many_vuln.json")) + open("unittests/scans/aws_prowler_v3plus/many_vuln.json")) self.assertEqual(3, len(findings)) with self.subTest(i=0): self.assertEqual("prowler-aws-acm_certificates_expiration_check-999999999999-us-east-1-api.teste.teste.com", findings[0].unique_id_from_tool) @@ -41,12 +41,12 @@ def test_aws_prowler_parser_with_many_vuln_has_many_findings_json(self): def test_aws_prowler_parser_with_no_vuln_has_no_findings_ocsf_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/no_vuln.ocsf.json")) + open("unittests/scans/aws_prowler_v3plus/no_vuln.ocsf.json")) self.assertEqual(0, len(findings)) def test_aws_prowler_parser_with_critical_vuln_has_one_findings_ocsf_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/one_vuln.ocsf.json")) + open("unittests/scans/aws_prowler_v3plus/one_vuln.ocsf.json")) self.assertEqual(1, len(findings)) self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) self.assertIn('Ensure IAM Roles with attached AdministratorAccess policy have a well defined trust relationship', findings[0].description) @@ -55,7 +55,7 @@ def test_aws_prowler_parser_with_critical_vuln_has_one_findings_ocsf_json(self): def test_aws_prowler_parser_with_many_vuln_has_many_findings_ocsf_json(self): findings = self.setup( - open("unittests/scans/aws_prowler_v3/many_vuln.ocsf.json")) + open("unittests/scans/aws_prowler_v3plus/many_vuln.ocsf.json")) self.assertEqual(3, len(findings)) with self.subTest(i=0): self.assertEqual("prowler-aws-iam_role_administratoraccess_policy_permissive_trust_relationship-123456789012-us-east-1-myAdministratorExecutionRole", findings[0].unique_id_from_tool) From c0ed386c952e7df0db1e92449cb6c856c685bfda Mon Sep 17 00:00:00 2001 From: DefectDojo Date: Thu, 13 Jun 2024 14:35:11 +0000 Subject: [PATCH 26/27] Update helm lock file Signed-off-by: DefectDojo --- helm/defectdojo/Chart.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/helm/defectdojo/Chart.lock b/helm/defectdojo/Chart.lock index a4d2a57936f..ede55f29a2a 100644 --- a/helm/defectdojo/Chart.lock +++ b/helm/defectdojo/Chart.lock @@ -4,15 +4,15 @@ dependencies: version: 9.19.1 - name: postgresql repository: https://charts.bitnami.com/bitnami - version: 15.5.4 + version: 15.5.5 - name: postgresql-ha repository: https://charts.bitnami.com/bitnami version: 9.4.11 - name: rabbitmq repository: https://charts.bitnami.com/bitnami - version: 14.4.1 + version: 14.4.2 - name: redis repository: https://charts.bitnami.com/bitnami version: 19.5.2 -digest: sha256:bbb03050d96cdc317d8088bb62b53a0b892746fd1e5763f7c374da31d3d30895 -generated: "2024-06-06T21:33:03.331847962Z" +digest: sha256:8a444d1caaa146855dadc4bdaacd89c4fa5ce7bd0830d73f33bd8fc45e62b7a8 +generated: "2024-06-13T14:35:01.739817424Z" From 24a97f23b2a27c2397f70b1bd41910afecab5d2c Mon Sep 17 00:00:00 2001 From: kagahd Date: Wed, 3 Jul 2024 08:32:02 +0200 Subject: [PATCH 27/27] make ruff happy --- unittests/tools/test_aws_prowler_v3plus_parser.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/unittests/tools/test_aws_prowler_v3plus_parser.py b/unittests/tools/test_aws_prowler_v3plus_parser.py index 37d7c418854..5096c6275d0 100644 --- a/unittests/tools/test_aws_prowler_v3plus_parser.py +++ b/unittests/tools/test_aws_prowler_v3plus_parser.py @@ -1,7 +1,6 @@ from dojo.models import Test from dojo.tools.aws_prowler_v3plus.parser import AWSProwlerV3plusParser - -from ..dojo_test_case import DojoTestCase +from unittests.dojo_test_case import DojoTestCase class TestAwsProwlerV3plusParser(DojoTestCase):