iam-policy-validator 1.10.1__py3-none-any.whl → 1.10.2__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.1.dist-info → iam_policy_validator-1.10.2.dist-info}/METADATA +1 -1
- {iam_policy_validator-1.10.1.dist-info → iam_policy_validator-1.10.2.dist-info}/RECORD +9 -9
- iam_validator/__version__.py +2 -2
- iam_validator/core/aws_service/fetcher.py +24 -7
- iam_validator/core/aws_service/validators.py +3 -5
- iam_validator/sdk/policy_utils.py +3 -3
- {iam_policy_validator-1.10.1.dist-info → iam_policy_validator-1.10.2.dist-info}/WHEEL +0 -0
- {iam_policy_validator-1.10.1.dist-info → iam_policy_validator-1.10.2.dist-info}/entry_points.txt +0 -0
- {iam_policy_validator-1.10.1.dist-info → iam_policy_validator-1.10.2.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.2
|
|
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=Fz08JonmD1dXoibF83ZYkSo1e0IWJO9aJ4iIAfq64mI,374
|
|
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
|
|
@@ -50,11 +50,11 @@ iam_validator/core/report.py,sha256=kzSeWnT1LqWZVA5pqKKz-maVowXVj0djdoShfRhhpz4,
|
|
|
50
50
|
iam_validator/core/aws_service/__init__.py,sha256=UqMh4HUdGlx2QF5OoueJJ2UlCnhX4QW_x3KeE_bxRQc,735
|
|
51
51
|
iam_validator/core/aws_service/cache.py,sha256=DPuOOPPJC867KAYgV1e0RyQs_k3mtefMdYli3jPaN64,3589
|
|
52
52
|
iam_validator/core/aws_service/client.py,sha256=Zv7rIpEFdUCDXKGp3migPDkj8L5eZltgrGe64M2t2Ko,7336
|
|
53
|
-
iam_validator/core/aws_service/fetcher.py,sha256=
|
|
53
|
+
iam_validator/core/aws_service/fetcher.py,sha256=s_o8h6ua9gPdiV9-ElNs7YY0HlyoP0Ewtl71hTrhsZA,23340
|
|
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=L9XRJdGmR-vZ1r0bj5SCznULyKEY_G1OAjij7-kOZPM,16463
|
|
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=bGdJ1X1aC72dVXXpAnAwyBpAiiX-qXvblpetY5BsjKU,13658
|
|
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.2.dist-info/METADATA,sha256=t5vzMawKVACC8uFWNJznjMBvw4xX9T7z1EduP8HbAQA,19070
|
|
93
|
+
iam_policy_validator-1.10.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
94
|
+
iam_policy_validator-1.10.2.dist-info/entry_points.txt,sha256=8HtWd8O7mvPiPdZR5YbzY8or_qcqLM4-pKaFdhtFT8M,62
|
|
95
|
+
iam_policy_validator-1.10.2.dist-info/licenses/LICENSE,sha256=AMnbFTBDcK4_MITe2wiQBkj0vg-jjBBhsc43ydC7tt4,1098
|
|
96
|
+
iam_policy_validator-1.10.2.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.2"
|
|
7
7
|
# Parse version, handling pre-release suffixes like -rc, -alpha, -beta
|
|
8
|
-
_version_base = __version__.split("-")[0] # Remove pre-release suffix if present
|
|
8
|
+
_version_base = __version__.split("-", maxsplit=1)[0] # Remove pre-release suffix if present
|
|
9
9
|
__version_info__ = tuple(int(part) for part in _version_base.split("."))
|
|
@@ -233,8 +233,16 @@ class AWSServiceFetcher:
|
|
|
233
233
|
await self._cache.set(services_cache_key, loaded_services)
|
|
234
234
|
return loaded_services
|
|
235
235
|
|
|
236
|
-
# Not in parsed cache,
|
|
237
|
-
data = await self.
|
|
236
|
+
# Not in parsed cache, check disk cache then fetch from API
|
|
237
|
+
data = await self._cache.get(
|
|
238
|
+
f"raw:{self.BASE_URL}", url=self.BASE_URL, base_url=self.BASE_URL
|
|
239
|
+
)
|
|
240
|
+
if data is None:
|
|
241
|
+
data = await self._client.fetch(self.BASE_URL)
|
|
242
|
+
# Cache the raw data
|
|
243
|
+
await self._cache.set(
|
|
244
|
+
f"raw:{self.BASE_URL}", data, url=self.BASE_URL, base_url=self.BASE_URL
|
|
245
|
+
)
|
|
238
246
|
|
|
239
247
|
if not isinstance(data, list):
|
|
240
248
|
raise ValueError("Expected list of services from root endpoint")
|
|
@@ -247,7 +255,7 @@ class AWSServiceFetcher:
|
|
|
247
255
|
if service and url:
|
|
248
256
|
services.append(ServiceInfo(service=str(service), url=str(url)))
|
|
249
257
|
|
|
250
|
-
# Cache the parsed services list (memory only
|
|
258
|
+
# Cache the parsed services list (memory only)
|
|
251
259
|
await self._cache.set(services_cache_key, services)
|
|
252
260
|
|
|
253
261
|
# Log only on first fetch (when parsed cache was empty)
|
|
@@ -312,13 +320,22 @@ class AWSServiceFetcher:
|
|
|
312
320
|
|
|
313
321
|
for service in services:
|
|
314
322
|
if service.service.lower() == service_name_lower:
|
|
315
|
-
#
|
|
316
|
-
data = await self.
|
|
323
|
+
# Check disk cache first, then fetch from API
|
|
324
|
+
data = await self._cache.get(
|
|
325
|
+
f"raw:{service.url}", url=service.url, base_url=self.BASE_URL
|
|
326
|
+
)
|
|
327
|
+
if data is None:
|
|
328
|
+
# Fetch service detail from API
|
|
329
|
+
data = await self._client.fetch(service.url)
|
|
330
|
+
# Cache the raw data
|
|
331
|
+
await self._cache.set(
|
|
332
|
+
f"raw:{service.url}", data, url=service.url, base_url=self.BASE_URL
|
|
333
|
+
)
|
|
317
334
|
|
|
318
335
|
# Validate and parse
|
|
319
336
|
service_detail = ServiceDetail.model_validate(data)
|
|
320
337
|
|
|
321
|
-
# Cache with service name as key (memory only
|
|
338
|
+
# Cache with service name as key (memory only)
|
|
322
339
|
await self._cache.set(cache_key, service_detail)
|
|
323
340
|
|
|
324
341
|
return service_detail
|
|
@@ -550,7 +567,7 @@ class AWSServiceFetcher:
|
|
|
550
567
|
if action_pattern in ("*", "*:*"):
|
|
551
568
|
return ["*"]
|
|
552
569
|
|
|
553
|
-
service_prefix,
|
|
570
|
+
service_prefix, _ = self._parser.parse_action(action_pattern)
|
|
554
571
|
service_detail = await self.fetch_service_by_name(service_prefix)
|
|
555
572
|
available = list(service_detail.actions.keys())
|
|
556
573
|
return self._parser.expand_wildcard_to_actions(action_pattern, available, service_prefix)
|
|
@@ -94,9 +94,7 @@ class ServiceValidator:
|
|
|
94
94
|
if not allow_wildcards:
|
|
95
95
|
return False, "Wildcard actions are not allowed", True
|
|
96
96
|
|
|
97
|
-
has_matches,
|
|
98
|
-
action_name, available_actions
|
|
99
|
-
)
|
|
97
|
+
has_matches, _ = self._parser.match_wildcard_action(action_name, available_actions)
|
|
100
98
|
|
|
101
99
|
if has_matches:
|
|
102
100
|
# Wildcard is valid and matches at least one action
|
|
@@ -161,7 +159,7 @@ class ServiceValidator:
|
|
|
161
159
|
get_global_conditions,
|
|
162
160
|
)
|
|
163
161
|
|
|
164
|
-
|
|
162
|
+
_, action_name = self._parser.parse_action(action)
|
|
165
163
|
|
|
166
164
|
# Check if it's a global condition key
|
|
167
165
|
is_global_key = False
|
|
@@ -323,7 +321,7 @@ class ServiceValidator:
|
|
|
323
321
|
>>> resources = validator.get_resources_for_action("s3:GetObject", service)
|
|
324
322
|
"""
|
|
325
323
|
try:
|
|
326
|
-
_, action_name = self._parser.parse_action(action)
|
|
324
|
+
_, action_name = self._parser.parse_action(action) # pylint: disable=unused-variable
|
|
327
325
|
|
|
328
326
|
# Find the action (case-insensitive)
|
|
329
327
|
action_detail = service_detail.actions.get(action_name)
|
|
@@ -199,7 +199,7 @@ def extract_condition_keys(policy: IAMPolicy) -> list[str]:
|
|
|
199
199
|
for stmt in policy.statement:
|
|
200
200
|
if stmt.condition:
|
|
201
201
|
# Condition format: {"StringEquals": {"aws:username": "johndoe"}}
|
|
202
|
-
for
|
|
202
|
+
for _, key_values in stmt.condition.items():
|
|
203
203
|
if isinstance(key_values, dict):
|
|
204
204
|
condition_keys.update(key_values.keys())
|
|
205
205
|
|
|
@@ -225,7 +225,7 @@ def find_statements_with_action(policy: IAMPolicy, action: str) -> list[Statemen
|
|
|
225
225
|
>>> for stmt in stmts:
|
|
226
226
|
... print(f"Statement {stmt.sid} allows s3:GetObject")
|
|
227
227
|
"""
|
|
228
|
-
import fnmatch
|
|
228
|
+
import fnmatch # pylint: disable=import-outside-toplevel
|
|
229
229
|
|
|
230
230
|
matching_statements = []
|
|
231
231
|
|
|
@@ -262,7 +262,7 @@ def find_statements_with_resource(policy: IAMPolicy, resource: str) -> list[Stat
|
|
|
262
262
|
>>> stmts = find_statements_with_resource(policy, "arn:aws:s3:::my-bucket/*")
|
|
263
263
|
>>> print(f"Found {len(stmts)} statements with this resource")
|
|
264
264
|
"""
|
|
265
|
-
import fnmatch
|
|
265
|
+
import fnmatch # pylint: disable=import-outside-toplevel
|
|
266
266
|
|
|
267
267
|
matching_statements = []
|
|
268
268
|
|
|
File without changes
|
{iam_policy_validator-1.10.1.dist-info → iam_policy_validator-1.10.2.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{iam_policy_validator-1.10.1.dist-info → iam_policy_validator-1.10.2.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|