diff --git a/README.md b/README.md index dc8650675c9..090cd90a433 100644 --- a/README.md +++ b/README.md @@ -63,9 +63,9 @@ It contains hundreds of controls covering CIS, NIST 800, NIST CSF, CISA, RBI, Fe | Provider | Checks | Services | [Compliance Frameworks](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/compliance/) | [Categories](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/misc/#categories) | |---|---|---|---|---| -| AWS | 457 | 67 -> `prowler aws --list-services` | 30 -> `prowler aws --list-compliance` | 9 -> `prowler aws --list-categories` | +| AWS | 553 | 77 -> `prowler aws --list-services` | 30 -> `prowler aws --list-compliance` | 9 -> `prowler aws --list-categories` | | GCP | 77 | 13 -> `prowler gcp --list-services` | 2 -> `prowler gcp --list-compliance` | 2 -> `prowler gcp --list-categories`| -| Azure | 136 | 17 -> `prowler azure --list-services` | 3 -> `prowler azure --list-compliance` | 2 -> `prowler azure --list-categories` | +| Azure | 138 | 17 -> `prowler azure --list-services` | 3 -> `prowler azure --list-compliance` | 2 -> `prowler azure --list-categories` | | Kubernetes | 83 | 7 -> `prowler kubernetes --list-services` | 1 -> `prowler kubernetes --list-compliance` | 7 -> `prowler kubernetes --list-categories` | # 💻 Installation diff --git a/prowler/providers/aws/aws_provider.py b/prowler/providers/aws/aws_provider.py index 37f41fa7e44..82d90ec61f5 100644 --- a/prowler/providers/aws/aws_provider.py +++ b/prowler/providers/aws/aws_provider.py @@ -1262,7 +1262,9 @@ def test_connection( logger.critical( f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}" ) - raise error + if raise_on_exception: + raise error + return Connection(error=error) @staticmethod def create_sts_session( diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az.metadata.json b/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az.metadata.json index 1b04b2463a4..73cf4a12364 100644 --- a/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az.metadata.json +++ b/prowler/providers/aws/services/autoscaling/autoscaling_group_multiple_az/autoscaling_group_multiple_az.metadata.json @@ -23,7 +23,9 @@ "Url": "https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-add-availability-zone.html" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/dms/dms_instance_multi_az_enabled/dms_instance_multi_az_enabled.metadata.json b/prowler/providers/aws/services/dms/dms_instance_multi_az_enabled/dms_instance_multi_az_enabled.metadata.json index 392068737bd..883488d3ad4 100644 --- a/prowler/providers/aws/services/dms/dms_instance_multi_az_enabled/dms_instance_multi_az_enabled.metadata.json +++ b/prowler/providers/aws/services/dms/dms_instance_multi_az_enabled/dms_instance_multi_az_enabled.metadata.json @@ -23,7 +23,9 @@ "Url": "https://www.trendmicro.com/cloudoneconformity-staging/knowledge-base/aws/DMS/multi-az.html#" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/elasticache/elasticache_redis_cluster_automatic_failover_enabled/elasticache_redis_cluster_automatic_failover_enabled.metadata.json b/prowler/providers/aws/services/elasticache/elasticache_redis_cluster_automatic_failover_enabled/elasticache_redis_cluster_automatic_failover_enabled.metadata.json index d0c0cc93336..b233843bf03 100644 --- a/prowler/providers/aws/services/elasticache/elasticache_redis_cluster_automatic_failover_enabled/elasticache_redis_cluster_automatic_failover_enabled.metadata.json +++ b/prowler/providers/aws/services/elasticache/elasticache_redis_cluster_automatic_failover_enabled/elasticache_redis_cluster_automatic_failover_enabled.metadata.json @@ -23,7 +23,9 @@ "Url": "https://redis.io/blog/highly-available-in-memory-cloud-datastores/" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/elasticache/elasticache_redis_cluster_multi_az_enabled/elasticache_redis_cluster_multi_az_enabled.metadata.json b/prowler/providers/aws/services/elasticache/elasticache_redis_cluster_multi_az_enabled/elasticache_redis_cluster_multi_az_enabled.metadata.json index 362f7e0a01d..42401dc23f6 100644 --- a/prowler/providers/aws/services/elasticache/elasticache_redis_cluster_multi_az_enabled/elasticache_redis_cluster_multi_az_enabled.metadata.json +++ b/prowler/providers/aws/services/elasticache/elasticache_redis_cluster_multi_az_enabled/elasticache_redis_cluster_multi_az_enabled.metadata.json @@ -23,7 +23,9 @@ "Url": "https://www.trendmicro.com/cloudoneconformity-staging/knowledge-base/aws/ElastiCache/elasticache-multi-az.html#" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/elb/elb_is_in_multiple_az/elb_is_in_multiple_az.metadata.json b/prowler/providers/aws/services/elb/elb_is_in_multiple_az/elb_is_in_multiple_az.metadata.json index c8571a7d2ae..c96d0229f3b 100644 --- a/prowler/providers/aws/services/elb/elb_is_in_multiple_az/elb_is_in_multiple_az.metadata.json +++ b/prowler/providers/aws/services/elb/elb_is_in_multiple_az/elb_is_in_multiple_az.metadata.json @@ -23,7 +23,9 @@ "Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/enable-disable-crosszone-lb.html" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/elbv2/elbv2_is_in_multiple_az/elbv2_is_in_multiple_az.metadata.json b/prowler/providers/aws/services/elbv2/elbv2_is_in_multiple_az/elbv2_is_in_multiple_az.metadata.json index aa83988cff9..9aa075b81dd 100644 --- a/prowler/providers/aws/services/elbv2/elbv2_is_in_multiple_az/elbv2_is_in_multiple_az.metadata.json +++ b/prowler/providers/aws/services/elbv2/elbv2_is_in_multiple_az/elbv2_is_in_multiple_az.metadata.json @@ -23,7 +23,9 @@ "Url": "https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-subnets.html" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/mq/mq_broker_cluster_deployment_mode/mq_broker_cluster_deployment_mode.metadata.json b/prowler/providers/aws/services/mq/mq_broker_cluster_deployment_mode/mq_broker_cluster_deployment_mode.metadata.json index dd33fa7ef54..cd89b12aed9 100644 --- a/prowler/providers/aws/services/mq/mq_broker_cluster_deployment_mode/mq_broker_cluster_deployment_mode.metadata.json +++ b/prowler/providers/aws/services/mq/mq_broker_cluster_deployment_mode/mq_broker_cluster_deployment_mode.metadata.json @@ -25,7 +25,9 @@ "Url": "https://docs.aws.amazon.com/amazon-mq/latest/developer-guide/rabbitmq-broker-architecture.html#rabbitmq-broker-architecture-cluster" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/neptune/neptune_cluster_multi_az/neptune_cluster_multi_az.metadata.json b/prowler/providers/aws/services/neptune/neptune_cluster_multi_az/neptune_cluster_multi_az.metadata.json index 68769914777..d80e6b2a99c 100644 --- a/prowler/providers/aws/services/neptune/neptune_cluster_multi_az/neptune_cluster_multi_az.metadata.json +++ b/prowler/providers/aws/services/neptune/neptune_cluster_multi_az/neptune_cluster_multi_az.metadata.json @@ -23,7 +23,9 @@ "Url": "https://docs.aws.amazon.com/securityhub/latest/userguide/neptune-controls.html#neptune-9" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/networkfirewall/networkfirewall_multi_az/networkfirewall_multi_az.metadata.json b/prowler/providers/aws/services/networkfirewall/networkfirewall_multi_az/networkfirewall_multi_az.metadata.json index b9d79644611..38591ee0338 100644 --- a/prowler/providers/aws/services/networkfirewall/networkfirewall_multi_az/networkfirewall_multi_az.metadata.json +++ b/prowler/providers/aws/services/networkfirewall/networkfirewall_multi_az/networkfirewall_multi_az.metadata.json @@ -25,7 +25,9 @@ "Url": "https://aws.amazon.com/es/blogs/networking-and-content-delivery/deployment-models-for-aws-network-firewall/" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/rds/rds_cluster_multi_az/rds_cluster_multi_az.metadata.json b/prowler/providers/aws/services/rds/rds_cluster_multi_az/rds_cluster_multi_az.metadata.json index 46f4b05ac03..a1bc501338d 100644 --- a/prowler/providers/aws/services/rds/rds_cluster_multi_az/rds_cluster_multi_az.metadata.json +++ b/prowler/providers/aws/services/rds/rds_cluster_multi_az/rds_cluster_multi_az.metadata.json @@ -23,7 +23,9 @@ "Url": "https://aws.amazon.com/rds/features/multi-az/" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/__init__.py b/prowler/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/prowler/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/rds_cluster_protected_by_backup_plan.metadata.json b/prowler/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/rds_cluster_protected_by_backup_plan.metadata.json new file mode 100644 index 00000000000..1739076a704 --- /dev/null +++ b/prowler/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/rds_cluster_protected_by_backup_plan.metadata.json @@ -0,0 +1,32 @@ +{ + "Provider": "aws", + "CheckID": "rds_cluster_protected_by_backup_plan", + "CheckTitle": "Check if RDS clusters are protected by a backup plan.", + "CheckType": [ + "Software and Configuration Checks, AWS Security Best Practices" + ], + "ServiceName": "rds", + "SubServiceName": "", + "ResourceIdTemplate": "arn:aws:rds:region:account-id:db-cluster", + "Severity": "medium", + "ResourceType": "AwsRdsDbInstance", + "Description": "Check if RDS clusters are protected by a backup plan.", + "Risk": "Without a backup plan, RDS clusters are vulnerable to data loss, accidental deletion, or corruption. This could lead to significant operational disruptions or loss of critical data.", + "RelatedUrl": "https://docs.aws.amazon.com/aws-backup/latest/devguide/assigning-resources.html", + "Remediation": { + "Code": { + "CLI": "aws backup create-backup-plan --backup-plan , aws backup tag-resource --resource-arn --tags Key=backup,Value=true", + "NativeIaC": "", + "Other": "https://docs.aws.amazon.com/securityhub/latest/userguide/rds-controls.html#rds-26", + "Terraform": "" + }, + "Recommendation": { + "Text": "Create a backup plan for the RDS cluster to protect it from data loss, accidental deletion, or corruption.", + "Url": "https://docs.aws.amazon.com/aws-backup/latest/devguide/assigning-resources.html" + } + }, + "Categories": [], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/rds_cluster_protected_by_backup_plan.py b/prowler/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/rds_cluster_protected_by_backup_plan.py new file mode 100644 index 00000000000..e1b81db62a4 --- /dev/null +++ b/prowler/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/rds_cluster_protected_by_backup_plan.py @@ -0,0 +1,33 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.backup.backup_client import backup_client +from prowler.providers.aws.services.rds.rds_client import rds_client + + +class rds_cluster_protected_by_backup_plan(Check): + def execute(self): + findings = [] + for db_cluster_arn, db_cluster in rds_client.db_clusters.items(): + report = Check_Report_AWS(self.metadata()) + report.region = db_cluster.region + report.resource_id = db_cluster.id + report.resource_arn = db_cluster_arn + report.resource_tags = db_cluster.tags + report.status = "FAIL" + report.status_extended = ( + f"RDS Cluster {db_cluster.id} is not protected by a backup plan." + ) + + if ( + db_cluster_arn in backup_client.protected_resources + or f"arn:{rds_client.audited_partition}:rds:*:*:cluster:*" + in backup_client.protected_resources + or "*" in backup_client.protected_resources + ): + report.status = "PASS" + report.status_extended = ( + f"RDS Cluster {db_cluster.id} is protected by a backup plan." + ) + + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/rds/rds_instance_multi_az/rds_instance_multi_az.metadata.json b/prowler/providers/aws/services/rds/rds_instance_multi_az/rds_instance_multi_az.metadata.json index 5678805ea30..86490bff109 100644 --- a/prowler/providers/aws/services/rds/rds_instance_multi_az/rds_instance_multi_az.metadata.json +++ b/prowler/providers/aws/services/rds/rds_instance_multi_az/rds_instance_multi_az.metadata.json @@ -23,7 +23,9 @@ "Url": "https://aws.amazon.com/rds/features/multi-az/" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" 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 25a5d938925..1f598988a7c 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 @@ -25,7 +25,9 @@ "Url": "https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html" } }, - "Categories": [], + "Categories": [ + "redundancy" + ], "DependsOn": [], "RelatedTo": [], "Notes": "" diff --git a/prowler/providers/gcp/gcp_provider.py b/prowler/providers/gcp/gcp_provider.py index 847480bdbfd..72529a563c1 100644 --- a/prowler/providers/gcp/gcp_provider.py +++ b/prowler/providers/gcp/gcp_provider.py @@ -411,7 +411,7 @@ def print_credentials(self): @staticmethod def get_projects( - credentials: Credentials, organization_id: str + credentials: Credentials, organization_id: str = None ) -> dict[str, GCPProject]: """ Get the projects accessible by the provided credentials. If an organization ID is provided, only the projects under that organization are returned. diff --git a/tests/providers/aws/aws_provider_test.py b/tests/providers/aws/aws_provider_test.py index 50dd38fb503..7f3895af23e 100644 --- a/tests/providers/aws/aws_provider_test.py +++ b/tests/providers/aws/aws_provider_test.py @@ -1443,6 +1443,18 @@ def test_test_connection_with_different_account_dont_raise(self): ) assert connection.error.code == 1015 + @mock_aws + def test_test_connection_generic_exception(self): + with patch( + "prowler.providers.aws.aws_provider.AwsProvider.setup_session", + side_effect=Exception(), + ): + connection = AwsProvider.test_connection(raise_on_exception=False) + + assert isinstance(connection, Connection) + assert not connection.is_connected + assert isinstance(connection.error, Exception) + @mock_aws def test_create_sts_session(self): current_session = session.Session() diff --git a/tests/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/rds_cluster_protected_by_backup_plan_test.py b/tests/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/rds_cluster_protected_by_backup_plan_test.py new file mode 100644 index 00000000000..8f293c6e916 --- /dev/null +++ b/tests/providers/aws/services/rds/rds_cluster_protected_by_backup_plan/rds_cluster_protected_by_backup_plan_test.py @@ -0,0 +1,416 @@ +from unittest import mock + +from moto import mock_aws + +from tests.providers.aws.utils import ( + AWS_ACCOUNT_NUMBER, + AWS_REGION_US_EAST_1, + set_mocked_aws_provider, +) + + +class Test_rds_cluster_protected_by_backup_plan: + @mock_aws + def test_rds_no_clusters(self): + from prowler.providers.aws.services.backup.backup_service import Backup + from prowler.providers.aws.services.rds.rds_service import RDS + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.rds_client", + new=RDS(aws_provider), + ), mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.backup_client", + new=Backup(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan import ( + rds_cluster_protected_by_backup_plan, + ) + + check = rds_cluster_protected_by_backup_plan() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_rds_cluster_no_existing_backup_plans(self): + cluster = mock.MagicMock() + backup = mock.MagicMock() + + from prowler.providers.aws.services.rds.rds_service import DBCluster + + arn = f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1" + cluster.db_clusters = { + arn: DBCluster( + id="db-cluster-1", + arn=f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1", + endpoint="db-cluster-1.c9akciq32.rds.amazonaws.com", + backtrack=1, + parameter_group="test", + engine_version="13.3", + status="available", + public=False, + encrypted=True, + deletion_protection=False, + auto_minor_version_upgrade=True, + multi_az=False, + username="admin", + iam_auth=False, + name="db-cluster-1", + region="us-east-1", + cluster_class="db.m1.small", + engine="aurora-postgres", + allocated_storage=10, + tags=[], + ) + } + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.rds_client", + new=cluster, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_client.rds_client", + new=cluster, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.backup_client", + new=backup, + ), mock.patch( + "prowler.providers.aws.services.backup.backup_client.backup_client", + new=backup, + ): + # Test Check + from prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan import ( + rds_cluster_protected_by_backup_plan, + ) + + check = rds_cluster_protected_by_backup_plan() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "RDS Cluster db-cluster-1 is not protected by a backup plan." + ) + assert result[0].resource_id == "db-cluster-1" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1" + ) + assert result[0].resource_tags == [] + + def test_rds_cluster_without_backup_plan(self): + cluster = mock.MagicMock() + backup = mock.MagicMock() + + from prowler.providers.aws.services.rds.rds_service import DBCluster + + arn = f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1" + cluster.db_clusters = { + arn: DBCluster( + id="db-cluster-1", + arn=f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1", + endpoint="db-cluster-1.c9akciq32.rds.amazonaws.com", + backtrack=1, + parameter_group="test", + engine_version="13.3", + status="available", + public=False, + encrypted=True, + deletion_protection=False, + auto_minor_version_upgrade=True, + multi_az=False, + username="admin", + iam_auth=False, + name="db-cluster-1", + region="us-east-1", + cluster_class="db.m1.small", + engine="aurora-postgres", + allocated_storage=10, + tags=[], + ) + } + + backup.protected_resources = [ + f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-master-2" + ] + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.rds_client", + new=cluster, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_client.rds_client", + new=cluster, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.backup_client", + new=backup, + ), mock.patch( + "prowler.providers.aws.services.backup.backup_client.backup_client", + new=backup, + ): + # Test Check + from prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan import ( + rds_cluster_protected_by_backup_plan, + ) + + check = rds_cluster_protected_by_backup_plan() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == "RDS Cluster db-cluster-1 is not protected by a backup plan." + ) + assert result[0].resource_id == "db-cluster-1" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1" + ) + assert result[0].resource_tags == [] + + def test_rds_cluster_with_backup_plan(self): + cluster = mock.MagicMock() + + from prowler.providers.aws.services.rds.rds_service import DBCluster + + arn = f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1" + cluster.db_clusters = { + arn: DBCluster( + id="db-cluster-1", + arn=f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1", + endpoint="db-cluster-1.c9akciq32.rds.amazonaws.com", + backtrack=1, + parameter_group="test", + engine_version="13.3", + status="available", + public=False, + encrypted=True, + deletion_protection=False, + auto_minor_version_upgrade=True, + multi_az=False, + username="admin", + iam_auth=False, + name="db-cluster-1", + region="us-east-1", + cluster_class="db.m1.small", + engine="aurora-postgres", + allocated_storage=10, + tags=[], + ) + } + + backup = mock.MagicMock() + backup.protected_resources = [arn] + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.rds_client", + new=cluster, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_client.rds_client", + new=cluster, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.backup_client", + new=backup, + ), mock.patch( + "prowler.providers.aws.services.backup.backup_client.backup_client", + new=backup, + ): + # Test Check + from prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan import ( + rds_cluster_protected_by_backup_plan, + ) + + check = rds_cluster_protected_by_backup_plan() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "RDS Cluster db-cluster-1 is protected by a backup plan." + ) + assert result[0].resource_id == "db-cluster-1" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1" + ) + assert result[0].resource_tags == [] + + def test_rds_cluster_with_backup_plan_via_cluster_wildcard(self): + cluster = mock.MagicMock() + cluster.audited_partition = "aws" + + from prowler.providers.aws.services.rds.rds_service import DBCluster + + arn = "arn:aws:rds:*:*:cluster:*" + cluster.db_clusters = { + f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1": DBCluster( + id="db-cluster-1", + arn=f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1", + endpoint="db-cluster-1.c9akciq32.rds.amazonaws.com", + backtrack=1, + parameter_group="test", + engine_version="13.3", + status="available", + public=False, + encrypted=True, + deletion_protection=False, + auto_minor_version_upgrade=True, + multi_az=False, + username="admin", + iam_auth=False, + name="db-cluster-1", + region="us-east-1", + cluster_class="db.m1.small", + engine="aurora-postgres", + allocated_storage=10, + tags=[], + ) + } + + backup = mock.MagicMock() + backup.protected_resources = [arn] + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.rds_client", + new=cluster, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_client.rds_client", + new=cluster, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.backup_client", + new=backup, + ), mock.patch( + "prowler.providers.aws.services.backup.backup_client.backup_client", + new=backup, + ): + # Test Check + from prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan import ( + rds_cluster_protected_by_backup_plan, + ) + + check = rds_cluster_protected_by_backup_plan() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "RDS Cluster db-cluster-1 is protected by a backup plan." + ) + assert result[0].resource_id == "db-cluster-1" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1" + ) + assert result[0].resource_tags == [] + + def test_rds_cluster_with_backup_plan_via_all_wildcard(self): + cluster = mock.MagicMock() + + from prowler.providers.aws.services.rds.rds_service import DBCluster + + arn = "*" + cluster.db_clusters = { + f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1": DBCluster( + id="db-cluster-1", + arn=f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1", + endpoint="db-cluster-1.c9akciq32.rds.amazonaws.com", + backtrack=1, + parameter_group="test", + engine_version="13.3", + status="available", + public=False, + encrypted=True, + deletion_protection=False, + auto_minor_version_upgrade=True, + multi_az=False, + username="admin", + iam_auth=False, + name="db-cluster-1", + region="us-east-1", + cluster_class="db.m1.small", + engine="aurora-postgres", + allocated_storage=10, + tags=[], + ) + } + + backup = mock.MagicMock() + backup.protected_resources = [arn] + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ): + with mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.rds_client", + new=cluster, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_client.rds_client", + new=cluster, + ), mock.patch( + "prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan.backup_client", + new=backup, + ), mock.patch( + "prowler.providers.aws.services.backup.backup_client.backup_client", + new=backup, + ): + # Test Check + from prowler.providers.aws.services.rds.rds_cluster_protected_by_backup_plan.rds_cluster_protected_by_backup_plan import ( + rds_cluster_protected_by_backup_plan, + ) + + check = rds_cluster_protected_by_backup_plan() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == "RDS Cluster db-cluster-1 is protected by a backup plan." + ) + assert result[0].resource_id == "db-cluster-1" + assert result[0].region == AWS_REGION_US_EAST_1 + assert ( + result[0].resource_arn + == f"arn:aws:rds:{AWS_REGION_US_EAST_1}:{AWS_ACCOUNT_NUMBER}:db:db-cluster-1" + ) + assert result[0].resource_tags == []