Skip to content

Commit ac14a13

Browse files
authored
Merge pull request #26 from ReversecLabs/case-insensitive-who-can
ensures case insensitivity when dealing with resource policies for who-can
2 parents 81ee9d4 + f4769be commit ac14a13

File tree

5 files changed

+92
-3
lines changed

5 files changed

+92
-3
lines changed

iamspy/model.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,15 @@ def _generate_query_conditions(
169169

170170
return output
171171

172+
def get_correct_case_principal(self, principal: str) -> str:
173+
identities = [x.lstrip("identity_") for x in self.model_vars if x.startswith("identity_arn")]
174+
175+
for identity in identities:
176+
if identity.lower() == principal.lower():
177+
return identity
178+
179+
raise Exception("Identity not found to match principal")
180+
172181
def can_i(
173182
self,
174183
source: str,
@@ -243,7 +252,7 @@ def who_can(
243252
sources.append(str(source)[1:-1])
244253
solver.add(s != source)
245254
sat = solver.check() == z3.sat
246-
yield str(source)[1:-1]
255+
yield self.get_correct_case_principal(str(source)[1:-1])
247256

248257
def who_can_batch_resource(
249258
self,

iamspy/parse.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ def generate_evaluation_logic_checks(model_vars, source: Optional[str], resource
490490
== z3.And(
491491
z3.Bool(x),
492492
z3.Bool(f"deny_{x}"),
493-
s == x.lstrip("identity_"),
493+
s == x.lstrip("identity_").lower(),
494494
z3.String("s_account") == z3.StringVal(x.split(":")[4]),
495495
)
496496
for x in identities
@@ -501,7 +501,7 @@ def generate_evaluation_logic_checks(model_vars, source: Optional[str], resource
501501
# TODO: This is a temporary fix for whocan, at some point need to expand this to do automatic wildcard resolution
502502
# for accounts external to known
503503
constraints.append(
504-
z3.Or(*[parse_string(s, x.lstrip("identity_"), wildcard=False, case_sensitive=True) for x in identities])
504+
z3.Or(*[parse_string(s, x.lstrip("identity_"), wildcard=False, case_sensitive=False) for x in identities])
505505
)
506506

507507
constraints.append(z3.Bool("identity") == identity_check)

tests/files/gaad-uppercase.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"RoleDetailList": [],
3+
"GroupDetailList": [],
4+
"UserDetailList": [
5+
{
6+
"Path": "/",
7+
"UserName": "userA",
8+
"UserId": "AIDAVRUVVWHX5JXO27GWS",
9+
"Arn": "arn:aws:iam::111111111111:user/userA",
10+
"CreateDate": "2025-09-02T09:40:39+00:00",
11+
"GroupList": [],
12+
"UserPolicyList": [
13+
{
14+
"PolicyName": "AllowGetObject",
15+
"PolicyDocument": {
16+
"Version": "2012-10-17",
17+
"Statement": [
18+
{
19+
"Sid": "AllowGetObject",
20+
"Effect": "Allow",
21+
"Action": "s3:GetObject",
22+
"Resource": "arn:aws:s3:::bucket/*"
23+
}
24+
]
25+
}
26+
}
27+
],
28+
"AttachedManagedPolicies": []
29+
},
30+
{
31+
"Path": "/",
32+
"UserName": "userB",
33+
"UserId": "AIDAVRUVVWHX4K7W3CHXO",
34+
"Arn": "arn:aws:iam::111111111111:user/userB",
35+
"CreateDate": "2025-09-01T14:28:12+00:00",
36+
"GroupList": [],
37+
"UserPolicyList": [],
38+
"AttachedManagedPolicies": []
39+
},
40+
{
41+
"Path": "/",
42+
"UserName": "userC",
43+
"UserId": "AIDAVRUVVWHXWLBII3IIU",
44+
"Arn": "arn:aws:iam::111111111111:user/userC",
45+
"CreateDate": "2025-09-02T10:03:39+00:00",
46+
"GroupList": [],
47+
"UserPolicyList": [],
48+
"AttachedManagedPolicies": []
49+
}
50+
],
51+
"Policies": []
52+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[
2+
{
3+
"Resource": "arn:aws:s3:::bucket",
4+
"Policy": {
5+
"Version": "2012-10-17",
6+
"Statement": [
7+
{
8+
"Sid": "AllowGetObject",
9+
"Effect": "Allow",
10+
"Principal": {
11+
"AWS": "arn:aws:iam::111111111111:user/userB"
12+
},
13+
"Action": "s3:GetObject",
14+
"Resource": "arn:aws:s3:::bucket/*"
15+
}
16+
]
17+
},
18+
"Account": "111111111111"
19+
}
20+
]

tests/test_integration.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,14 @@ def test_can_i(files, inp, out):
368368
),
369369
set(["arn:aws:iam::111111111111:role/testing", "arn:aws:iam::111111111111:role/testing2"]),
370370
),
371+
(
372+
{"gaads": ["gaad-uppercase.json"], "resources": ["resource-uppercase.json"]},
373+
(
374+
"s3:GetObject",
375+
"arn:aws:s3:::bucket/key",
376+
),
377+
set(["arn:aws:iam::111111111111:user/userA", "arn:aws:iam::111111111111:user/userB"]),
378+
),
371379
],
372380
)
373381
def test_who_can(files, inp, out):

0 commit comments

Comments
 (0)