From 571141f57c5dc9cabff8cffd24f1d56a4903f2c7 Mon Sep 17 00:00:00 2001 From: Pepe Fagoaga Date: Wed, 25 Mar 2026 14:47:51 +0000 Subject: [PATCH] fix(aws): set partition's region for global services (#10458) --- prowler/CHANGELOG.md | 2 + prowler/providers/aws/aws_provider.py | 15 +++-- prowler/providers/aws/lib/service/service.py | 4 +- tests/providers/aws/aws_provider_test.py | 62 +++++++++++++++++++ .../providers/aws/lib/service/service_test.py | 44 +++++++++++++ 5 files changed, 120 insertions(+), 7 deletions(-) diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 72dfb0263e..31f1d431e5 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -21,6 +21,8 @@ All notable changes to the **Prowler SDK** are documented in this file. ### 🐞 Fixed +- Oracle Cloud `events_rule_idp_group_mapping_changes` now recognizes the CIS 3.1 `add/remove` event names to avoid false positives [(#10411)](https://github.com/prowler-cloud/prowler/issues/10411) +- AWS global services (CloudFront, Route53, Shield, FMS) now use the partition's global region instead of the profile's default region [(#10458)](https://github.com/prowler-cloud/prowler/issues/10458) - Oracle Cloud `events_rule_idp_group_mapping_changes` now recognizes the CIS 3.1 `add/remove` event names to avoid false positives [(#10416)](https://github.com/prowler-cloud/prowler/pull/10416) - Oracle Cloud password policy checks now exclude immutable system-managed policies (`SimplePasswordPolicy`, `StandardPasswordPolicy`) to avoid false positives [(#10453)](https://github.com/prowler-cloud/prowler/pull/10453) - Oracle Cloud `kms_key_rotation_enabled` now checks current key version age to avoid false positives on vaults without auto-rotation support [(#10450)](https://github.com/prowler-cloud/prowler/pull/10450) diff --git a/prowler/providers/aws/aws_provider.py b/prowler/providers/aws/aws_provider.py index e5adbd5022..9f0afe97f3 100644 --- a/prowler/providers/aws/aws_provider.py +++ b/prowler/providers/aws/aws_provider.py @@ -942,20 +942,23 @@ class AwsProvider(Provider): ) raise error - def get_default_region(self, service: str) -> str: - """get_default_region returns the default region based on the profile and audited service regions + def get_default_region(self, service: str, global_service: bool = False) -> str: + """get_default_region returns the default region based on the profile and audited service regions. + + For global services (CloudFront, Route53, Shield, FMS) the partition's + global region is always returned, ignoring profile and audited regions. Args: - service: The AWS service name + - global_service: If True, return the partition's global region directly Returns: - str: The default region for the given service - - Example: - service = "ec2" - default_region = get_default_region(service) """ try: + if global_service: + return self.get_global_region() + service_regions = AwsProvider.get_available_aws_service_regions( service, self._identity.partition, self._identity.audited_regions ) diff --git a/prowler/providers/aws/lib/service/service.py b/prowler/providers/aws/lib/service/service.py index 2444bf0b25..1044a21881 100644 --- a/prowler/providers/aws/lib/service/service.py +++ b/prowler/providers/aws/lib/service/service.py @@ -61,7 +61,9 @@ class AWSService: # Get a single region and client if the service needs it (e.g. AWS Global Service) # We cannot include this within an else because some services needs both the regional_clients # and a single client like S3 - self.region = provider.get_default_region(self.service) + self.region = provider.get_default_region( + self.service, global_service=global_service + ) self.client = self.session.client(self.service, self.region) # Thread pool for __threading_call__ diff --git a/tests/providers/aws/aws_provider_test.py b/tests/providers/aws/aws_provider_test.py index 81ae5b218c..68342a2735 100644 --- a/tests/providers/aws/aws_provider_test.py +++ b/tests/providers/aws/aws_provider_test.py @@ -943,6 +943,68 @@ aws: aws_provider._identity.profile_region = "non-existent-region" assert aws_provider.get_default_region("ec2") == AWS_REGION_EU_WEST_1 + @mock_aws + def test_get_default_region_global_service_ignores_profile_region(self): + region = [AWS_REGION_EU_WEST_1] + aws_provider = AwsProvider( + regions=region, + ) + aws_provider._identity.profile_region = AWS_REGION_EU_WEST_1 + + assert ( + aws_provider.get_default_region("cloudfront", global_service=True) + == AWS_REGION_US_EAST_1 + ) + + @mock_aws + def test_get_default_region_global_service_ignores_audited_regions(self): + region = [AWS_REGION_EU_WEST_1] + aws_provider = AwsProvider( + regions=region, + ) + aws_provider._identity.profile_region = None + + assert ( + aws_provider.get_default_region("route53", global_service=True) + == AWS_REGION_US_EAST_1 + ) + + @mock_aws + def test_get_default_region_global_service_china_partition(self): + aws_provider = AwsProvider() + aws_provider._identity.partition = AWS_CHINA_PARTITION + aws_provider._identity.profile_region = AWS_REGION_CN_NORTHWEST_1 + + assert ( + aws_provider.get_default_region("cloudfront", global_service=True) + == AWS_REGION_CN_NORTH_1 + ) + + @mock_aws + def test_get_default_region_global_service_gov_cloud_partition(self): + aws_provider = AwsProvider() + aws_provider._identity.partition = AWS_GOV_CLOUD_PARTITION + aws_provider._identity.profile_region = "us-gov-west-1" + + assert ( + aws_provider.get_default_region("shield", global_service=True) + == AWS_REGION_GOV_CLOUD_US_EAST_1 + ) + + @mock_aws + def test_get_default_region_non_global_service_unaffected(self): + """Ensure global_service=False (default) still follows profile region logic.""" + region = [AWS_REGION_EU_WEST_1] + aws_provider = AwsProvider( + regions=region, + ) + aws_provider._identity.profile_region = AWS_REGION_EU_WEST_1 + + assert ( + aws_provider.get_default_region("ec2", global_service=False) + == AWS_REGION_EU_WEST_1 + ) + @mock_aws def test_aws_gov_get_global_region(self): aws_provider = AwsProvider() diff --git a/tests/providers/aws/lib/service/service_test.py b/tests/providers/aws/lib/service/service_test.py index 8269c663f5..ea6768bdc1 100644 --- a/tests/providers/aws/lib/service/service_test.py +++ b/tests/providers/aws/lib/service/service_test.py @@ -4,7 +4,13 @@ from prowler.providers.aws.lib.service.service import AWSService from tests.providers.aws.utils import ( AWS_ACCOUNT_ARN, AWS_ACCOUNT_NUMBER, + AWS_CHINA_PARTITION, AWS_COMMERCIAL_PARTITION, + AWS_GOV_CLOUD_PARTITION, + AWS_REGION_CN_NORTH_1, + AWS_REGION_CN_NORTHWEST_1, + AWS_REGION_EU_WEST_1, + AWS_REGION_GOV_CLOUD_US_EAST_1, AWS_REGION_US_EAST_1, set_mocked_aws_provider, ) @@ -61,6 +67,44 @@ class TestAWSService: assert service.region == AWS_REGION_US_EAST_1 assert service.client.__class__.__name__ == "CloudFront" + def test_AWSService_global_service_uses_global_region_with_profile_region(self): + """Global services must use the partition's global region, not the profile region.""" + service_name = "cloudfront" + provider = set_mocked_aws_provider(profile_region=AWS_REGION_EU_WEST_1) + service = AWSService(service_name, provider, global_service=True) + + assert service.region == AWS_REGION_US_EAST_1 + + def test_AWSService_non_global_service_uses_profile_region(self): + """Non-global services should use the profile region when available.""" + service_name = "s3" + provider = set_mocked_aws_provider(profile_region=AWS_REGION_EU_WEST_1) + service = AWSService(service_name, provider) + + assert service.region == AWS_REGION_EU_WEST_1 + + def test_AWSService_global_service_china_partition(self): + """Global services in aws-cn partition should use cn-north-1.""" + service_name = "cloudfront" + provider = set_mocked_aws_provider( + audited_partition=AWS_CHINA_PARTITION, + profile_region=AWS_REGION_CN_NORTHWEST_1, + ) + service = AWSService(service_name, provider, global_service=True) + + assert service.region == AWS_REGION_CN_NORTH_1 + + def test_AWSService_global_service_gov_cloud_partition(self): + """Global services in aws-us-gov partition should use us-gov-east-1.""" + service_name = "cloudfront" + provider = set_mocked_aws_provider( + audited_partition=AWS_GOV_CLOUD_PARTITION, + profile_region="us-gov-west-1", + ) + service = AWSService(service_name, provider, global_service=True) + + assert service.region == AWS_REGION_GOV_CLOUD_US_EAST_1 + def test_AWSService_set_failed_check(self): AWSService.failed_checks.clear()