Skip to content

Commit 382a40f

Browse files
authored
Merge pull request #284 from saikirankv/arn_paths
Unexpected output when working with ARNs that have a path in them
2 parents e581075 + 8e07aea commit 382a40f

File tree

5 files changed

+117
-18
lines changed

5 files changed

+117
-18
lines changed

policy_sentry/bin/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"""
33
Policy Sentry is a tool for generating least-privilege IAM Policies.
44
"""
5-
__version__ = "0.10.0"
5+
__version__ = "0.11.0"
66
import click
77
from policy_sentry import command
88

policy_sentry/util/arns.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ def __init__(self, provided_arn):
3939
self.resource, self.resource_path = self.resource.split(":", 1)
4040
self.resource_string = self._resource_string()
4141

42+
def __repr__(self):
43+
return self.arn
44+
4245
# pylint: disable=too-many-return-statements
4346
def _resource_string(self):
4447
"""
@@ -96,8 +99,18 @@ def same_resource_type(self, arn_in_database):
9699
# 4b: If we have a confusing resource string, the length of the split resource string list
97100
# should at least be the same
98101
# Again, table/${TableName} (len of 2) should not match `table/${TableName}/backup/${BackupName}` (len of 4)
99-
if len(split_resource_string_to_test) != len(arn_format_list):
100-
return False
102+
# if len(split_resource_string_to_test) != len(arn_format_list):
103+
# return False
104+
105+
non_empty_arn_format_list = []
106+
for i in arn_format_list:
107+
if i != "":
108+
non_empty_arn_format_list.append(i)
109+
110+
lower_resource_string = list(map(lambda x:x.lower(),split_resource_string_to_test))
111+
for i in non_empty_arn_format_list:
112+
if i.lower() not in lower_resource_string:
113+
return False
101114

102115
# 4c: See if the non-normalized fields match
103116
for i in range(len(arn_format_list)):
@@ -116,14 +129,17 @@ def same_resource_type(self, arn_in_database):
116129

117130
# 4. Special type for S3 bucket objects and CodeCommit repos
118131
# Note: Each service can only have one of these, so these are definitely exceptions
119-
exclusion_list = ["${ObjectName}", "${RepositoryName}", "${BucketName}"]
132+
exclusion_list = ["${ObjectName}", "${RepositoryName}", "${BucketName}", "table/${TableName}", "${BucketName}/${ObjectName}"]
120133
resource_path_arn_in_database = elements[5]
121134
if resource_path_arn_in_database in exclusion_list:
122135
logger.debug("Special type: %s", resource_path_arn_in_database)
136+
# handling special case table/${TableName}
137+
if resource_string_arn_in_database in ["table/${TableName}", "${BucketName}"]:
138+
return len(self.resource_string.split('/')) == len(elements[5].split('/'))
123139
# If we've made it this far, then it is a special type
124140
# return True
125141
# Presence of / would mean it's an object in both so it matches
126-
if "/" in self.resource_string and "/" in elements[5]:
142+
elif "/" in self.resource_string and "/" in elements[5]:
127143
return True
128144
# / not being present in either means it's a bucket in both so it matches
129145
elif "/" not in self.resource_string and "/" not in elements[5]:

test/files/test_gh_204_multiple_resource_types_wildcards.json

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
"Effect": "Allow",
118118
"Action": [
119119
"rds:AddRoleToDBCluster",
120+
"rds:ApplyPendingMaintenanceAction",
120121
"rds:BacktrackDBCluster",
121122
"rds:CreateDBCluster",
122123
"rds:CreateDBClusterEndpoint",
@@ -361,41 +362,42 @@
361362
"Effect": "Allow",
362363
"Action": [
363364
"rds:DescribeDBClusterBacktracks",
365+
"rds:DescribeDBClusterEndpoints",
364366
"rds:DescribeDBClusters",
365-
"rds:DescribeDBProxyTargets"
367+
"rds:DescribeDBProxyTargets",
368+
"rds:DescribePendingMaintenanceActions"
366369
],
367370
"Resource": [
368371
"arn:aws:rds:us-east-1:123456789012:*:*"
369372
]
370373
},
371374
{
372-
"Sid": "RdsListClusterpg",
375+
"Sid": "RdsListClusterendpoint",
373376
"Effect": "Allow",
374377
"Action": [
375-
"rds:DescribeDBClusterParameterGroups",
376-
"rds:DescribeDBClusterParameters"
378+
"rds:DescribeDBClusterEndpoints"
377379
],
378380
"Resource": [
379381
"arn:aws:rds:us-east-1:123456789012:*:*"
380382
]
381383
},
382384
{
383-
"Sid": "RdsListClustersnapshot",
385+
"Sid": "RdsListClusterpg",
384386
"Effect": "Allow",
385387
"Action": [
386-
"rds:DescribeDBClusterSnapshotAttributes"
388+
"rds:DescribeDBClusterParameterGroups",
389+
"rds:DescribeDBClusterParameters"
387390
],
388391
"Resource": [
389392
"arn:aws:rds:us-east-1:123456789012:*:*"
390393
]
391394
},
392395
{
393-
"Sid": "RdsListPg",
396+
"Sid": "RdsListClustersnapshot",
394397
"Effect": "Allow",
395398
"Action": [
396-
"rds:DescribeDBEngineVersions",
397-
"rds:DescribeDBParameterGroups",
398-
"rds:DescribeDBParameters"
399+
"rds:DescribeDBClusterSnapshotAttributes",
400+
"rds:DescribeDBClusterSnapshots"
399401
],
400402
"Resource": [
401403
"arn:aws:rds:us-east-1:123456789012:*:*"
@@ -405,6 +407,8 @@
405407
"Sid": "RdsListDb",
406408
"Effect": "Allow",
407409
"Action": [
410+
"rds:DescribeDBInstanceAutomatedBackups",
411+
"rds:DescribeDBInstances",
408412
"rds:DescribeDBLogFiles",
409413
"rds:DescribeDBProxyTargets",
410414
"rds:DescribeDBSnapshots",
@@ -415,6 +419,17 @@
415419
"arn:aws:rds:us-east-1:123456789012:*:*"
416420
]
417421
},
422+
{
423+
"Sid": "RdsListPg",
424+
"Effect": "Allow",
425+
"Action": [
426+
"rds:DescribeDBParameterGroups",
427+
"rds:DescribeDBParameters"
428+
],
429+
"Resource": [
430+
"arn:aws:rds:us-east-1:123456789012:*:*"
431+
]
432+
},
418433
{
419434
"Sid": "RdsListProxy",
420435
"Effect": "Allow",
@@ -478,6 +493,16 @@
478493
"arn:aws:rds:us-east-1:123456789012:*:*"
479494
]
480495
},
496+
{
497+
"Sid": "RdsListGlobalcluster",
498+
"Effect": "Allow",
499+
"Action": [
500+
"rds:DescribeGlobalClusters"
501+
],
502+
"Resource": [
503+
"arn:aws:rds:us-east-1:123456789012:*:*"
504+
]
505+
},
481506
{
482507
"Sid": "RdsListOg",
483508
"Effect": "Allow",

test/util/test_arns.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,51 @@ def test_dynamodb_arn_matching_gh_215(self):
145145
self.assertFalse(does_arn_match(this_arn, stream))
146146
self.assertFalse(does_arn_match(this_arn, backup))
147147
self.assertFalse(does_arn_match(this_arn, global_table))
148+
149+
150+
class ArnPathTestCase(unittest.TestCase):
151+
# When paths are used
152+
def test_ssm_paths(self):
153+
parameter_1 = ARN("arn:aws:ssm:::parameter/dev/foo/bar*")
154+
parameter_2 = "arn:aws:ssm:::parameter/dev"
155+
print(parameter_1.same_resource_type(parameter_2))
156+
self.assertTrue(parameter_1.same_resource_type(parameter_2))
157+
158+
159+
# When confusing ARNs that look like paths but are not actually paths are used
160+
def test_dynamo_db_non_paths(self):
161+
backup_arn = "arn:aws:dynamodb:us-east-1:123456789123:table/mytable/backup/mybackup"
162+
backup_raw_arn = "arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName}/backup/${BackupName}"
163+
164+
table_arn = "arn:aws:dynamodb:us-east-1:123456789123:table/mytable"
165+
table_raw_arn = "arn:${Partition}:dynamodb:${Region}:${Account}:table/${TableName}"
166+
167+
parameter_arn_with_path = "arn:aws:ssm:::parameter/dev/foo/bar*"
168+
parameter_arn_without_path = "arn:aws:ssm:::parameter/dev"
169+
parameter_raw_arn = "arn:${Partition}:ssm:${Region}:${Account}:parameter/${FullyQualifiedParameterName}"
170+
171+
s3_object_with_path = "arn:aws:s3:::foo/bar/baz"
172+
s3_object_without_path = "arn:aws:s3:::foo/bar"
173+
174+
s3_object_raw_arn = "arn:${Partition}:s3:::${BucketName}/${ObjectName}"
175+
s3_bucket_raw_arn = "arn:${Partition}:s3:::${BucketName}"
176+
177+
ecr_raw_arn = "arn:${Partition}:ecr:${Region}:${Account}:repository/${RepositoryName}"
178+
ecr_arn_with_path = "arn:aws:ecr:*:*:repository/foo/bar"
179+
ecr_arn_without_path = "arn:aws:ecr:*:*:repository/foo"
180+
181+
self.assertTrue(does_arn_match(backup_arn, backup_raw_arn))
182+
self.assertTrue(does_arn_match(table_arn, table_raw_arn))
183+
self.assertFalse(does_arn_match(table_arn, backup_raw_arn))
184+
self.assertFalse(does_arn_match(backup_arn, table_raw_arn))
185+
186+
self.assertTrue(does_arn_match(parameter_arn_with_path, parameter_raw_arn))
187+
self.assertTrue(does_arn_match(parameter_arn_without_path, parameter_raw_arn))
188+
189+
self.assertTrue(does_arn_match(s3_object_without_path, s3_object_raw_arn))
190+
self.assertTrue(does_arn_match(s3_object_with_path, s3_object_raw_arn))
191+
self.assertFalse(does_arn_match(s3_object_with_path, s3_bucket_raw_arn))
192+
self.assertFalse(does_arn_match(s3_object_without_path, s3_bucket_raw_arn))
193+
194+
self.assertTrue(does_arn_match(ecr_arn_with_path, ecr_raw_arn))
195+
self.assertTrue(does_arn_match(ecr_arn_without_path, ecr_raw_arn))

test/writing/test_write_policy_library_usage.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
get_crud_template_dict,
88
get_actions_template_dict,
99
)
10+
from policy_sentry.util.arns import ARN
1011

1112
desired_crud_policy = {
1213
"Version": "2012-10-17",
@@ -348,6 +349,15 @@ def test_gh_204_multiple_resource_types_wildcards(self):
348349
expected_results = json.load(yaml_file)
349350
result = write_policy_with_template(crud_template)
350351
# print(json.dumps(result, indent=4))
351-
print(len(result.get("Statement")))
352-
self.assertTrue(len(result.get("Statement")) > 40)
353-
# self.assertDictEqual(result, expected_results)
352+
self.assertDictEqual(result, expected_results)
353+
354+
def test_gh_237_ssm_arns_with_paths(self):
355+
"""test_gh_237_ssm_arns_with_paths: Test GitHub issue #204 with resource ARN paths"""
356+
crud_template = {
357+
"mode": "crud",
358+
'read': ["arn:aws:ssm:::parameter/dev/foo/bar*"]
359+
}
360+
# result = write_policy_with_template(crud_template)
361+
# print(json.dumps(result, indent=4))
362+
arn = ARN("arn:aws:ssm:::parameter/dev/foo/bar*")
363+
self.assertTrue(arn.same_resource_type("arn:aws:ssm:::parameter/dev"))

0 commit comments

Comments
 (0)