From 0d81bd457c9c162070748ba3fa5091d8bfd0bcb2 Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Date: Fri, 16 Jun 2023 12:17:09 +0200 Subject: [PATCH] fix(asff): handle empty Recommendation Url (#2496) Co-authored-by: Pepe Fagoaga --- prowler/lib/outputs/json.py | 27 ++++-- .../backup_plans_exist.metadata.json | 4 +- .../backup_reportplans_exist.metadata.json | 4 +- .../backup_vaults_encrypted.metadata.json | 6 +- .../backup_vaults_exist.metadata.json | 4 +- .../fms_policy_compliant.metadata.json | 2 +- ...ce_deprecated_engine_version.metadata.json | 6 +- ...incidents_enabled_with_plans.metadata.json | 2 +- .../vpc_different_regions.metadata.json | 2 +- .../vpc_subnet_different_az.metadata.json | 4 +- ...bnet_separate_private_public.metadata.json | 2 +- tests/lib/outputs/outputs_test.py | 97 ++++++++++++++++++- 12 files changed, 130 insertions(+), 30 deletions(-) diff --git a/prowler/lib/outputs/json.py b/prowler/lib/outputs/json.py index 1407354fa7..6713a13257 100644 --- a/prowler/lib/outputs/json.py +++ b/prowler/lib/outputs/json.py @@ -73,21 +73,16 @@ def fill_json_asff(finding_output, audit_info, finding, output_options): compliance_summary.append(item) # Ensures finding_status matches allowed values in ASFF - finding_status = "" - if finding.status == "PASS": - finding_status = "PASSED" - elif finding.status == "FAIL": - finding_status = "FAILED" - elif finding.status == "WARNING": - finding_status = "WARNING" - else: - finding_status = "NOT_AVAILABLE" + finding_status = generate_json_asff_status(finding.status) finding_output.Compliance = Compliance( Status=finding_status, AssociatedStandards=associated_standards, RelatedRequirements=compliance_summary, ) + # Fill Recommendation Url if it is blank + if not finding.check_metadata.Remediation.Recommendation.Url: + finding.check_metadata.Remediation.Recommendation.Url = "https://docs.aws.amazon.com/securityhub/latest/userguide/what-is-securityhub.html" finding_output.Remediation = { "Recommendation": finding.check_metadata.Remediation.Recommendation } @@ -95,6 +90,20 @@ def fill_json_asff(finding_output, audit_info, finding, output_options): return finding_output +def generate_json_asff_status(status: str) -> str: + json_asff_status = "" + if status == "PASS": + json_asff_status = "PASSED" + elif status == "FAIL": + json_asff_status = "FAILED" + elif status == "WARNING": + json_asff_status = "WARNING" + else: + json_asff_status = "NOT_AVAILABLE" + + return json_asff_status + + def fill_json_ocsf( finding_output: Check_Output_JSON_OCSF, audit_info, finding, output_options ): diff --git a/prowler/providers/aws/services/backup/backup_plans_exist/backup_plans_exist.metadata.json b/prowler/providers/aws/services/backup/backup_plans_exist/backup_plans_exist.metadata.json index 1de6796d02..9a630c335c 100644 --- a/prowler/providers/aws/services/backup/backup_plans_exist/backup_plans_exist.metadata.json +++ b/prowler/providers/aws/services/backup/backup_plans_exist/backup_plans_exist.metadata.json @@ -14,7 +14,7 @@ "ResourceType": "AwsBackupBackupPlan", "Description": "This check ensures that there is at least one backup plan in place.", "Risk": "Without a backup plan, an organization may be at risk of losing important data due to accidental deletion, system failures, or natural disasters. This can result in significant financial and reputational damage for the organization.", - "RelatedUrl": "", + "RelatedUrl": "https://docs.aws.amazon.com/aws-backup/latest/devguide/about-backup-plans.html", "Remediation": { "Code": { "CLI": "aws backup create-backup-plan --backup-plan --backup-plan-rule ", @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Use AWS Backup to create backup plans for your critical data and services.", - "Url": "" + "Url": "https://docs.aws.amazon.com/aws-backup/latest/devguide/about-backup-plans.html" } }, "Categories": [], diff --git a/prowler/providers/aws/services/backup/backup_reportplans_exist/backup_reportplans_exist.metadata.json b/prowler/providers/aws/services/backup/backup_reportplans_exist/backup_reportplans_exist.metadata.json index 3f83b9a5d2..0e84b55f3a 100644 --- a/prowler/providers/aws/services/backup/backup_reportplans_exist/backup_reportplans_exist.metadata.json +++ b/prowler/providers/aws/services/backup/backup_reportplans_exist/backup_reportplans_exist.metadata.json @@ -14,7 +14,7 @@ "ResourceType": "Other", "Description": "This check ensures that there is at least one backup report plan in place.", "Risk": "Without a backup report plan, an organization may lack visibility into the success or failure of backup operations.", - "RelatedUrl": "", + "RelatedUrl": "https://docs.aws.amazon.com/aws-backup/latest/devguide/create-report-plan-console.html", "Remediation": { "Code": { "CLI": "aws backup create-report-plan --report-plan-name --report-delivery-channel --report-setting ", @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Use AWS Backup to create backup report plans that provide visibility into the success or failure of backup operations.", - "Url": "" + "Url": "https://docs.aws.amazon.com/aws-backup/latest/devguide/create-report-plan-console.html" } }, "Categories": [], diff --git a/prowler/providers/aws/services/backup/backup_vaults_encrypted/backup_vaults_encrypted.metadata.json b/prowler/providers/aws/services/backup/backup_vaults_encrypted/backup_vaults_encrypted.metadata.json index 9bafabf234..6d568a450c 100644 --- a/prowler/providers/aws/services/backup/backup_vaults_encrypted/backup_vaults_encrypted.metadata.json +++ b/prowler/providers/aws/services/backup/backup_vaults_encrypted/backup_vaults_encrypted.metadata.json @@ -15,17 +15,17 @@ "ResourceType": "AwsBackupBackupVault", "Description": "This check ensures that AWS Backup vaults are encrypted with AWS KMS.", "Risk": "Without encryption using AWS KMS, an organization's backup data may be at risk of unauthorized access, which can lead to data breaches and other security incidents.", - "RelatedUrl": "", + "RelatedUrl": "https://docs.aws.amazon.com/aws-backup/latest/devguide/encryption.html", "Remediation": { "Code": { "CLI": "aws backup update-backup-vault --backup-vault-name --encryption-key-arn ", "NativeIaC": "", - "Other": "", + "Other": "https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/Athena/encrypted-with-cmk.html", "Terraform": "" }, "Recommendation": { "Text": "Use AWS KMS to encrypt your AWS Backup vaults and backup data.", - "Url": "" + "Url": "https://docs.aws.amazon.com/aws-backup/latest/devguide/encryption.html" } }, "Categories": [], diff --git a/prowler/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist.metadata.json b/prowler/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist.metadata.json index 36ca257f14..2828d212c8 100644 --- a/prowler/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist.metadata.json +++ b/prowler/providers/aws/services/backup/backup_vaults_exist/backup_vaults_exist.metadata.json @@ -14,7 +14,7 @@ "ResourceType": "AwsBackupBackupVault", "Description": "This check ensures that AWS Backup vaults exist to provide a secure and durable storage location for backup data.", "Risk": "Without an AWS Backup vault, an organization's critical data may be at risk of being lost in the event of an accidental deletion, system failures, or natural disasters.", - "RelatedUrl": "", + "RelatedUrl": "https://docs.aws.amazon.com/aws-backup/latest/devguide/vaults.html", "Remediation": { "Code": { "CLI": "aws backup create-backup-vault --backup-vault-name ", @@ -24,7 +24,7 @@ }, "Recommendation": { "Text": "Use AWS Backup to create backup vaults for your critical data and services.", - "Url": "" + "Url": "https://docs.aws.amazon.com/aws-backup/latest/devguide/vaults.html" } }, "Categories": [], diff --git a/prowler/providers/aws/services/fms/fms_policy_compliant/fms_policy_compliant.metadata.json b/prowler/providers/aws/services/fms/fms_policy_compliant/fms_policy_compliant.metadata.json index 35dd57fbea..b8d67bf4ac 100644 --- a/prowler/providers/aws/services/fms/fms_policy_compliant/fms_policy_compliant.metadata.json +++ b/prowler/providers/aws/services/fms/fms_policy_compliant/fms_policy_compliant.metadata.json @@ -20,7 +20,7 @@ }, "Recommendation": { "Text": "Ensure FMS is enabled and all the policies are compliant across your AWS accounts", - "Url": "" + "Url": "https://docs.aws.amazon.com/waf/latest/developerguide/getting-started-fms-intro.html" } }, "Categories": [], diff --git a/prowler/providers/aws/services/rds/rds_instance_deprecated_engine_version/rds_instance_deprecated_engine_version.metadata.json b/prowler/providers/aws/services/rds/rds_instance_deprecated_engine_version/rds_instance_deprecated_engine_version.metadata.json index 1b03550932..affb983673 100644 --- a/prowler/providers/aws/services/rds/rds_instance_deprecated_engine_version/rds_instance_deprecated_engine_version.metadata.json +++ b/prowler/providers/aws/services/rds/rds_instance_deprecated_engine_version/rds_instance_deprecated_engine_version.metadata.json @@ -10,17 +10,17 @@ "ResourceType": "AwsRdsDbInstance", "Description": "Check if RDS is using a supported engine version for MariaDB, MySQL and PostgreSQL", "Risk": "If not enabled RDS instances may be vulnerable to security issues", - "RelatedUrl": "", + "RelatedUrl": "https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-engine-versions.html", "Remediation": { "Code": { "CLI": "aws rds describe-db-engine-versions --engine '", "NativeIaC": "", - "Other": "https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-engine-versions.html", + "Other": "", "Terraform": "" }, "Recommendation": { "Text": "", - "Url": "" + "Url": "https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-engine-versions.html" } }, "Categories": [], diff --git a/prowler/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans.metadata.json b/prowler/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans.metadata.json index edf0c4251e..8393ff6c84 100644 --- a/prowler/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans.metadata.json +++ b/prowler/providers/aws/services/ssmincidents/ssmincidents_enabled_with_plans/ssmincidents_enabled_with_plans.metadata.json @@ -20,7 +20,7 @@ }, "Recommendation": { "Text": "Enable SSM Incidents and create response plans", - "Url": "" + "Url": "https://docs.aws.amazon.com/incident-manager/latest/userguide/response-plans.html" } }, "Categories": [], diff --git a/prowler/providers/aws/services/vpc/vpc_different_regions/vpc_different_regions.metadata.json b/prowler/providers/aws/services/vpc/vpc_different_regions/vpc_different_regions.metadata.json index 5b505e1831..3bb93b0007 100644 --- a/prowler/providers/aws/services/vpc/vpc_different_regions/vpc_different_regions.metadata.json +++ b/prowler/providers/aws/services/vpc/vpc_different_regions/vpc_different_regions.metadata.json @@ -22,7 +22,7 @@ }, "Recommendation": { "Text": "Ensure there are vpcs in more than one region", - "Url": "" + "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/vpc-example-private-subnets-nat.html" } }, "Categories": [], diff --git a/prowler/providers/aws/services/vpc/vpc_subnet_different_az/vpc_subnet_different_az.metadata.json b/prowler/providers/aws/services/vpc/vpc_subnet_different_az/vpc_subnet_different_az.metadata.json index 3dfa5a2280..0691a1f8d4 100644 --- a/prowler/providers/aws/services/vpc/vpc_subnet_different_az/vpc_subnet_different_az.metadata.json +++ b/prowler/providers/aws/services/vpc/vpc_subnet_different_az/vpc_subnet_different_az.metadata.json @@ -12,7 +12,7 @@ "ResourceType": "AwsEc2Vpc", "Description": "Ensure all vpc has subnets in more than one availability zone", "Risk": "", - "RelatedUrl": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenario2.html", + "RelatedUrl": "https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html", "Remediation": { "Code": { "CLI": "aws ec2 create-subnet", @@ -22,7 +22,7 @@ }, "Recommendation": { "Text": "Ensure all vpc has subnets in more than one availability zone", - "Url": "" + "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html" } }, "Categories": [], diff --git a/prowler/providers/aws/services/vpc/vpc_subnet_separate_private_public/vpc_subnet_separate_private_public.metadata.json b/prowler/providers/aws/services/vpc/vpc_subnet_separate_private_public/vpc_subnet_separate_private_public.metadata.json index 75660a262e..1a0bc2f3a1 100644 --- a/prowler/providers/aws/services/vpc/vpc_subnet_separate_private_public/vpc_subnet_separate_private_public.metadata.json +++ b/prowler/providers/aws/services/vpc/vpc_subnet_separate_private_public/vpc_subnet_separate_private_public.metadata.json @@ -22,7 +22,7 @@ }, "Recommendation": { "Text": "Ensure all vpc has public and private subnets defined", - "Url": "" + "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Scenario2.html" } }, "Categories": [], diff --git a/tests/lib/outputs/outputs_test.py b/tests/lib/outputs/outputs_test.py index ce5d3c82f0..3abb556603 100644 --- a/tests/lib/outputs/outputs_test.py +++ b/tests/lib/outputs/outputs_test.py @@ -24,7 +24,7 @@ from prowler.lib.check.compliance_models import ( ) from prowler.lib.check.models import Check_Report, load_check_metadata from prowler.lib.outputs.file_descriptors import fill_file_descriptors -from prowler.lib.outputs.json import fill_json_asff +from prowler.lib.outputs.json import fill_json_asff, generate_json_asff_status from prowler.lib.outputs.models import ( Check_Output_CSV, Check_Output_JSON_ASFF, @@ -428,8 +428,6 @@ class Test_Outputs: finding.status = "PASS" finding.status_extended = "This is a test" - input = Check_Output_JSON_ASFF() - expected = Check_Output_JSON_ASFF() expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}" expected.ProductArn = "arn:aws:securityhub:eu-west-1::product/prowler/prowler" @@ -462,6 +460,93 @@ class Test_Outputs: expected.Remediation = { "Recommendation": finding.check_metadata.Remediation.Recommendation } + + input = Check_Output_JSON_ASFF() + output_options = mock.MagicMock() + + assert ( + fill_json_asff(input, input_audit_info, finding, output_options) == expected + ) + + def test_fill_json_asff_without_remediation_recommendation_url(self): + input_audit_info = AWS_Audit_Info( + session_config=None, + original_session=None, + audit_session=None, + audited_account=AWS_ACCOUNT_ID, + audited_account_arn=f"arn:aws:iam::{AWS_ACCOUNT_ID}:root", + audited_identity_arn="test-arn", + audited_user_id="test", + audited_partition="aws", + profile="default", + profile_region="eu-west-1", + credentials=None, + assumed_role_info=None, + audited_regions=["eu-west-2", "eu-west-1"], + organizations_metadata=None, + audit_resources=None, + mfa_enabled=False, + ) + finding = Check_Report( + load_check_metadata( + f"{path.dirname(path.realpath(__file__))}/fixtures/metadata.json" + ).json() + ) + + # Empty the Remediation.Recomendation.URL + finding.check_metadata.Remediation.Recommendation.Url = "" + + finding.resource_details = "Test resource details" + finding.resource_id = "test-resource" + finding.resource_arn = "test-arn" + finding.region = "eu-west-1" + finding.status = "PASS" + finding.status_extended = "This is a test" + + expected = Check_Output_JSON_ASFF() + expected.Id = f"prowler-{finding.check_metadata.CheckID}-123456789012-eu-west-1-{hash_sha512('test-resource')}" + expected.ProductArn = "arn:aws:securityhub:eu-west-1::product/prowler/prowler" + expected.ProductFields = ProductFields( + ProviderVersion=prowler_version, ProwlerResourceName="test-arn" + ) + expected.GeneratorId = "prowler-" + finding.check_metadata.CheckID + expected.AwsAccountId = AWS_ACCOUNT_ID + expected.Types = finding.check_metadata.CheckType + expected.FirstObservedAt = ( + expected.UpdatedAt + ) = expected.CreatedAt = timestamp_utc.strftime("%Y-%m-%dT%H:%M:%SZ") + expected.Severity = Severity(Label=finding.check_metadata.Severity.upper()) + expected.Title = finding.check_metadata.CheckTitle + expected.Description = finding.status_extended + expected.Resources = [ + Resource( + Id="test-arn", + Type=finding.check_metadata.ResourceType, + Partition="aws", + Region="eu-west-1", + ) + ] + + expected.Compliance = Compliance( + Status="PASS" + "ED", + RelatedRequirements=[], + AssociatedStandards=[], + ) + + # Set the check's remediation + expected.Remediation = { + "Recommendation": finding.check_metadata.Remediation.Recommendation, + # "Code": finding.check_metadata.Remediation.Code, + } + + expected.Remediation[ + "Recommendation" + ].Text = finding.check_metadata.Remediation.Recommendation.Text + expected.Remediation[ + "Recommendation" + ].Url = "https://docs.aws.amazon.com/securityhub/latest/userguide/what-is-securityhub.html" + + input = Check_Output_JSON_ASFF() output_options = mock.MagicMock() assert ( @@ -832,3 +917,9 @@ class Test_Outputs: "CIS-1.4": ["2.1.3"], "CIS-1.5": ["2.1.3"], } + + def test_generate_json_asff_status(self): + assert generate_json_asff_status("PASS") == "PASSED" + assert generate_json_asff_status("FAIL") == "FAILED" + assert generate_json_asff_status("WARNING") == "WARNING" + assert generate_json_asff_status("SOMETHING ELSE") == "NOT_AVAILABLE"