From 6811a22651c195f885883ad5bf6445dc06a5baf8 Mon Sep 17 00:00:00 2001 From: Daniel Barranquero <74871504+danibarranqueroo@users.noreply.github.com> Date: Fri, 22 Nov 2024 16:14:24 +0100 Subject: [PATCH] feat(documentdb): add new fixer `documentdb_cluster_public_snapshot_fixer` (#5759) --- ...ocumentdb_cluster_public_snapshot_fixer.py | 44 +++++++++ ...ntdb_cluster_public_snapshot_fixer_test.py | 93 +++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 prowler/providers/aws/services/documentdb/documentdb_cluster_public_snapshot/documentdb_cluster_public_snapshot_fixer.py create mode 100644 tests/providers/aws/services/documentdb/documentdb_cluster_public_snapshot/documentdb_cluster_public_snapshot_fixer_test.py diff --git a/prowler/providers/aws/services/documentdb/documentdb_cluster_public_snapshot/documentdb_cluster_public_snapshot_fixer.py b/prowler/providers/aws/services/documentdb/documentdb_cluster_public_snapshot/documentdb_cluster_public_snapshot_fixer.py new file mode 100644 index 0000000000..e6423fabac --- /dev/null +++ b/prowler/providers/aws/services/documentdb/documentdb_cluster_public_snapshot/documentdb_cluster_public_snapshot_fixer.py @@ -0,0 +1,44 @@ +from prowler.lib.logger import logger +from prowler.providers.aws.services.documentdb.documentdb_client import ( + documentdb_client, +) + + +def fixer(resource_id: str, region: str) -> bool: + """ + Modify the attributes of a DocumentDB cluster snapshot to remove public access. + Specifically, this fixer removes the 'all' value from the 'restore' attribute to + prevent the snapshot from being publicly accessible. + + Requires the rds:ModifyDBClusterSnapshotAttribute permissions. + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "rds:ModifyDBClusterSnapshotAttribute", + "Resource": "*" + } + ] + } + + Args: + resource_id (str): The DB cluster snapshot identifier. + region (str): AWS region where the snapshot exists. + Returns: + bool: True if the operation is successful (public access is removed), False otherwise. + """ + try: + regional_client = documentdb_client.regional_clients[region] + regional_client.modify_db_cluster_snapshot_attribute( + DBClusterSnapshotIdentifier=resource_id, + AttributeName="restore", + ValuesToRemove=["all"], + ) + except Exception as error: + logger.error( + f"{region} -- {error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" + ) + return False + else: + return True diff --git a/tests/providers/aws/services/documentdb/documentdb_cluster_public_snapshot/documentdb_cluster_public_snapshot_fixer_test.py b/tests/providers/aws/services/documentdb/documentdb_cluster_public_snapshot/documentdb_cluster_public_snapshot_fixer_test.py new file mode 100644 index 0000000000..861230655c --- /dev/null +++ b/tests/providers/aws/services/documentdb/documentdb_cluster_public_snapshot/documentdb_cluster_public_snapshot_fixer_test.py @@ -0,0 +1,93 @@ +from unittest import mock + +import botocore +import botocore.client +from moto import mock_aws + +from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider + +mock_make_api_call = botocore.client.BaseClient._make_api_call + + +def mock_make_api_call_public_snapshot(self, operation_name, kwarg): + if operation_name == "ModifyDBClusterSnapshotAttribute": + return { + "DBClusterSnapshotAttributesResult": { + "DBClusterSnapshotAttributes": [ + { + "AttributeName": "restore", + "DBClusterSnapshotIdentifier": "test-snapshot", + "AttributeValues": [], + } + ] + } + } + return mock_make_api_call(self, operation_name, kwarg) + + +def mock_make_api_call_public_snapshot_error(self, operation_name, kwarg): + if operation_name == "ModifyDBClusterSnapshotAttribute": + raise botocore.exceptions.ClientError( + { + "Error": { + "Code": "DBClusterSnapshotNotFoundFault", + "Message": "DBClusterSnapshotNotFoundFault", + } + }, + operation_name, + ) + return mock_make_api_call(self, operation_name, kwarg) + + +class Test_documentdb_cluster_public_snapshot_fixer: + @mock_aws + def test_documentdb_cluster_public_snapshot_fixer(self): + with mock.patch( + "botocore.client.BaseClient._make_api_call", + new=mock_make_api_call_public_snapshot, + ): + from prowler.providers.aws.services.documentdb.documentdb_service import ( + DocumentDB, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.documentdb.documentdb_cluster_public_snapshot.documentdb_cluster_public_snapshot_fixer.documentdb_client", + new=DocumentDB(aws_provider), + ): + from prowler.providers.aws.services.documentdb.documentdb_cluster_public_snapshot.documentdb_cluster_public_snapshot_fixer import ( + fixer, + ) + + assert fixer(resource_id="test-snapshot", region=AWS_REGION_EU_WEST_1) + + @mock_aws + def test_documentdb_cluster_public_snapshot_fixer_error(self): + with mock.patch( + "botocore.client.BaseClient._make_api_call", + new=mock_make_api_call_public_snapshot_error, + ): + from prowler.providers.aws.services.documentdb.documentdb_service import ( + DocumentDB, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.documentdb.documentdb_cluster_public_snapshot.documentdb_cluster_public_snapshot_fixer.documentdb_client", + new=DocumentDB(aws_provider), + ): + from prowler.providers.aws.services.documentdb.documentdb_cluster_public_snapshot.documentdb_cluster_public_snapshot_fixer import ( + fixer, + ) + + assert not fixer( + resource_id="test-snapshot", region=AWS_REGION_EU_WEST_1 + )