iam-policy-validator 1.10.0__py3-none-any.whl → 1.10.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {iam_policy_validator-1.10.0.dist-info → iam_policy_validator-1.10.1.dist-info}/METADATA +1 -1
- {iam_policy_validator-1.10.0.dist-info → iam_policy_validator-1.10.1.dist-info}/RECORD +9 -9
- iam_validator/__version__.py +1 -1
- iam_validator/core/aws_service/validators.py +5 -2
- iam_validator/core/models.py +6 -6
- iam_validator/sdk/policy_utils.py +34 -8
- {iam_policy_validator-1.10.0.dist-info → iam_policy_validator-1.10.1.dist-info}/WHEEL +0 -0
- {iam_policy_validator-1.10.0.dist-info → iam_policy_validator-1.10.1.dist-info}/entry_points.txt +0 -0
- {iam_policy_validator-1.10.0.dist-info → iam_policy_validator-1.10.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: iam-policy-validator
|
|
3
|
-
Version: 1.10.
|
|
3
|
+
Version: 1.10.1
|
|
4
4
|
Summary: Validate AWS IAM policies for correctness and security using AWS Service Reference API
|
|
5
5
|
Project-URL: Homepage, https://github.com/boogy/iam-policy-validator
|
|
6
6
|
Project-URL: Documentation, https://github.com/boogy/iam-policy-validator/tree/main/docs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
iam_validator/__init__.py,sha256=xHdUASOxFHwEXfT_GSr_KrkLlnxZ-pAAr1wW1PwAGko,693
|
|
2
2
|
iam_validator/__main__.py,sha256=to_nz3n_IerJpVVZZ6WSFlFR5s_06J0csfPOTfQZG8g,197
|
|
3
|
-
iam_validator/__version__.py,sha256=
|
|
3
|
+
iam_validator/__version__.py,sha256=SRRdMee5oIovcL25-xNSjSxoHM9bzGQhwmx_U4YhnXQ,362
|
|
4
4
|
iam_validator/checks/__init__.py,sha256=OTkPnmlelu4YjMO8krjhu2wXiTV72RzopA5u1SfPQA0,1990
|
|
5
5
|
iam_validator/checks/action_condition_enforcement.py,sha256=0dCH_xX-Xc0uLxtNeRjrpNjWYbdWQRzO1XNcLTSn6sI,51698
|
|
6
6
|
iam_validator/checks/action_resource_matching.py,sha256=WiGJmCIJfx5yituMjZxpKmk-99N6nK20ueN02ddy9oM,19296
|
|
@@ -42,7 +42,7 @@ iam_validator/core/condition_validators.py,sha256=7zBjlcf2xGFKGbcFrXSLvWT5tFhWxo
|
|
|
42
42
|
iam_validator/core/constants.py,sha256=cVBPgbXr4ALltH_NTSKsgBi6wmndLnOyUWhyBx0ZwrM,6113
|
|
43
43
|
iam_validator/core/ignore_patterns.py,sha256=pZqDJBtkbck-85QK5eFPM5ZOPEKs3McRh3avqiCT5z0,10398
|
|
44
44
|
iam_validator/core/label_manager.py,sha256=48CRASWg98wyjfVF_1pUzj6dm9itzmG7SeIWf0TSUfc,7502
|
|
45
|
-
iam_validator/core/models.py,sha256=
|
|
45
|
+
iam_validator/core/models.py,sha256=yQ5iBTffdAzx88h8RyVCCmBg6kkD2zg5_lb-qLdjy3w,13386
|
|
46
46
|
iam_validator/core/policy_checks.py,sha256=FNVuS2GTffwCjjrlupVIazC172gSxKYAAT_ObV6Apbo,8803
|
|
47
47
|
iam_validator/core/policy_loader.py,sha256=2KJnXzGg3g9pDXWZHk3DO0xpZnZZ-wXWFEOdQ_naJ8s,17862
|
|
48
48
|
iam_validator/core/pr_commenter.py,sha256=NTKoSmjvspYX2rbl3Xn8d611XkTNSfYlGUY0zBHBP4g,16801
|
|
@@ -54,7 +54,7 @@ iam_validator/core/aws_service/fetcher.py,sha256=X4iI6fiLj4l9f3W6_J0E58lSP26UsBh
|
|
|
54
54
|
iam_validator/core/aws_service/parsers.py,sha256=gJzR7HCD8ItCWCCbguTQIZpPEdj2rdMwC7LPhu7ve14,5174
|
|
55
55
|
iam_validator/core/aws_service/patterns.py,sha256=gGc55Tn-EJ3cmcWtmYAZROUajKYz7DaMchYWGEhHpC0,1726
|
|
56
56
|
iam_validator/core/aws_service/storage.py,sha256=PrfKdvF60IL7E_8xYs_XwFoAJPRcVYw57FVLHCoqwVk,10429
|
|
57
|
-
iam_validator/core/aws_service/validators.py,sha256=
|
|
57
|
+
iam_validator/core/aws_service/validators.py,sha256=AY0BjydskXoesEzUShH4gZKp6gtSX7s1rCLP_iOZQMc,16493
|
|
58
58
|
iam_validator/core/config/__init__.py,sha256=CWSyIA7kEyzrskEenjYbs9Iih10BXRpiY9H2dHg61rU,2671
|
|
59
59
|
iam_validator/core/config/aws_api.py,sha256=HLIzOItQ0A37wxHcgWck6ZFO0wmNY8JNTiWMMK6JKYU,1248
|
|
60
60
|
iam_validator/core/config/aws_global_conditions.py,sha256=gdmMxXGBy95B3uYUG-J7rnM6Ixgc6L7Y9Pcd2XAMb60,7170
|
|
@@ -83,14 +83,14 @@ iam_validator/sdk/arn_matching.py,sha256=HSDpLltOYISq-SoPebAlM89mKOaUaghq_04urch
|
|
|
83
83
|
iam_validator/sdk/context.py,sha256=FvAEyUa_s7tHWoSdgjSkzHf1CLlYpAEmLZANxs2IJ4A,6826
|
|
84
84
|
iam_validator/sdk/exceptions.py,sha256=tm91TxIwU157U_UHN7w5qICf_OhU11agj6pV5W_YP-4,1023
|
|
85
85
|
iam_validator/sdk/helpers.py,sha256=sjfK0na_Fo7O8GhEVhl44rVHqOdw6nAKkBL4FVL-QdU,5697
|
|
86
|
-
iam_validator/sdk/policy_utils.py,sha256=
|
|
86
|
+
iam_validator/sdk/policy_utils.py,sha256=Fh-QElhmPypzSJuF9rcrY7y46Gz3hQu3-yN5b1_mSHY,13579
|
|
87
87
|
iam_validator/sdk/shortcuts.py,sha256=EVNSYV7rv4TFH03ulsZ3mS1UVmTSp2jKpc2AXs4j1q4,8531
|
|
88
88
|
iam_validator/utils/__init__.py,sha256=NveA2F3G1E6-ANZzFr7J6Q6u5mogvMp862iFokmYuCs,1021
|
|
89
89
|
iam_validator/utils/cache.py,sha256=wOQKOBeoG6QqC5f0oXcHz63Cjtu_-SsSS-0pTSwyAiM,3254
|
|
90
90
|
iam_validator/utils/regex.py,sha256=xHoMECttb7qaMhts-c9b0GIxdhHNZTt-UBr7wNhWfzg,6219
|
|
91
91
|
iam_validator/utils/terminal.py,sha256=FsRaRMH_JAyDgXWBCOgOEhbS89cs17HCmKYoughq5io,724
|
|
92
|
-
iam_policy_validator-1.10.
|
|
93
|
-
iam_policy_validator-1.10.
|
|
94
|
-
iam_policy_validator-1.10.
|
|
95
|
-
iam_policy_validator-1.10.
|
|
96
|
-
iam_policy_validator-1.10.
|
|
92
|
+
iam_policy_validator-1.10.1.dist-info/METADATA,sha256=lIMmE1Y6TX34sh3stfe9J2_q5ATb9fYZqVqOupYNcL8,19070
|
|
93
|
+
iam_policy_validator-1.10.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
94
|
+
iam_policy_validator-1.10.1.dist-info/entry_points.txt,sha256=8HtWd8O7mvPiPdZR5YbzY8or_qcqLM4-pKaFdhtFT8M,62
|
|
95
|
+
iam_policy_validator-1.10.1.dist-info/licenses/LICENSE,sha256=AMnbFTBDcK4_MITe2wiQBkj0vg-jjBBhsc43ydC7tt4,1098
|
|
96
|
+
iam_policy_validator-1.10.1.dist-info/RECORD,,
|
iam_validator/__version__.py
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
This file is the single source of truth for the package version.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
__version__ = "1.10.
|
|
6
|
+
__version__ = "1.10.1"
|
|
7
7
|
# Parse version, handling pre-release suffixes like -rc, -alpha, -beta
|
|
8
8
|
_version_base = __version__.split("-")[0] # Remove pre-release suffix if present
|
|
9
9
|
__version_info__ = tuple(int(part) for part in _version_base.split("."))
|
|
@@ -280,9 +280,12 @@ class ServiceValidator:
|
|
|
280
280
|
"- `aws:RequestedRegion`\n"
|
|
281
281
|
"- `aws:SourceIp`\n"
|
|
282
282
|
"- `aws:SourceVpce`\n"
|
|
283
|
-
"- `aws:
|
|
283
|
+
"- `aws:ResourceOrgID`\n"
|
|
284
|
+
"- `aws:PrincipalOrgID`\n"
|
|
285
|
+
"- `aws:SourceAccount`\n"
|
|
286
|
+
"- `aws:PrincipalAccount`\n"
|
|
284
287
|
"- `aws:CurrentTime`\n"
|
|
285
|
-
"- `aws:
|
|
288
|
+
"- `aws:ResourceAccount`\n"
|
|
286
289
|
"- `aws:PrincipalArn`\n"
|
|
287
290
|
"- And many others"
|
|
288
291
|
)
|
iam_validator/core/models.py
CHANGED
|
@@ -31,7 +31,7 @@ class ServiceInfo(BaseModel):
|
|
|
31
31
|
class ActionDetail(BaseModel):
|
|
32
32
|
"""Details about an AWS IAM action."""
|
|
33
33
|
|
|
34
|
-
model_config = ConfigDict(
|
|
34
|
+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
|
|
35
35
|
|
|
36
36
|
name: str = Field(alias="Name")
|
|
37
37
|
action_condition_keys: list[str] | None = Field(
|
|
@@ -45,7 +45,7 @@ class ActionDetail(BaseModel):
|
|
|
45
45
|
class ResourceType(BaseModel):
|
|
46
46
|
"""Details about an AWS resource type."""
|
|
47
47
|
|
|
48
|
-
model_config = ConfigDict(
|
|
48
|
+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
|
|
49
49
|
|
|
50
50
|
name: str = Field(alias="Name")
|
|
51
51
|
arn_formats: list[str] | None = Field(default=None, alias="ARNFormats")
|
|
@@ -68,7 +68,7 @@ class ResourceType(BaseModel):
|
|
|
68
68
|
class ConditionKey(BaseModel):
|
|
69
69
|
"""Details about an AWS condition key."""
|
|
70
70
|
|
|
71
|
-
model_config = ConfigDict(
|
|
71
|
+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
|
|
72
72
|
|
|
73
73
|
name: str = Field(alias="Name")
|
|
74
74
|
description: str | None = Field(default=None, alias="Description")
|
|
@@ -78,7 +78,7 @@ class ConditionKey(BaseModel):
|
|
|
78
78
|
class ServiceDetail(BaseModel):
|
|
79
79
|
"""Detailed information about an AWS service."""
|
|
80
80
|
|
|
81
|
-
model_config = ConfigDict(
|
|
81
|
+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
|
|
82
82
|
|
|
83
83
|
name: str = Field(alias="Name")
|
|
84
84
|
prefix: str | None = None # Not always present in API response
|
|
@@ -106,7 +106,7 @@ class ServiceDetail(BaseModel):
|
|
|
106
106
|
class Statement(BaseModel):
|
|
107
107
|
"""IAM policy statement."""
|
|
108
108
|
|
|
109
|
-
model_config = ConfigDict(
|
|
109
|
+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True, extra="allow")
|
|
110
110
|
|
|
111
111
|
sid: str | None = Field(default=None, alias="Sid")
|
|
112
112
|
effect: str | None = Field(default=None, alias="Effect")
|
|
@@ -136,7 +136,7 @@ class Statement(BaseModel):
|
|
|
136
136
|
class IAMPolicy(BaseModel):
|
|
137
137
|
"""IAM policy document."""
|
|
138
138
|
|
|
139
|
-
model_config = ConfigDict(
|
|
139
|
+
model_config = ConfigDict(validate_by_name=True, validate_by_alias=True, extra="allow")
|
|
140
140
|
|
|
141
141
|
version: str | None = Field(default=None, alias="Version")
|
|
142
142
|
statement: list[Statement] | None = Field(default=None, alias="Statement")
|
|
@@ -63,9 +63,13 @@ def normalize_policy(policy: IAMPolicy) -> IAMPolicy:
|
|
|
63
63
|
"""
|
|
64
64
|
# Pydantic model already handles this via Field(alias="Statement")
|
|
65
65
|
# which expects a list, but we can ensure it's always a list
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
)
|
|
66
|
+
if policy.statement is None:
|
|
67
|
+
statements: list[Statement] = []
|
|
68
|
+
elif isinstance(policy.statement, list):
|
|
69
|
+
statements = policy.statement
|
|
70
|
+
else:
|
|
71
|
+
# Single statement - wrap in list
|
|
72
|
+
statements = [policy.statement]
|
|
69
73
|
|
|
70
74
|
# Normalize actions and resources in each statement
|
|
71
75
|
normalized_statements: list[Statement] = []
|
|
@@ -118,6 +122,9 @@ def extract_actions(policy: IAMPolicy) -> list[str]:
|
|
|
118
122
|
"""
|
|
119
123
|
actions = set()
|
|
120
124
|
|
|
125
|
+
if policy.statement is None:
|
|
126
|
+
return []
|
|
127
|
+
|
|
121
128
|
for stmt in policy.statement:
|
|
122
129
|
# Handle Action field
|
|
123
130
|
if stmt.action:
|
|
@@ -150,6 +157,9 @@ def extract_resources(policy: IAMPolicy) -> list[str]:
|
|
|
150
157
|
"""
|
|
151
158
|
resources = set()
|
|
152
159
|
|
|
160
|
+
if policy.statement is None:
|
|
161
|
+
return []
|
|
162
|
+
|
|
153
163
|
for stmt in policy.statement:
|
|
154
164
|
# Handle Resource field
|
|
155
165
|
if stmt.resource:
|
|
@@ -181,7 +191,10 @@ def extract_condition_keys(policy: IAMPolicy) -> list[str]:
|
|
|
181
191
|
>>> keys = extract_condition_keys(policy)
|
|
182
192
|
>>> print(f"Policy uses condition keys: {', '.join(keys)}")
|
|
183
193
|
"""
|
|
184
|
-
condition_keys = set()
|
|
194
|
+
condition_keys: set[str] = set()
|
|
195
|
+
|
|
196
|
+
if policy.statement is None:
|
|
197
|
+
return []
|
|
185
198
|
|
|
186
199
|
for stmt in policy.statement:
|
|
187
200
|
if stmt.condition:
|
|
@@ -216,6 +229,9 @@ def find_statements_with_action(policy: IAMPolicy, action: str) -> list[Statemen
|
|
|
216
229
|
|
|
217
230
|
matching_statements = []
|
|
218
231
|
|
|
232
|
+
if policy.statement is None:
|
|
233
|
+
return []
|
|
234
|
+
|
|
219
235
|
for stmt in policy.statement:
|
|
220
236
|
stmt_actions = stmt.get_actions()
|
|
221
237
|
|
|
@@ -250,6 +266,9 @@ def find_statements_with_resource(policy: IAMPolicy, resource: str) -> list[Stat
|
|
|
250
266
|
|
|
251
267
|
matching_statements = []
|
|
252
268
|
|
|
269
|
+
if policy.statement is None:
|
|
270
|
+
return []
|
|
271
|
+
|
|
253
272
|
for stmt in policy.statement:
|
|
254
273
|
stmt_resources = stmt.get_resources()
|
|
255
274
|
|
|
@@ -286,7 +305,8 @@ def merge_policies(*policies: IAMPolicy) -> IAMPolicy:
|
|
|
286
305
|
|
|
287
306
|
all_statements: list[Statement] = []
|
|
288
307
|
for policy in policies:
|
|
289
|
-
|
|
308
|
+
if policy.statement is not None:
|
|
309
|
+
all_statements.extend(policy.statement)
|
|
290
310
|
|
|
291
311
|
# Use capitalized field names (aliases) for Pydantic model construction
|
|
292
312
|
return IAMPolicy(
|
|
@@ -318,8 +338,9 @@ def get_policy_summary(policy: IAMPolicy) -> dict[str, Any]:
|
|
|
318
338
|
condition_keys = extract_condition_keys(policy)
|
|
319
339
|
|
|
320
340
|
# Count allow vs deny statements
|
|
321
|
-
|
|
322
|
-
|
|
341
|
+
statements = policy.statement or []
|
|
342
|
+
allow_count = sum(1 for s in statements if s.effect and s.effect.lower() == "allow")
|
|
343
|
+
deny_count = sum(1 for s in statements if s.effect and s.effect.lower() == "deny")
|
|
323
344
|
|
|
324
345
|
# Check for wildcards
|
|
325
346
|
has_wildcard_actions = any("*" in action for action in actions)
|
|
@@ -327,7 +348,7 @@ def get_policy_summary(policy: IAMPolicy) -> dict[str, Any]:
|
|
|
327
348
|
|
|
328
349
|
return {
|
|
329
350
|
"version": policy.version,
|
|
330
|
-
"statement_count": len(
|
|
351
|
+
"statement_count": len(statements),
|
|
331
352
|
"allow_statements": allow_count,
|
|
332
353
|
"deny_statements": deny_count,
|
|
333
354
|
"action_count": len(actions),
|
|
@@ -396,6 +417,8 @@ def is_resource_policy(policy: IAMPolicy) -> bool:
|
|
|
396
417
|
>>> if is_resource_policy(policy):
|
|
397
418
|
... print("This is an S3 bucket policy or similar")
|
|
398
419
|
"""
|
|
420
|
+
if policy.statement is None:
|
|
421
|
+
return False
|
|
399
422
|
return any(stmt.principal is not None for stmt in policy.statement)
|
|
400
423
|
|
|
401
424
|
|
|
@@ -414,6 +437,9 @@ def has_public_access(policy: IAMPolicy) -> bool:
|
|
|
414
437
|
>>> if has_public_access(policy):
|
|
415
438
|
... print("WARNING: This policy allows public access!")
|
|
416
439
|
"""
|
|
440
|
+
if policy.statement is None:
|
|
441
|
+
return False
|
|
442
|
+
|
|
417
443
|
for stmt in policy.statement:
|
|
418
444
|
if stmt.principal == "*":
|
|
419
445
|
return True
|
|
File without changes
|
{iam_policy_validator-1.10.0.dist-info → iam_policy_validator-1.10.1.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{iam_policy_validator-1.10.0.dist-info → iam_policy_validator-1.10.1.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|