diff --git a/prowler/providers/aws/services/sqs/sqs_queues_not_publicly_accessible/sqs_queues_not_publicly_accessible_fixer.py b/prowler/providers/aws/services/sqs/sqs_queues_not_publicly_accessible/sqs_queues_not_publicly_accessible_fixer.py new file mode 100644 index 0000000000..a8e0e489d4 --- /dev/null +++ b/prowler/providers/aws/services/sqs/sqs_queues_not_publicly_accessible/sqs_queues_not_publicly_accessible_fixer.py @@ -0,0 +1,64 @@ +import json + +from prowler.lib.logger import logger +from prowler.providers.aws.services.sqs.sqs_client import sqs_client + + +def fixer(resource_id: str, region: str) -> bool: + """ + Modify the SQS queue's resource-based policy to remove public access and replace with trusted account access. + Specifically, this fixer checks if any statement has a public Principal (e.g., "*" or "CanonicalUser") + and replaces it with the ARN of the trusted AWS account. + Requires the sqs:SetQueueAttributes permission. + Permissions: + { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "sqs:SetQueueAttributes", + "Resource": "*" + } + ] + } + Args: + resource_id (str): The SQS queue name or ARN. + region (str): AWS region where the SQS queue exists. + Returns: + bool: True if the operation is successful (policy updated), False otherwise. + """ + try: + account_id = sqs_client.audited_account + audited_partition = sqs_client.audited_partition + + regional_client = sqs_client.regional_clients[region] + + queue_name = resource_id.split("/")[-1] + + trusted_policy = { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "ProwlerFixerStatement", + "Effect": "Allow", + "Principal": { + "AWS": account_id, + }, + "Action": "sqs:*", + "Resource": f"arn:{audited_partition}:sqs:{region}:{account_id}:{queue_name}", + } + ], + } + + regional_client.set_queue_attributes( + QueueUrl=resource_id, + Attributes={"Policy": json.dumps(trusted_policy)}, + ) + + 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/sqs/sqs_queues_not_publicly_accessible/sqs_queues_not_publicly_accessible_fixer_test.py b/tests/providers/aws/services/sqs/sqs_queues_not_publicly_accessible/sqs_queues_not_publicly_accessible_fixer_test.py new file mode 100644 index 0000000000..8603573e3a --- /dev/null +++ b/tests/providers/aws/services/sqs/sqs_queues_not_publicly_accessible/sqs_queues_not_publicly_accessible_fixer_test.py @@ -0,0 +1,106 @@ +from json import dumps +from unittest import mock + +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import AWS_REGION_EU_WEST_1, set_mocked_aws_provider + + +class Test_sqs_queues_not_publicly_accessible_fixer: + @mock_aws + def test_queue_public(self): + sqs_client = client("sqs", region_name=AWS_REGION_EU_WEST_1) + + queue_url = sqs_client.create_queue(QueueName="test-queue")["QueueUrl"] + + sqs_client.set_queue_attributes( + QueueUrl=queue_url, + Attributes={ + "Policy": dumps({"Statement": [{"Effect": "Allow", "Principal": "*"}]}) + }, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + from prowler.providers.aws.services.sqs.sqs_service import SQS + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible_fixer.sqs_client", + new=SQS(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible_fixer import ( + fixer, + ) + + assert fixer(queue_url, AWS_REGION_EU_WEST_1) + + @mock_aws + def test_queue_public_with_aws(self): + sqs_client = client("sqs", region_name=AWS_REGION_EU_WEST_1) + + queue_url = sqs_client.create_queue(QueueName="test-queue")["QueueUrl"] + + sqs_client.set_queue_attributes( + QueueUrl=queue_url, + Attributes={ + "Policy": dumps( + {"Statement": [{"Effect": "Allow", "Principal": {"AWS": "*"}}]} + ) + }, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + from prowler.providers.aws.services.sqs.sqs_service import SQS + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible_fixer.sqs_client", + new=SQS(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible_fixer import ( + fixer, + ) + + assert fixer(queue_url, AWS_REGION_EU_WEST_1) + + @mock_aws + def test_queue_public_error(self): + sqs_client = client("sqs", region_name=AWS_REGION_EU_WEST_1) + + queue_url = sqs_client.create_queue(QueueName="test-queue")["QueueUrl"] + + sqs_client.set_queue_attributes( + QueueUrl=queue_url, + Attributes={ + "Policy": dumps( + {"Statement": [{"Effect": "Allow", "Principal": {"AWS": "*"}}]} + ) + }, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_EU_WEST_1]) + + from prowler.providers.aws.services.sqs.sqs_service import SQS + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible_fixer.sqs_client", + new=SQS(aws_provider), + ): + # Test Fixer + from prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible_fixer import ( + fixer, + ) + + assert not fixer("queue_url_non_existing", AWS_REGION_EU_WEST_1) diff --git a/tests/providers/aws/services/sqs/sqs_queues_not_publicly_accessible/sqs_queues_not_publicly_accessible_test.py b/tests/providers/aws/services/sqs/sqs_queues_not_publicly_accessible/sqs_queues_not_publicly_accessible_test.py index 6f0c5197a5..b1d6ab3a78 100644 --- a/tests/providers/aws/services/sqs/sqs_queues_not_publicly_accessible/sqs_queues_not_publicly_accessible_test.py +++ b/tests/providers/aws/services/sqs/sqs_queues_not_publicly_accessible/sqs_queues_not_publicly_accessible_test.py @@ -110,7 +110,10 @@ class Test_sqs_queues_not_publicly_accessible: sqs_client.queues = [] with mock.patch( "prowler.providers.aws.services.sqs.sqs_service.SQS", - sqs_client, + new=sqs_client, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_client.sqs_client", + new=sqs_client, ): from prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible import ( sqs_queues_not_publicly_accessible, @@ -167,7 +170,10 @@ class Test_sqs_queues_not_publicly_accessible: ) with mock.patch( "prowler.providers.aws.services.sqs.sqs_service.SQS", - sqs_client, + new=sqs_client, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_client.sqs_client", + new=sqs_client, ): from prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible import ( sqs_queues_not_publicly_accessible, @@ -201,7 +207,10 @@ class Test_sqs_queues_not_publicly_accessible: ) with mock.patch( "prowler.providers.aws.services.sqs.sqs_service.SQS", - sqs_client, + new=sqs_client, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_client.sqs_client", + new=sqs_client, ): from prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible import ( sqs_queues_not_publicly_accessible, @@ -235,7 +244,10 @@ class Test_sqs_queues_not_publicly_accessible: ) with mock.patch( "prowler.providers.aws.services.sqs.sqs_service.SQS", - sqs_client, + new=sqs_client, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_client.sqs_client", + new=sqs_client, ): from prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible import ( sqs_queues_not_publicly_accessible, @@ -269,7 +281,10 @@ class Test_sqs_queues_not_publicly_accessible: ) with mock.patch( "prowler.providers.aws.services.sqs.sqs_service.SQS", - sqs_client, + new=sqs_client, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_client.sqs_client", + new=sqs_client, ): from prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible import ( sqs_queues_not_publicly_accessible, @@ -303,7 +318,10 @@ class Test_sqs_queues_not_publicly_accessible: ) with mock.patch( "prowler.providers.aws.services.sqs.sqs_service.SQS", - sqs_client, + new=sqs_client, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_client.sqs_client", + new=sqs_client, ): from prowler.providers.aws.services.sqs.sqs_queues_not_publicly_accessible.sqs_queues_not_publicly_accessible import ( sqs_queues_not_publicly_accessible, diff --git a/tests/providers/aws/services/sqs/sqs_queues_server_side_encryption_enabled/sqs_queues_server_side_encryption_enabled_test.py b/tests/providers/aws/services/sqs/sqs_queues_server_side_encryption_enabled/sqs_queues_server_side_encryption_enabled_test.py index e24b28b475..29d7f5ed05 100644 --- a/tests/providers/aws/services/sqs/sqs_queues_server_side_encryption_enabled/sqs_queues_server_side_encryption_enabled_test.py +++ b/tests/providers/aws/services/sqs/sqs_queues_server_side_encryption_enabled/sqs_queues_server_side_encryption_enabled_test.py @@ -18,7 +18,10 @@ class Test_sqs_queues_server_side_encryption_enabled: sqs_client.queues = [] with mock.patch( "prowler.providers.aws.services.sqs.sqs_service.SQS", - sqs_client, + new=sqs_client, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_client.sqs_client", + new=sqs_client, ): from prowler.providers.aws.services.sqs.sqs_queues_server_side_encryption_enabled.sqs_queues_server_side_encryption_enabled import ( sqs_queues_server_side_encryption_enabled, @@ -42,7 +45,10 @@ class Test_sqs_queues_server_side_encryption_enabled: ) with mock.patch( "prowler.providers.aws.services.sqs.sqs_service.SQS", - sqs_client, + new=sqs_client, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_client.sqs_client", + new=sqs_client, ): from prowler.providers.aws.services.sqs.sqs_queues_server_side_encryption_enabled.sqs_queues_server_side_encryption_enabled import ( sqs_queues_server_side_encryption_enabled, @@ -72,7 +78,10 @@ class Test_sqs_queues_server_side_encryption_enabled: ) with mock.patch( "prowler.providers.aws.services.sqs.sqs_service.SQS", - sqs_client, + new=sqs_client, + ), mock.patch( + "prowler.providers.aws.services.sqs.sqs_client.sqs_client", + new=sqs_client, ): from prowler.providers.aws.services.sqs.sqs_queues_server_side_encryption_enabled.sqs_queues_server_side_encryption_enabled import ( sqs_queues_server_side_encryption_enabled,