From e5dec1251d54d42b4cf1a5608407cac2e25781bc Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Tue, 11 Jun 2024 18:08:13 +0200 Subject: [PATCH] fix(s3): Send HTML also (#4240) --- prowler/config/config.py | 1 + prowler/lib/cli/parser.py | 3 +- prowler/providers/aws/lib/s3/s3.py | 6 +- .../prowler-output-123456789012.asff.json | 1 + .../fixtures/prowler-output-123456789012.html | 0 .../prowler-output-123456789012.ocsf.json | 1 + tests/providers/aws/lib/s3/s3_test.py | 149 ++++++++++++++++-- 7 files changed, 148 insertions(+), 13 deletions(-) create mode 100644 tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.asff.json create mode 100644 tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.html create mode 100644 tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.ocsf.json diff --git a/prowler/config/config.py b/prowler/config/config.py index 6cf473172d..7bdc2cef72 100644 --- a/prowler/config/config.py +++ b/prowler/config/config.py @@ -64,6 +64,7 @@ default_config_file_path = ( default_fixer_config_file_path = ( f"{pathlib.Path(os.path.dirname(os.path.realpath(__file__)))}/fixer_config.yaml" ) +available_output_formats = ["csv", "json-asff", "json-ocsf", "html"] def get_default_mute_file_path(provider: str): diff --git a/prowler/lib/cli/parser.py b/prowler/lib/cli/parser.py index 7155f37767..bb3af1de2f 100644 --- a/prowler/lib/cli/parser.py +++ b/prowler/lib/cli/parser.py @@ -5,6 +5,7 @@ from argparse import RawTextHelpFormatter from dashboard.lib.arguments.arguments import init_dashboard_parser from prowler.config.config import ( available_compliance_frameworks, + available_output_formats, check_current_version, default_config_file_path, default_fixer_config_file_path, @@ -147,7 +148,7 @@ Detailed documentation at https://docs.prowler.com nargs="+", help="Output modes, by default csv and json-oscf are saved. When using AWS Security Hub integration, json-asff output is also saved.", default=["csv", "json-ocsf", "html"], - choices=["csv", "json-asff", "json-ocsf", "html"], + choices=available_output_formats, ) common_outputs_parser.add_argument( "--output-filename", diff --git a/prowler/providers/aws/lib/s3/s3.py b/prowler/providers/aws/lib/s3/s3.py index e5b2763625..9cbeed23a7 100644 --- a/prowler/providers/aws/lib/s3/s3.py +++ b/prowler/providers/aws/lib/s3/s3.py @@ -1,5 +1,7 @@ from prowler.config.config import ( + available_output_formats, csv_file_suffix, + html_file_suffix, json_asff_file_suffix, json_ocsf_file_suffix, ) @@ -14,13 +16,15 @@ def send_to_s3_bucket( bucket_directory = get_s3_object_path(output_directory) filename = "" # Get only last part of the path - if output_mode in ["csv", "json-asff", "json-ocsf"]: + if output_mode in available_output_formats: if output_mode == "csv": filename = f"{output_filename}{csv_file_suffix}" elif output_mode == "json-asff": filename = f"{output_filename}{json_asff_file_suffix}" elif output_mode == "json-ocsf": filename = f"{output_filename}{json_ocsf_file_suffix}" + elif output_mode == "html": + filename = f"{output_filename}{html_file_suffix}" file_name = output_directory + "/" + filename object_name = bucket_directory + "/" + output_mode + "/" + filename else: # Compliance output mode diff --git a/tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.asff.json b/tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.asff.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.asff.json @@ -0,0 +1 @@ +{} diff --git a/tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.html b/tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.ocsf.json b/tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.ocsf.json new file mode 100644 index 0000000000..0967ef424b --- /dev/null +++ b/tests/providers/aws/lib/s3/fixtures/prowler-output-123456789012.ocsf.json @@ -0,0 +1 @@ +{} diff --git a/tests/providers/aws/lib/s3/s3_test.py b/tests/providers/aws/lib/s3/s3_test.py index 0ea57c1fa4..efddcd2a8d 100644 --- a/tests/providers/aws/lib/s3/s3_test.py +++ b/tests/providers/aws/lib/s3/s3_test.py @@ -5,30 +5,36 @@ import boto3 from mock import MagicMock from moto import mock_aws -from prowler.config.config import csv_file_suffix +from prowler.config.config import ( + csv_file_suffix, + html_file_suffix, + json_asff_file_suffix, + json_ocsf_file_suffix, +) from prowler.providers.aws.lib.s3.s3 import get_s3_object_path, send_to_s3_bucket - -AWS_ACCOUNT_ID = "123456789012" -AWS_REGION = "us-east-1" +from tests.providers.aws.utils import AWS_ACCOUNT_NUMBER, AWS_REGION_US_EAST_1 ACTUAL_DIRECTORY = Path(path.dirname(path.realpath(__file__))) FIXTURES_DIR_NAME = "fixtures" - S3_BUCKET_NAME = "test_bucket" - OUTPUT_MODE_CSV = "csv" +OUTPUT_MODE_JSON_OCSF = "json-ocsf" +OUTPUT_MODE_JSON_ASFF = "json-asff" +OUTPUT_MODE_HTML = "html" OUTPUT_MODE_CIS_1_4_AWS = "cis_1.4_aws" class TestS3: @mock_aws - def test_send_to_s3_bucket(self): + def test_send_to_s3_bucket_csv(self): # Mock Audit Info provider = MagicMock() # Create mock session - provider.current_session = boto3.session.Session(region_name=AWS_REGION) - provider.identity.account = AWS_ACCOUNT_ID + provider.current_session = boto3.session.Session( + region_name=AWS_REGION_US_EAST_1 + ) + provider.identity.account = AWS_ACCOUNT_NUMBER # Create mock bucket client = provider.current_session.client("s3") @@ -60,14 +66,135 @@ class TestS3: == "binary/octet-stream" ) + @mock_aws + def test_send_to_s3_bucket_json_ocsf(self): + # Mock Audit Info + provider = MagicMock() + + # Create mock session + provider.current_session = boto3.session.Session( + region_name=AWS_REGION_US_EAST_1 + ) + provider.identity.account = AWS_ACCOUNT_NUMBER + + # Create mock bucket + client = provider.current_session.client("s3") + client.create_bucket(Bucket=S3_BUCKET_NAME) + + # Mocked CSV output file + output_directory = f"{ACTUAL_DIRECTORY}/{FIXTURES_DIR_NAME}" + filename = f"prowler-output-{provider.identity.account}" + + # Send mock CSV file to mock S3 Bucket + send_to_s3_bucket( + filename, + output_directory, + OUTPUT_MODE_JSON_OCSF, + S3_BUCKET_NAME, + provider.current_session, + ) + + bucket_directory = get_s3_object_path(output_directory) + object_name = f"{bucket_directory}/{OUTPUT_MODE_JSON_OCSF}/{filename}{json_ocsf_file_suffix}" + + assert ( + client.get_object( + Bucket=S3_BUCKET_NAME, + Key=object_name, + )["ContentType"] + == "binary/octet-stream" + ) + + @mock_aws + def test_send_to_s3_bucket_json_asff(self): + # Mock Audit Info + provider = MagicMock() + + # Create mock session + provider.current_session = boto3.session.Session( + region_name=AWS_REGION_US_EAST_1 + ) + provider.identity.account = AWS_ACCOUNT_NUMBER + + # Create mock bucket + client = provider.current_session.client("s3") + client.create_bucket(Bucket=S3_BUCKET_NAME) + + # Mocked CSV output file + output_directory = f"{ACTUAL_DIRECTORY}/{FIXTURES_DIR_NAME}" + filename = f"prowler-output-{provider.identity.account}" + + # Send mock CSV file to mock S3 Bucket + send_to_s3_bucket( + filename, + output_directory, + OUTPUT_MODE_JSON_ASFF, + S3_BUCKET_NAME, + provider.current_session, + ) + + bucket_directory = get_s3_object_path(output_directory) + object_name = f"{bucket_directory}/{OUTPUT_MODE_JSON_ASFF}/{filename}{json_asff_file_suffix}" + + assert ( + client.get_object( + Bucket=S3_BUCKET_NAME, + Key=object_name, + )["ContentType"] + == "binary/octet-stream" + ) + + @mock_aws + def test_send_to_s3_bucket_html(self): + # Mock Audit Info + provider = MagicMock() + + # Create mock session + provider.current_session = boto3.session.Session( + region_name=AWS_REGION_US_EAST_1 + ) + provider.identity.account = AWS_ACCOUNT_NUMBER + + # Create mock bucket + client = provider.current_session.client("s3") + client.create_bucket(Bucket=S3_BUCKET_NAME) + + # Mocked CSV output file + output_directory = f"{ACTUAL_DIRECTORY}/{FIXTURES_DIR_NAME}" + filename = f"prowler-output-{provider.identity.account}" + + # Send mock CSV file to mock S3 Bucket + send_to_s3_bucket( + filename, + output_directory, + OUTPUT_MODE_HTML, + S3_BUCKET_NAME, + provider.current_session, + ) + + bucket_directory = get_s3_object_path(output_directory) + object_name = ( + f"{bucket_directory}/{OUTPUT_MODE_HTML}/{filename}{html_file_suffix}" + ) + + assert ( + client.get_object( + Bucket=S3_BUCKET_NAME, + Key=object_name, + )["ContentType"] + == "binary/octet-stream" + ) + @mock_aws def test_send_to_s3_bucket_compliance(self): # Mock Audit Info provider = MagicMock() # Create mock session - provider.current_session = boto3.session.Session(region_name=AWS_REGION) - provider.identity.account = AWS_ACCOUNT_ID + provider.current_session = boto3.session.Session( + region_name=AWS_REGION_US_EAST_1 + ) + provider.identity.account = AWS_ACCOUNT_NUMBER # Create mock bucket client = provider.current_session.client("s3")