Skip to content

Commit a7a8ef3

Browse files
committed
fix(aws): resolve regions and the global region in the ISO partitions
Three assumptions in the provider broke as soon as a partition was not one of the four the region matrix used to carry. get_available_aws_service_regions indexed the matrix directly, so an unknown service or partition raised a KeyError rather than reporting the service as unavailable. It now yields an empty set, the same outcome a service explicitly recorded as unavailable already produced. generate_regional_clients swallowed that KeyError, logged it and fell through without returning, so it handed back None while promising a dict. Callers then failed with "'NoneType' object has no attribute 'values'", with the real cause left behind in a log line. It now returns an empty dict and the service is simply not scanned. get_global_region returned the string "aws-iso-global" for anything matching "aws-iso". That is a botocore pseudo endpoint rather than a region, and matching on a substring collapsed the four ISO partitions into a single answer. It now reads the partition's global STS region from the botocore endpoints data, which returns exactly the values the hardcoded branches did for the existing partitions and a real region for each ISO one.
1 parent 5be68b2 commit a7a8ef3

5 files changed

Lines changed: 101 additions & 14 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`get_global_region()` now returns a real region for the ISO partitions (`us-iso-east-1`, `us-isob-east-1`, `eu-isoe-west-1`, `us-isof-east-1`) instead of the `aws-iso-global` pseudo endpoint, which also collapsed the four ISO partitions into one
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`AwsProvider.generate_regional_clients()` returns an empty dict instead of `None` when the regional clients cannot be built, and `AwsProvider.get_available_aws_service_regions()` returns an empty set for an unknown service or partition instead of raising, so a service unavailable in the audited partition is skipped rather than raising `AttributeError: 'NoneType' object has no attribute 'values'`

prowler/providers/aws/aws_provider.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,9 @@ def generate_regional_clients(
908908
logger.error(
909909
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
910910
)
911+
# Return an empty dict, as promised by the signature, so the service
912+
# is simply not scanned instead of the callers failing later on a None
913+
return {}
911914

912915
@staticmethod
913916
def get_available_aws_service_regions(
@@ -923,9 +926,13 @@ def get_available_aws_service_regions(
923926
924927
Returns:
925928
- A set of strings representing the available regions for the given service and partition.
929+
A service or a partition not present in the regions file yields an empty set, the same
930+
outcome as a service explicitly recorded as unavailable in the partition.
926931
"""
927932
data = read_aws_regions_file()
928-
json_regions = set(data["services"][service]["regions"][partition])
933+
json_regions = set(
934+
data["services"].get(service, {}).get("regions", {}).get(partition, [])
935+
)
929936
if audited_regions:
930937
# Get common regions between input and json
931938
regions = json_regions.intersection(audited_regions)
@@ -1132,16 +1139,14 @@ def get_global_region(self) -> str:
11321139
Example:
11331140
global_region = get_global_region()a
11341141
"""
1135-
global_region = "us-east-1"
1136-
if self._identity.partition == "aws-cn":
1137-
global_region = "cn-north-1"
1138-
elif self._identity.partition == "aws-eusc":
1139-
global_region = "eusc-de-east-1"
1140-
elif self._identity.partition == "aws-us-gov":
1141-
global_region = "us-gov-east-1"
1142-
elif "aws-iso" in self._identity.partition:
1143-
global_region = "aws-iso-global"
1144-
return global_region
1142+
# The first region of the partition is the one of its global STS endpoint,
1143+
# which is always a real region, never a pseudo endpoint like "aws-iso-global"
1144+
partition_regions = get_botocore_partition_regions().get(
1145+
self._identity.partition
1146+
)
1147+
if partition_regions:
1148+
return partition_regions[0]
1149+
return "us-east-1"
11451150

11461151
@staticmethod
11471152
def input_role_mfa_token_and_code() -> AWSMFAInfo:

tests/providers/aws/aws_provider_test.py

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
AWS_REGION_GOV_CLOUD_US_EAST_1,
6060
AWS_REGION_ISO_B_EAST_1,
6161
AWS_REGION_ISO_EAST_1,
62-
AWS_REGION_ISO_GLOBAL,
6362
AWS_REGION_ISO_WEST_1,
6463
AWS_REGION_US_EAST_1,
6564
AWS_REGION_US_EAST_2,
@@ -1185,6 +1184,13 @@ def test_get_default_region_non_global_service_unaffected(self):
11851184
== AWS_REGION_EU_WEST_1
11861185
)
11871186

1187+
@mock_aws
1188+
def test_aws_get_global_region(self):
1189+
aws_provider = AwsProvider()
1190+
aws_provider._identity.partition = AWS_COMMERCIAL_PARTITION
1191+
1192+
assert aws_provider.get_global_region() == AWS_REGION_US_EAST_1
1193+
11881194
@mock_aws
11891195
def test_aws_gov_get_global_region(self):
11901196
aws_provider = AwsProvider()
@@ -1204,7 +1210,21 @@ def test_aws_iso_get_global_region(self):
12041210
aws_provider = AwsProvider()
12051211
aws_provider._identity.partition = AWS_ISO_PARTITION
12061212

1207-
assert aws_provider.get_global_region() == AWS_REGION_ISO_GLOBAL
1213+
assert aws_provider.get_global_region() == AWS_REGION_ISO_EAST_1
1214+
1215+
@mock_aws
1216+
def test_aws_iso_b_get_global_region(self):
1217+
aws_provider = AwsProvider()
1218+
aws_provider._identity.partition = AWS_ISO_B_PARTITION
1219+
1220+
assert aws_provider.get_global_region() == AWS_REGION_ISO_B_EAST_1
1221+
1222+
@mock_aws
1223+
def test_get_global_region_for_an_unknown_partition(self):
1224+
aws_provider = AwsProvider()
1225+
aws_provider._identity.partition = "aws-unknown"
1226+
1227+
assert aws_provider.get_global_region() == AWS_REGION_US_EAST_1
12081228

12091229
@mock_aws
12101230
def test_aws_eusc_get_global_region(self):
@@ -1292,6 +1312,27 @@ def test_get_available_aws_service_regions_with_all_regions_audited(self):
12921312
len(aws_provider.get_available_aws_service_regions("ec2", "aws")) == 17
12931313
)
12941314

1315+
@mock_aws
1316+
def test_get_available_aws_service_regions_commercial_and_gov_cloud(self):
1317+
aws_provider = AwsProvider()
1318+
1319+
assert AWS_REGION_US_EAST_1 in aws_provider.get_available_aws_service_regions(
1320+
"ec2", AWS_COMMERCIAL_PARTITION
1321+
)
1322+
assert (
1323+
AWS_REGION_GOV_CLOUD_US_EAST_1
1324+
in aws_provider.get_available_aws_service_regions(
1325+
"ec2", AWS_GOV_CLOUD_PARTITION
1326+
)
1327+
)
1328+
# A service recorded as unavailable in the partition yields an empty set
1329+
assert (
1330+
aws_provider.get_available_aws_service_regions(
1331+
"bedrock-agent", AWS_CHINA_PARTITION
1332+
)
1333+
== set()
1334+
)
1335+
12951336
@mock_aws
12961337
def test_get_available_aws_service_regions_iso_partitions(self):
12971338
aws_provider = AwsProvider()
@@ -1313,6 +1354,46 @@ def test_get_available_aws_service_regions_iso_partitions(self):
13131354
== set()
13141355
)
13151356

