From d6c3c0c6c169a98c80269cf7a37b18bac6efce4f Mon Sep 17 00:00:00 2001 From: Sergio Garcia <38561120+sergargar@users.noreply.github.com> Date: Thu, 2 Mar 2023 10:18:27 +0100 Subject: [PATCH] feat(s3_bucket_level_public_access_block): new check (#1953) --- prowler/compliance/aws/cis_1.4_aws.json | 2 +- prowler/compliance/aws/cis_1.5_aws.json | 2 +- .../__init__.py | 0 ...et_level_public_access_block.metadata.json | 36 ++++ .../s3_bucket_level_public_access_block.py | 24 +++ ...3_bucket_level_public_access_block_test.py | 154 ++++++++++++++++++ 6 files changed, 216 insertions(+), 2 deletions(-) create mode 100644 prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/__init__.py create mode 100644 prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block.metadata.json create mode 100644 prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block.py create mode 100644 tests/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block_test.py diff --git a/prowler/compliance/aws/cis_1.4_aws.json b/prowler/compliance/aws/cis_1.4_aws.json index 1efe3492..2904f395 100644 --- a/prowler/compliance/aws/cis_1.4_aws.json +++ b/prowler/compliance/aws/cis_1.4_aws.json @@ -533,7 +533,7 @@ "Id": "2.1.5", "Description": "Ensure that S3 Buckets are configured with 'Block public access (bucket settings)'", "Checks": [ - "s3_account_level_public_access_blocks" + "s3_bucket_level_public_access_block" ], "Attributes": [ { diff --git a/prowler/compliance/aws/cis_1.5_aws.json b/prowler/compliance/aws/cis_1.5_aws.json index cf37b8e0..5350e07e 100644 --- a/prowler/compliance/aws/cis_1.5_aws.json +++ b/prowler/compliance/aws/cis_1.5_aws.json @@ -533,7 +533,7 @@ "Id": "2.1.5", "Description": "Ensure that S3 Buckets are configured with 'Block public access (bucket settings)'", "Checks": [ - "s3_account_level_public_access_blocks" + "s3_bucket_level_public_access_block" ], "Attributes": [ { diff --git a/prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/__init__.py b/prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block.metadata.json b/prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block.metadata.json new file mode 100644 index 00000000..dc9c7be2 --- /dev/null +++ b/prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block.metadata.json @@ -0,0 +1,36 @@ +{ + "Provider": "aws", + "CheckID": "s3_bucket_level_public_access_block", + "CheckTitle": "Check S3 Bucket Level Public Access Block.", + "CheckType": [ + "Data Protection" + ], + "ServiceName": "s3", + "SubServiceName": "", + "ResourceIdTemplate": "arn:partition:service:region:account-id:resource-id", + "Severity": "medium", + "ResourceType": "AwsS3Bucket", + "Description": "Check S3 Bucket Level Public Access Block.", + "Risk": "Public access policies may be applied to sensitive data buckets.", + "RelatedUrl": "https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html", + "Remediation": { + "Code": { + "CLI": "aws s3api put-public-access-block --region --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true --bucket ", + "NativeIaC": "", + "Other": "https://github.com/cloudmatos/matos/tree/master/remediations/aws/s3/s3/block-public-access", + "Terraform": "https://docs.bridgecrew.io/docs/bc_aws_s3_20#terraform" + }, + "Recommendation": { + "Text": "You can enable Public Access Block at the bucket level to prevent the exposure of your data stored in S3.", + "Url": "https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html" + } + }, + "Categories": [], + "Tags": { + "Tag1Key": "value", + "Tag2Key": "value" + }, + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block.py b/prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block.py new file mode 100644 index 00000000..6f4be96a --- /dev/null +++ b/prowler/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block.py @@ -0,0 +1,24 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.s3.s3_client import s3_client + + +class s3_bucket_level_public_access_block(Check): + def execute(self): + findings = [] + for bucket in s3_client.buckets: + report = Check_Report_AWS(self.metadata()) + report.region = bucket.region + report.resource_id = bucket.name + report.resource_arn = bucket.arn + report.status = "PASS" + report.status_extended = ( + f"Block Public Access is configured for the S3 Bucket {bucket.name}." + ) + if not ( + bucket.public_access_block.ignore_public_acls + and bucket.public_access_block.restrict_public_buckets + ): + report.status = "FAIL" + report.status_extended = f"Block Public Access is not configured for the S3 Bucket {bucket.name}." + findings.append(report) + return findings diff --git a/tests/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block_test.py b/tests/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block_test.py new file mode 100644 index 00000000..9b36484e --- /dev/null +++ b/tests/providers/aws/services/s3/s3_bucket_level_public_access_block/s3_bucket_level_public_access_block_test.py @@ -0,0 +1,154 @@ +from re import search +from unittest import mock + +from boto3 import client, session +from moto import mock_s3 + +from prowler.providers.aws.lib.audit_info.models import AWS_Audit_Info + +AWS_ACCOUNT_NUMBER = "123456789012" +AWS_REGION = "us-east-1" + + +class Test_s3_bucket_level_public_access_block: + # Mocked Audit Info + def set_mocked_audit_info(self): + audit_info = AWS_Audit_Info( + session_config=None, + original_session=None, + audit_session=session.Session( + profile_name=None, + botocore_session=None, + region_name=AWS_REGION, + ), + audited_account=AWS_ACCOUNT_NUMBER, + audited_user_id=None, + audited_partition="aws", + audited_identity_arn=None, + profile=None, + profile_region=AWS_REGION, + credentials=None, + assumed_role_info=None, + audited_regions=None, + organizations_metadata=None, + audit_resources=None, + ) + return audit_info + + @mock_s3 + def test_no_buckets(self): + from prowler.providers.aws.services.s3.s3_service import S3 + + audit_info = self.set_mocked_audit_info() + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=audit_info, + ): + with mock.patch( + "prowler.providers.aws.services.s3.s3_bucket_level_public_access_block.s3_bucket_level_public_access_block.s3_client", + new=S3(audit_info), + ): + # Test Check + from prowler.providers.aws.services.s3.s3_bucket_level_public_access_block.s3_bucket_level_public_access_block import ( + s3_bucket_level_public_access_block, + ) + + check = s3_bucket_level_public_access_block() + result = check.execute() + + assert len(result) == 0 + + @mock_s3 + def test_bucket_without_public_block(self): + s3_client = client("s3", region_name=AWS_REGION) + bucket_name_us = "bucket_test_us" + s3_client.create_bucket(Bucket=bucket_name_us) + s3_client.put_public_access_block( + Bucket=bucket_name_us, + PublicAccessBlockConfiguration={ + "BlockPublicAcls": False, + "IgnorePublicAcls": False, + "BlockPublicPolicy": False, + "RestrictPublicBuckets": False, + }, + ) + from prowler.providers.aws.services.s3.s3_service import S3 + + audit_info = self.set_mocked_audit_info() + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=audit_info, + ): + with mock.patch( + "prowler.providers.aws.services.s3.s3_bucket_level_public_access_block.s3_bucket_level_public_access_block.s3_client", + new=S3(audit_info), + ): + # Test Check + from prowler.providers.aws.services.s3.s3_bucket_level_public_access_block.s3_bucket_level_public_access_block import ( + s3_bucket_level_public_access_block, + ) + + check = s3_bucket_level_public_access_block() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert search( + "Block Public Access is not configured", + result[0].status_extended, + ) + assert result[0].resource_id == bucket_name_us + assert ( + result[0].resource_arn + == f"arn:{audit_info.audited_partition}:s3:::{bucket_name_us}" + ) + assert result[0].region == AWS_REGION + + @mock_s3 + def test_bucket_public_block(self): + s3_client = client("s3", region_name=AWS_REGION) + bucket_name_us = "bucket_test_us" + s3_client.create_bucket(Bucket=bucket_name_us) + s3_client.put_public_access_block( + Bucket=bucket_name_us, + PublicAccessBlockConfiguration={ + "BlockPublicAcls": True, + "IgnorePublicAcls": True, + "BlockPublicPolicy": True, + "RestrictPublicBuckets": True, + }, + ) + from prowler.providers.aws.services.s3.s3_service import S3 + + audit_info = self.set_mocked_audit_info() + + with mock.patch( + "prowler.providers.aws.lib.audit_info.audit_info.current_audit_info", + new=audit_info, + ): + with mock.patch( + "prowler.providers.aws.services.s3.s3_bucket_level_public_access_block.s3_bucket_level_public_access_block.s3_client", + new=S3(audit_info), + ): + # Test Check + from prowler.providers.aws.services.s3.s3_bucket_level_public_access_block.s3_bucket_level_public_access_block import ( + s3_bucket_level_public_access_block, + ) + + check = s3_bucket_level_public_access_block() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert search( + "Block Public Access is configured", + result[0].status_extended, + ) + assert result[0].resource_id == bucket_name_us + assert ( + result[0].resource_arn + == f"arn:{audit_info.audited_partition}:s3:::{bucket_name_us}" + ) + assert result[0].region == AWS_REGION