iam-policy-validator 1.14.7__py3-none-any.whl → 1.15.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.14.7.dist-info → iam_policy_validator-1.15.1.dist-info}/METADATA +16 -11
- {iam_policy_validator-1.14.7.dist-info → iam_policy_validator-1.15.1.dist-info}/RECORD +41 -28
- iam_policy_validator-1.15.1.dist-info/entry_points.txt +4 -0
- iam_validator/__version__.py +1 -1
- iam_validator/checks/__init__.py +2 -0
- iam_validator/checks/action_validation.py +91 -27
- iam_validator/checks/not_action_not_resource.py +163 -0
- iam_validator/checks/resource_validation.py +132 -81
- iam_validator/checks/wildcard_resource.py +136 -6
- iam_validator/commands/__init__.py +3 -0
- iam_validator/commands/cache.py +66 -24
- iam_validator/commands/completion.py +94 -15
- iam_validator/commands/mcp.py +210 -0
- iam_validator/commands/query.py +489 -65
- iam_validator/core/aws_service/__init__.py +5 -1
- iam_validator/core/aws_service/cache.py +20 -0
- iam_validator/core/aws_service/fetcher.py +180 -11
- iam_validator/core/aws_service/storage.py +14 -6
- iam_validator/core/aws_service/validators.py +68 -51
- iam_validator/core/check_registry.py +100 -35
- iam_validator/core/config/aws_global_conditions.py +18 -9
- iam_validator/core/config/check_documentation.py +104 -51
- iam_validator/core/config/config_loader.py +39 -3
- iam_validator/core/config/defaults.py +6 -0
- iam_validator/core/constants.py +11 -4
- iam_validator/core/models.py +39 -14
- iam_validator/mcp/__init__.py +162 -0
- iam_validator/mcp/models.py +118 -0
- iam_validator/mcp/server.py +2928 -0
- iam_validator/mcp/session_config.py +319 -0
- iam_validator/mcp/templates/__init__.py +79 -0
- iam_validator/mcp/templates/builtin.py +856 -0
- iam_validator/mcp/tools/__init__.py +72 -0
- iam_validator/mcp/tools/generation.py +888 -0
- iam_validator/mcp/tools/org_config_tools.py +263 -0
- iam_validator/mcp/tools/query.py +395 -0
- iam_validator/mcp/tools/validation.py +376 -0
- iam_validator/sdk/__init__.py +2 -0
- iam_validator/sdk/policy_utils.py +31 -5
- iam_policy_validator-1.14.7.dist-info/entry_points.txt +0 -2
- {iam_policy_validator-1.14.7.dist-info → iam_policy_validator-1.15.1.dist-info}/WHEEL +0 -0
- {iam_policy_validator-1.14.7.dist-info → iam_policy_validator-1.15.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
"""Validation tools for MCP server.
|
|
2
|
+
|
|
3
|
+
This module provides MCP tools for validating IAM policies using the existing
|
|
4
|
+
SDK validation functionality. All functions wrap the core validation logic
|
|
5
|
+
from iam_validator.sdk without reimplementing it.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import atexit
|
|
9
|
+
import json
|
|
10
|
+
import tempfile
|
|
11
|
+
from collections.abc import Generator
|
|
12
|
+
from contextlib import contextmanager
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Any
|
|
15
|
+
|
|
16
|
+
import yaml
|
|
17
|
+
|
|
18
|
+
from iam_validator.core.models import IAMPolicy
|
|
19
|
+
from iam_validator.core.policy_checks import validate_policies
|
|
20
|
+
from iam_validator.mcp.models import ValidationResult
|
|
21
|
+
|
|
22
|
+
# Track temp files for cleanup on exit (safety net for abnormal termination)
|
|
23
|
+
_temp_files_to_cleanup: set[Path] = set()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _cleanup_temp_files() -> None:
|
|
27
|
+
"""Clean up any remaining temp files on process exit."""
|
|
28
|
+
for temp_path in list(_temp_files_to_cleanup):
|
|
29
|
+
try:
|
|
30
|
+
if temp_path.exists():
|
|
31
|
+
temp_path.unlink()
|
|
32
|
+
except OSError:
|
|
33
|
+
pass
|
|
34
|
+
_temp_files_to_cleanup.clear()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
atexit.register(_cleanup_temp_files)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@contextmanager
|
|
41
|
+
def _temp_config_file(
|
|
42
|
+
session_config: Any,
|
|
43
|
+
) -> Generator[str | None, None, None]:
|
|
44
|
+
"""Context manager for temporary config file with guaranteed cleanup.
|
|
45
|
+
|
|
46
|
+
Creates a temporary YAML config file from ValidatorConfig and ensures
|
|
47
|
+
cleanup even if exceptions occur or process is killed.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
session_config: ValidatorConfig instance from SessionConfigManager
|
|
51
|
+
|
|
52
|
+
Yields:
|
|
53
|
+
Path to temporary config file, or None if no config provided
|
|
54
|
+
"""
|
|
55
|
+
if session_config is None:
|
|
56
|
+
yield None
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
# ValidatorConfig already has the right structure - just dump its config_dict
|
|
60
|
+
config_dict = session_config.config_dict
|
|
61
|
+
|
|
62
|
+
# Create temp file and register for cleanup
|
|
63
|
+
temp_path: Path | None = None
|
|
64
|
+
try:
|
|
65
|
+
with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
|
|
66
|
+
yaml.dump(config_dict, f)
|
|
67
|
+
temp_path = Path(f.name)
|
|
68
|
+
_temp_files_to_cleanup.add(temp_path)
|
|
69
|
+
|
|
70
|
+
yield str(temp_path)
|
|
71
|
+
finally:
|
|
72
|
+
# Clean up temp file
|
|
73
|
+
if temp_path:
|
|
74
|
+
try:
|
|
75
|
+
if temp_path.exists():
|
|
76
|
+
temp_path.unlink()
|
|
77
|
+
except OSError:
|
|
78
|
+
pass
|
|
79
|
+
_temp_files_to_cleanup.discard(temp_path)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# Trust policy actions (case-insensitive prefixes for matching)
|
|
83
|
+
_TRUST_POLICY_ACTIONS = frozenset(
|
|
84
|
+
[
|
|
85
|
+
"sts:assumerole",
|
|
86
|
+
"sts:assumerolewithwebidentity",
|
|
87
|
+
"sts:assumerolewithsaml",
|
|
88
|
+
]
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _is_trust_action(action: str) -> bool:
|
|
93
|
+
"""Check if an action indicates a trust policy (case-insensitive)."""
|
|
94
|
+
action_lower = action.lower()
|
|
95
|
+
# Check exact match or if it's a wildcard that would include assume role
|
|
96
|
+
return action_lower in _TRUST_POLICY_ACTIONS or action_lower in ("sts:*", "*")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _detect_policy_type(policy: dict[str, Any]) -> str:
|
|
100
|
+
"""Auto-detect policy type from structure.
|
|
101
|
+
|
|
102
|
+
Analyzes ALL statements in the policy to determine the appropriate policy type
|
|
103
|
+
based on AWS IAM policy patterns. Uses case-insensitive matching for actions.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
policy: IAM policy dictionary to analyze
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
- "trust" if ANY statement contains sts:AssumeRole* actions with Principal
|
|
110
|
+
- "resource" if ANY statement contains Principal/NotPrincipal without trust actions
|
|
111
|
+
- "identity" otherwise (default, identity-based policy)
|
|
112
|
+
"""
|
|
113
|
+
statements = policy.get("Statement", [])
|
|
114
|
+
if isinstance(statements, dict):
|
|
115
|
+
statements = [statements]
|
|
116
|
+
|
|
117
|
+
has_principal = False
|
|
118
|
+
has_trust_action = False
|
|
119
|
+
|
|
120
|
+
for stmt in statements:
|
|
121
|
+
# Check for Principal in any statement
|
|
122
|
+
if "Principal" in stmt or "NotPrincipal" in stmt:
|
|
123
|
+
has_principal = True
|
|
124
|
+
|
|
125
|
+
# Check for trust policy actions (case-insensitive)
|
|
126
|
+
actions = stmt.get("Action", [])
|
|
127
|
+
if isinstance(actions, str):
|
|
128
|
+
actions = [actions]
|
|
129
|
+
|
|
130
|
+
for action in actions:
|
|
131
|
+
if _is_trust_action(action):
|
|
132
|
+
has_trust_action = True
|
|
133
|
+
break
|
|
134
|
+
|
|
135
|
+
# Determine type based on all statements
|
|
136
|
+
if has_principal:
|
|
137
|
+
if has_trust_action:
|
|
138
|
+
return "trust"
|
|
139
|
+
return "resource"
|
|
140
|
+
|
|
141
|
+
return "identity"
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
async def validate_policy(
|
|
145
|
+
policy: dict[str, Any],
|
|
146
|
+
policy_type: str | None = None,
|
|
147
|
+
config_path: str | None = None,
|
|
148
|
+
use_org_config: bool = True,
|
|
149
|
+
) -> ValidationResult:
|
|
150
|
+
"""Validate an IAM policy dictionary.
|
|
151
|
+
|
|
152
|
+
This tool validates a policy object against AWS IAM rules and security best
|
|
153
|
+
practices. It runs all enabled checks and returns detailed validation results.
|
|
154
|
+
|
|
155
|
+
Policy Type Auto-Detection:
|
|
156
|
+
If policy_type is None (default), the policy type is automatically detected:
|
|
157
|
+
- "trust" if contains sts:AssumeRole action (trust/assume role policy)
|
|
158
|
+
- "resource" if contains Principal/NotPrincipal (resource-based policy)
|
|
159
|
+
- "identity" otherwise (identity-based policy attached to users/roles/groups)
|
|
160
|
+
|
|
161
|
+
Configuration priority:
|
|
162
|
+
1. config_path (if provided) - explicit YAML config file path
|
|
163
|
+
2. Session org config (if use_org_config=True and config set)
|
|
164
|
+
3. Default validator configuration
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
policy: IAM policy as a Python dictionary (must contain Version and Statement)
|
|
168
|
+
policy_type: Type of policy to validate. If None (default), auto-detects from structure.
|
|
169
|
+
Explicit options:
|
|
170
|
+
- "identity": Identity-based policy (attached to users/roles/groups)
|
|
171
|
+
- "resource": Resource-based policy (attached to resources like S3 buckets)
|
|
172
|
+
- "trust": Trust policy (role assumption policy)
|
|
173
|
+
config_path: Optional path to YAML configuration file
|
|
174
|
+
use_org_config: Whether to use session organization config (default: True)
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
ValidationResult with:
|
|
178
|
+
- is_valid: True if no errors/warnings found
|
|
179
|
+
- issues: List of ValidationIssue objects with details
|
|
180
|
+
- policy_file: Set to "inline-policy" for dict validation
|
|
181
|
+
- policy_type_detected: The policy type used (auto-detected or provided)
|
|
182
|
+
|
|
183
|
+
Example:
|
|
184
|
+
>>> policy = {
|
|
185
|
+
... "Version": "2012-10-17",
|
|
186
|
+
... "Statement": [{
|
|
187
|
+
... "Effect": "Allow",
|
|
188
|
+
... "Action": "s3:GetObject",
|
|
189
|
+
... "Resource": "arn:aws:s3:::my-bucket/*"
|
|
190
|
+
... }]
|
|
191
|
+
... }
|
|
192
|
+
>>> result = await validate_policy(policy)
|
|
193
|
+
>>> print(f"Valid: {result.is_valid}, Issues: {len(result.issues)}")
|
|
194
|
+
"""
|
|
195
|
+
# Auto-detect policy type if not provided
|
|
196
|
+
effective_policy_type = policy_type
|
|
197
|
+
if effective_policy_type is None:
|
|
198
|
+
effective_policy_type = _detect_policy_type(policy)
|
|
199
|
+
|
|
200
|
+
# Map user-friendly policy type names to internal constants
|
|
201
|
+
policy_type_mapping = {
|
|
202
|
+
"identity": "IDENTITY_POLICY",
|
|
203
|
+
"resource": "RESOURCE_POLICY",
|
|
204
|
+
"trust": "TRUST_POLICY",
|
|
205
|
+
"scp": "SERVICE_CONTROL_POLICY",
|
|
206
|
+
"rcp": "RESOURCE_CONTROL_POLICY",
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
# Normalize the policy type
|
|
210
|
+
normalized_type = policy_type_mapping.get(effective_policy_type.lower(), "IDENTITY_POLICY")
|
|
211
|
+
|
|
212
|
+
# Parse the dict into an IAMPolicy model
|
|
213
|
+
iam_policy = IAMPolicy(**policy)
|
|
214
|
+
|
|
215
|
+
# Determine config path and session_config to use
|
|
216
|
+
session_config = None
|
|
217
|
+
if not config_path and use_org_config:
|
|
218
|
+
# Try to use session config
|
|
219
|
+
from iam_validator.mcp.session_config import SessionConfigManager
|
|
220
|
+
|
|
221
|
+
session_config = SessionConfigManager.get_config()
|
|
222
|
+
|
|
223
|
+
# Use context manager for temp file to ensure cleanup
|
|
224
|
+
with _temp_config_file(session_config) as temp_path:
|
|
225
|
+
effective_config_path = config_path or temp_path
|
|
226
|
+
|
|
227
|
+
# Use validate_policies to perform validation with policy_type support
|
|
228
|
+
# This handles all the validation logic including check execution
|
|
229
|
+
results = await validate_policies(
|
|
230
|
+
policies=[("inline-policy", iam_policy)],
|
|
231
|
+
config_path=effective_config_path,
|
|
232
|
+
policy_type=normalized_type, # type: ignore
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Get the first (and only) result
|
|
236
|
+
sdk_result = results[0] if results else None
|
|
237
|
+
if not sdk_result:
|
|
238
|
+
# Fallback if no results returned (shouldn't happen)
|
|
239
|
+
from iam_validator.core.models import PolicyValidationResult
|
|
240
|
+
|
|
241
|
+
sdk_result = PolicyValidationResult(
|
|
242
|
+
policy_file="inline-policy",
|
|
243
|
+
is_valid=False,
|
|
244
|
+
issues=[],
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
# Convert SDK result to MCP ValidationResult
|
|
248
|
+
return ValidationResult(
|
|
249
|
+
is_valid=sdk_result.is_valid,
|
|
250
|
+
issues=sdk_result.issues,
|
|
251
|
+
policy_file=sdk_result.policy_file,
|
|
252
|
+
policy_type_detected=effective_policy_type,
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
async def validate_policy_json(
|
|
257
|
+
policy_json: str, policy_type: str | None = None
|
|
258
|
+
) -> ValidationResult:
|
|
259
|
+
"""Validate an IAM policy from a JSON string.
|
|
260
|
+
|
|
261
|
+
This tool parses a JSON string into a policy object and validates it.
|
|
262
|
+
Useful when working with policy text from files, API responses, or user input.
|
|
263
|
+
|
|
264
|
+
Policy Type Auto-Detection:
|
|
265
|
+
If policy_type is None (default), the policy type is automatically detected
|
|
266
|
+
from the policy structure (see validate_policy for details).
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
policy_json: IAM policy as a JSON string
|
|
270
|
+
policy_type: Type of policy to validate. If None (default), auto-detects.
|
|
271
|
+
Options: "identity", "resource", "trust"
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
ValidationResult with validation status and issues
|
|
275
|
+
|
|
276
|
+
Raises:
|
|
277
|
+
Returns ValidationResult with parsing error if JSON is invalid
|
|
278
|
+
|
|
279
|
+
Example:
|
|
280
|
+
>>> policy_json = '''
|
|
281
|
+
... {
|
|
282
|
+
... "Version": "2012-10-17",
|
|
283
|
+
... "Statement": [{
|
|
284
|
+
... "Effect": "Allow",
|
|
285
|
+
... "Action": "*",
|
|
286
|
+
... "Resource": "*"
|
|
287
|
+
... }]
|
|
288
|
+
... }
|
|
289
|
+
... '''
|
|
290
|
+
>>> result = await validate_policy_json(policy_json)
|
|
291
|
+
>>> for issue in result.issues:
|
|
292
|
+
... print(f"{issue.severity}: {issue.message}")
|
|
293
|
+
"""
|
|
294
|
+
try:
|
|
295
|
+
# Parse JSON string to dict
|
|
296
|
+
policy_dict = json.loads(policy_json)
|
|
297
|
+
except json.JSONDecodeError as e:
|
|
298
|
+
# Return validation result with parsing error
|
|
299
|
+
from iam_validator.core.models import ValidationIssue
|
|
300
|
+
|
|
301
|
+
return ValidationResult(
|
|
302
|
+
is_valid=False,
|
|
303
|
+
issues=[
|
|
304
|
+
ValidationIssue(
|
|
305
|
+
severity="error",
|
|
306
|
+
statement_index=-1,
|
|
307
|
+
issue_type="json_parse_error",
|
|
308
|
+
message=f"Failed to parse policy JSON: {e}",
|
|
309
|
+
suggestion="Ensure the policy is valid JSON format",
|
|
310
|
+
check_id="policy_structure",
|
|
311
|
+
)
|
|
312
|
+
],
|
|
313
|
+
policy_file="inline-policy",
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Validate the parsed policy dict
|
|
317
|
+
return await validate_policy(policy=policy_dict, policy_type=policy_type)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
async def quick_validate(policy: dict[str, Any]) -> dict[str, Any]:
|
|
321
|
+
"""Quick pass/fail validation check for a policy.
|
|
322
|
+
|
|
323
|
+
This is a lightweight validation that returns just the essential information:
|
|
324
|
+
whether the policy is valid, the number of issues found, and critical issues.
|
|
325
|
+
Useful for rapid validation without detailed issue analysis.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
policy: IAM policy as a Python dictionary
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
Dictionary containing:
|
|
332
|
+
- is_valid (bool): Whether the policy passed validation
|
|
333
|
+
- issue_count (int): Total number of issues found
|
|
334
|
+
- critical_issues (list[str]): List of critical/high severity issue messages
|
|
335
|
+
- sensitive_actions_found (int): Count of sensitive actions detected
|
|
336
|
+
- wildcards_detected (bool): Whether wildcards were found in actions/resources
|
|
337
|
+
|
|
338
|
+
Example:
|
|
339
|
+
>>> policy = {"Version": "2012-10-17", "Statement": [...]}
|
|
340
|
+
>>> result = await quick_validate(policy)
|
|
341
|
+
>>> if result["is_valid"]:
|
|
342
|
+
... print("Policy is valid!")
|
|
343
|
+
>>> else:
|
|
344
|
+
... print(f"Found {result['issue_count']} issues")
|
|
345
|
+
... for msg in result["critical_issues"]:
|
|
346
|
+
... print(f" - {msg}")
|
|
347
|
+
"""
|
|
348
|
+
# Use validate_policy to get full results
|
|
349
|
+
validation_result = await validate_policy(policy=policy)
|
|
350
|
+
|
|
351
|
+
# Filter critical and high severity issues
|
|
352
|
+
critical_issues = []
|
|
353
|
+
sensitive_actions_count = 0
|
|
354
|
+
wildcards_detected = False
|
|
355
|
+
|
|
356
|
+
for issue in validation_result.issues:
|
|
357
|
+
severity = issue.severity.lower()
|
|
358
|
+
if severity in {"critical", "high", "error"}:
|
|
359
|
+
critical_issues.append(issue.message)
|
|
360
|
+
|
|
361
|
+
# Count sensitive action issues
|
|
362
|
+
if issue.check_id == "sensitive_action":
|
|
363
|
+
sensitive_actions_count += 1
|
|
364
|
+
|
|
365
|
+
# Detect wildcard issues
|
|
366
|
+
if issue.check_id in {"wildcard_action", "wildcard_resource", "service_wildcard"}:
|
|
367
|
+
wildcards_detected = True
|
|
368
|
+
|
|
369
|
+
# Return simplified result with enhanced fields
|
|
370
|
+
return {
|
|
371
|
+
"is_valid": validation_result.is_valid,
|
|
372
|
+
"issue_count": len(validation_result.issues),
|
|
373
|
+
"critical_issues": critical_issues,
|
|
374
|
+
"sensitive_actions_found": sensitive_actions_count,
|
|
375
|
+
"wildcards_detected": wildcards_detected,
|
|
376
|
+
}
|
iam_validator/sdk/__init__.py
CHANGED
|
@@ -99,6 +99,7 @@ from iam_validator.sdk.helpers import CheckHelper, expand_actions
|
|
|
99
99
|
from iam_validator.sdk.policy_utils import (
|
|
100
100
|
extract_actions,
|
|
101
101
|
extract_condition_keys,
|
|
102
|
+
extract_condition_keys_from_statement,
|
|
102
103
|
extract_resources,
|
|
103
104
|
find_statements_with_action,
|
|
104
105
|
find_statements_with_resource,
|
|
@@ -153,6 +154,7 @@ __all__ = [
|
|
|
153
154
|
"extract_actions",
|
|
154
155
|
"extract_resources",
|
|
155
156
|
"extract_condition_keys",
|
|
157
|
+
"extract_condition_keys_from_statement",
|
|
156
158
|
"find_statements_with_action",
|
|
157
159
|
"find_statements_with_resource",
|
|
158
160
|
"merge_policies",
|
|
@@ -176,6 +176,36 @@ def extract_resources(policy: IAMPolicy) -> list[str]:
|
|
|
176
176
|
return sorted(resources)
|
|
177
177
|
|
|
178
178
|
|
|
179
|
+
def extract_condition_keys_from_statement(statement: Statement) -> set[str]:
|
|
180
|
+
"""
|
|
181
|
+
Extract all condition keys from a single statement.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
statement: Statement to extract condition keys from
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
Set of condition key names (e.g., {"aws:ResourceAccount", "aws:SourceIp"})
|
|
188
|
+
|
|
189
|
+
Example:
|
|
190
|
+
>>> stmt = Statement(
|
|
191
|
+
... Effect="Allow",
|
|
192
|
+
... Action=["s3:GetObject"],
|
|
193
|
+
... Resource=["*"],
|
|
194
|
+
... Condition={"StringEquals": {"aws:ResourceAccount": "123456789012"}}
|
|
195
|
+
... )
|
|
196
|
+
>>> keys = extract_condition_keys_from_statement(stmt)
|
|
197
|
+
>>> print(keys) # {"aws:ResourceAccount"}
|
|
198
|
+
"""
|
|
199
|
+
if not statement.condition:
|
|
200
|
+
return set()
|
|
201
|
+
|
|
202
|
+
keys: set[str] = set()
|
|
203
|
+
for operator_block in statement.condition.values():
|
|
204
|
+
if isinstance(operator_block, dict):
|
|
205
|
+
keys.update(operator_block.keys())
|
|
206
|
+
return keys
|
|
207
|
+
|
|
208
|
+
|
|
179
209
|
def extract_condition_keys(policy: IAMPolicy) -> list[str]:
|
|
180
210
|
"""
|
|
181
211
|
Extract all condition keys used in a policy.
|
|
@@ -197,11 +227,7 @@ def extract_condition_keys(policy: IAMPolicy) -> list[str]:
|
|
|
197
227
|
return []
|
|
198
228
|
|
|
199
229
|
for stmt in policy.statement:
|
|
200
|
-
|
|
201
|
-
# Condition format: {"StringEquals": {"aws:username": "johndoe"}}
|
|
202
|
-
for _, key_values in stmt.condition.items():
|
|
203
|
-
if isinstance(key_values, dict):
|
|
204
|
-
condition_keys.update(key_values.keys())
|
|
230
|
+
condition_keys.update(extract_condition_keys_from_statement(stmt))
|
|
205
231
|
|
|
206
232
|
return sorted(condition_keys)
|
|
207
233
|
|
|
File without changes
|
{iam_policy_validator-1.14.7.dist-info → iam_policy_validator-1.15.1.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|