From 424b44b131e9f13490cbacc0ebfad99bd76a58c5 Mon Sep 17 00:00:00 2001 From: Khushi_Kalantri Date: Tue, 15 Jul 2025 16:29:48 +0000 Subject: [PATCH 1/2] added test case for test_rds_instance_backup_enabled --- .../rds/test_rds_instance_backup_enabled.py | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 library/aws/tests/rds/test_rds_instance_backup_enabled.py diff --git a/library/aws/tests/rds/test_rds_instance_backup_enabled.py b/library/aws/tests/rds/test_rds_instance_backup_enabled.py new file mode 100644 index 00000000..c2bcd89c --- /dev/null +++ b/library/aws/tests/rds/test_rds_instance_backup_enabled.py @@ -0,0 +1,137 @@ +""" +Test for RDS instance backup enabled check. +""" + +import pytest +from unittest.mock import MagicMock +from botocore.exceptions import ClientError + +from library.aws.checks.rds.rds_instance_backup_enabled import rds_instance_backup_enabled +from tevico.engine.entities.report.check_model import CheckStatus, CheckMetadata +from tevico.engine.entities.report.check_model import Remediation, RemediationCode, RemediationRecommendation + + +class TestRdsInstanceBackupEnabled: + """Test cases for RDS backup enabled check.""" + + def setup_method(self): + """Set up test method.""" + metadata = CheckMetadata( + Provider="aws", + CheckID="rds_instance_backup_enabled", + CheckTitle="Ensure RDS instances have backup enabled.", + CheckType=[], + ServiceName="rds", + SubServiceName="", + ResourceIdTemplate="arn:aws:rds:region:account-id:db-instance", + Severity="medium", + ResourceType="AwsRdsDbInstance", + Description="Ensure RDS instances have backup enabled.", + Risk="If backup is not enabled, data is vulnerable. Human error or bad actors could erase or modify data.", + RelatedUrl="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html", + Remediation=Remediation( + Code=RemediationCode( + CLI="aws rds modify-db-instance --db-instance-identifier --backup-retention-period 7 --apply-immediately", + Terraform="https://docs.prowler.com/checks/aws/general-policies/ensure-that-rds-instances-have-backup-policy#terraform", + NativeIaC="", + Other="https://www.trendmicro.com/cloudoneconformity/knowledge-base/aws/RDS/rds-automated-backups-enabled.html", + ), + Recommendation=RemediationRecommendation( + Text="Enable automated backup for production data. Define a retention period and periodically test backup restoration. A Disaster Recovery process should be in place to govern Data Protection approach.", + Url="https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithAutomatedBackups.html" + ) + ), + Categories=[] + ) + + self.check = rds_instance_backup_enabled(metadata) + self.mock_session = MagicMock() + self.mock_client = MagicMock() + self.mock_session.client.return_value = self.mock_client + + def test_rds_backup_enabled(self): + """Test when backup is enabled on all RDS instances.""" + self.mock_client.describe_db_instances.return_value = { + "DBInstances": [ + { + "DBInstanceIdentifier": "db-1", + "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-1", + "BackupRetentionPeriod": 7 + } + ] + } + + report = self.check.execute(self.mock_session) + + assert report.status == CheckStatus.PASSED + assert len(report.resource_ids_status) == 1 + assert report.resource_ids_status[0].status == CheckStatus.PASSED + assert "Backup is enabled" in report.resource_ids_status[0].summary + + def test_rds_backup_not_enabled(self): + """Test when backup is not enabled on an RDS instance.""" + self.mock_client.describe_db_instances.return_value = { + "DBInstances": [ + { + "DBInstanceIdentifier": "db-2", + "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-2", + "BackupRetentionPeriod": 0 + } + ] + } + + report = self.check.execute(self.mock_session) + + assert report.status == CheckStatus.FAILED + assert len(report.resource_ids_status) == 1 + assert report.resource_ids_status[0].status == CheckStatus.FAILED + assert "NOT enabled" in report.resource_ids_status[0].summary + + def test_rds_backup_mixed_instances(self): + """Test with a mix of compliant and non-compliant RDS instances.""" + self.mock_client.describe_db_instances.return_value = { + "DBInstances": [ + { + "DBInstanceIdentifier": "db-1", + "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-1", + "BackupRetentionPeriod": 7 + }, + { + "DBInstanceIdentifier": "db-2", + "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-2", + "BackupRetentionPeriod": 0 + } + ] + } + + report = self.check.execute(self.mock_session) + + assert report.status == CheckStatus.FAILED + assert len(report.resource_ids_status) == 2 + assert any("NOT enabled" in res.summary for res in report.resource_ids_status) + assert any(res.status == CheckStatus.FAILED for res in report.resource_ids_status) + + def test_no_rds_instances(self): + """Test when no RDS instances are found.""" + self.mock_client.describe_db_instances.return_value = { + "DBInstances": [] + } + + report = self.check.execute(self.mock_session) + + assert report.status == CheckStatus.NOT_APPLICABLE + assert len(report.resource_ids_status) == 1 + assert report.resource_ids_status[0].status == CheckStatus.NOT_APPLICABLE + assert "No RDS instances found" in report.resource_ids_status[0].summary + + def test_rds_client_error(self): + """Test when describe_db_instances raises a ClientError.""" + error_response = {"Error": {"Code": "AccessDenied", "Message": "Not authorized"}} + self.mock_client.describe_db_instances.side_effect = ClientError(error_response, "DescribeDBInstances") + + report = self.check.execute(self.mock_session) + + assert report.status == CheckStatus.UNKNOWN + assert len(report.resource_ids_status) == 1 + assert report.resource_ids_status[0].status == CheckStatus.UNKNOWN + assert "Error retrieving RDS instance details" in report.resource_ids_status[0].summary From d87ed4b70d0d9c99fab4bc03266bb7a1d8df30a4 Mon Sep 17 00:00:00 2001 From: Khushi_Kalantri Date: Thu, 17 Jul 2025 01:17:24 +0000 Subject: [PATCH 2/2] Added testcase with required improvements --- .../rds/test_rds_instance_backup_enabled.py | 79 +++++++++++-------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/library/aws/tests/rds/test_rds_instance_backup_enabled.py b/library/aws/tests/rds/test_rds_instance_backup_enabled.py index c2bcd89c..3564909b 100644 --- a/library/aws/tests/rds/test_rds_instance_backup_enabled.py +++ b/library/aws/tests/rds/test_rds_instance_backup_enabled.py @@ -49,17 +49,19 @@ def setup_method(self): self.mock_client = MagicMock() self.mock_session.client.return_value = self.mock_client + def set_mock_describe_db_instances_response(self, instances): + """Helper to mock the describe_db_instances response.""" + self.mock_client.describe_db_instances.return_value = {"DBInstances": instances} + def test_rds_backup_enabled(self): """Test when backup is enabled on all RDS instances.""" - self.mock_client.describe_db_instances.return_value = { - "DBInstances": [ - { - "DBInstanceIdentifier": "db-1", - "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-1", - "BackupRetentionPeriod": 7 - } - ] - } + self.set_mock_describe_db_instances_response([ + { + "DBInstanceIdentifier": "db-1", + "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-1", + "BackupRetentionPeriod": 7 + } + ]) report = self.check.execute(self.mock_session) @@ -70,15 +72,13 @@ def test_rds_backup_enabled(self): def test_rds_backup_not_enabled(self): """Test when backup is not enabled on an RDS instance.""" - self.mock_client.describe_db_instances.return_value = { - "DBInstances": [ - { - "DBInstanceIdentifier": "db-2", - "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-2", - "BackupRetentionPeriod": 0 - } - ] - } + self.set_mock_describe_db_instances_response([ + { + "DBInstanceIdentifier": "db-2", + "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-2", + "BackupRetentionPeriod": 0 + } + ]) report = self.check.execute(self.mock_session) @@ -89,20 +89,18 @@ def test_rds_backup_not_enabled(self): def test_rds_backup_mixed_instances(self): """Test with a mix of compliant and non-compliant RDS instances.""" - self.mock_client.describe_db_instances.return_value = { - "DBInstances": [ - { - "DBInstanceIdentifier": "db-1", - "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-1", - "BackupRetentionPeriod": 7 - }, - { - "DBInstanceIdentifier": "db-2", - "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-2", - "BackupRetentionPeriod": 0 - } - ] - } + self.set_mock_describe_db_instances_response([ + { + "DBInstanceIdentifier": "db-1", + "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-1", + "BackupRetentionPeriod": 7 + }, + { + "DBInstanceIdentifier": "db-2", + "DBInstanceArn": "arn:aws:rds:us-east-1:123456789012:db:db-2", + "BackupRetentionPeriod": 0 + } + ]) report = self.check.execute(self.mock_session) @@ -113,9 +111,7 @@ def test_rds_backup_mixed_instances(self): def test_no_rds_instances(self): """Test when no RDS instances are found.""" - self.mock_client.describe_db_instances.return_value = { - "DBInstances": [] - } + self.set_mock_describe_db_instances_response([]) report = self.check.execute(self.mock_session) @@ -135,3 +131,16 @@ def test_rds_client_error(self): assert len(report.resource_ids_status) == 1 assert report.resource_ids_status[0].status == CheckStatus.UNKNOWN assert "Error retrieving RDS instance details" in report.resource_ids_status[0].summary + assert "AccessDenied" in report.resource_ids_status[0].exception + + def test_rds_generic_exception(self): + """Test when describe_db_instances raises a generic exception.""" + self.mock_client.describe_db_instances.side_effect = Exception("Unexpected error") + + report = self.check.execute(self.mock_session) + + assert report.status == CheckStatus.UNKNOWN + assert len(report.resource_ids_status) == 1 + assert report.resource_ids_status[0].status == CheckStatus.UNKNOWN + assert "Error retrieving RDS instance details" in report.resource_ids_status[0].summary + assert "Unexpected error" in report.resource_ids_status[0].exception