1357+
@mock_aws
1358+
def test_get_available_aws_service_regions_unknown_partition(self):
1359+
aws_provider = AwsProvider()
1360+
1361+
assert (
1362+
aws_provider.get_available_aws_service_regions("ec2", "aws-unknown")
1363+
== set()
1364+
)
1365+
1366+
@mock_aws
1367+
def test_get_available_aws_service_regions_unknown_service(self):
1368+
aws_provider = AwsProvider()
1369+
1370+
assert (
1371+
aws_provider.get_available_aws_service_regions(
1372+
"unknown-service", AWS_COMMERCIAL_PARTITION
1373+
)
1374+
== set()
1375+
)
1376+
1377+
@mock_aws
1378+
def test_generate_regional_clients_service_not_in_partition(self):
1379+
aws_provider = AwsProvider()
1380+
aws_provider._identity.partition = AWS_ISO_PARTITION
1381+
1382+
response = aws_provider.generate_regional_clients("bedrock")
1383+
1384+
assert response == {}
1385+
1386+
@mock_aws
1387+
def test_generate_regional_clients_returns_empty_dict_on_error(self):
1388+
aws_provider = AwsProvider()
1389+
1390+
with patch.object(
1391+
AwsProvider,
1392+
"get_available_aws_service_regions",
1393+
side_effect=Exception("boom"),
1394+
):
1395+
assert aws_provider.generate_regional_clients("ec2") == {}
1396+
13161397
@mock_aws
13171398
def test_get_tagged_resources(self):
13181399
ec2_client = client("ec2", region_name=AWS_REGION_EU_CENTRAL_1)

tests/providers/aws/utils.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
AWS_REGION_GOV_CLOUD_US_EAST_1 = "us-gov-east-1"
5555

5656
# Iso Regions
57-
AWS_REGION_ISO_GLOBAL = "aws-iso-global"
5857
AWS_REGION_ISO_EAST_1 = "us-iso-east-1"
5958
AWS_REGION_ISO_WEST_1 = "us-iso-west-1"
6059
AWS_REGION_ISO_B_EAST_1 = "us-isob-east-1"

0 commit comments

Comments
 (0)