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 1407354f..6713a132 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 1de6796d..9a630c33 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 3f83b9a5..0e84b55f 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 9bafabf2..6d568a45 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 36ca257f..2828d212 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 35dd57fb..b8d67bf4 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 1b035509..affb9836 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 edf0c425..8393ff6c 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 5b505e18..3bb93b00 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 3dfa5a22..0691a1f8 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 75660a26..1a0bc2f3 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 ce5d3c82..3abb5566 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"