iam-policy-validator 1.4.0__py3-none-any.whl → 1.6.0__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.4.0.dist-info → iam_policy_validator-1.6.0.dist-info}/METADATA +106 -78
- iam_policy_validator-1.6.0.dist-info/RECORD +82 -0
- iam_validator/__version__.py +1 -1
- iam_validator/checks/__init__.py +20 -4
- iam_validator/checks/action_condition_enforcement.py +165 -8
- iam_validator/checks/action_resource_matching.py +424 -0
- iam_validator/checks/condition_key_validation.py +24 -2
- iam_validator/checks/condition_type_mismatch.py +259 -0
- iam_validator/checks/full_wildcard.py +67 -0
- iam_validator/checks/mfa_condition_check.py +112 -0
- iam_validator/checks/principal_validation.py +497 -3
- iam_validator/checks/sensitive_action.py +250 -0
- iam_validator/checks/service_wildcard.py +105 -0
- iam_validator/checks/set_operator_validation.py +157 -0
- iam_validator/checks/utils/sensitive_action_matcher.py +74 -32
- iam_validator/checks/wildcard_action.py +62 -0
- iam_validator/checks/wildcard_resource.py +131 -0
- iam_validator/commands/cache.py +1 -1
- iam_validator/commands/download_services.py +3 -8
- iam_validator/commands/validate.py +72 -13
- iam_validator/core/aws_fetcher.py +114 -64
- iam_validator/core/check_registry.py +167 -29
- iam_validator/core/condition_validators.py +626 -0
- iam_validator/core/config/__init__.py +81 -0
- iam_validator/core/config/aws_api.py +35 -0
- iam_validator/core/config/aws_global_conditions.py +160 -0
- iam_validator/core/config/category_suggestions.py +104 -0
- iam_validator/core/config/condition_requirements.py +155 -0
- iam_validator/core/{config_loader.py → config/config_loader.py} +32 -9
- iam_validator/core/config/defaults.py +523 -0
- iam_validator/core/config/principal_requirements.py +421 -0
- iam_validator/core/config/sensitive_actions.py +672 -0
- iam_validator/core/config/service_principals.py +95 -0
- iam_validator/core/config/wildcards.py +124 -0
- iam_validator/core/formatters/enhanced.py +11 -5
- iam_validator/core/formatters/sarif.py +78 -14
- iam_validator/core/models.py +14 -1
- iam_validator/core/policy_checks.py +4 -4
- iam_validator/core/pr_commenter.py +1 -1
- iam_validator/sdk/__init__.py +187 -0
- iam_validator/sdk/arn_matching.py +274 -0
- iam_validator/sdk/context.py +222 -0
- iam_validator/sdk/exceptions.py +48 -0
- iam_validator/sdk/helpers.py +177 -0
- iam_validator/sdk/policy_utils.py +425 -0
- iam_validator/sdk/shortcuts.py +283 -0
- iam_validator/utils/__init__.py +31 -0
- iam_validator/utils/cache.py +105 -0
- iam_validator/utils/regex.py +206 -0
- iam_policy_validator-1.4.0.dist-info/RECORD +0 -56
- iam_validator/checks/action_resource_constraint.py +0 -151
- iam_validator/checks/security_best_practices.py +0 -536
- iam_validator/core/aws_global_conditions.py +0 -137
- iam_validator/core/defaults.py +0 -393
- {iam_policy_validator-1.4.0.dist-info → iam_policy_validator-1.6.0.dist-info}/WHEEL +0 -0
- {iam_policy_validator-1.4.0.dist-info → iam_policy_validator-1.6.0.dist-info}/entry_points.txt +0 -0
- {iam_policy_validator-1.4.0.dist-info → iam_policy_validator-1.6.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -31,6 +31,112 @@ class CheckConfig:
|
|
|
31
31
|
config: dict[str, Any] = field(default_factory=dict) # Check-specific config
|
|
32
32
|
description: str = ""
|
|
33
33
|
root_config: dict[str, Any] = field(default_factory=dict) # Full config for cross-check access
|
|
34
|
+
ignore_patterns: list[dict[str, Any]] = field(default_factory=list) # NEW: Ignore patterns
|
|
35
|
+
"""
|
|
36
|
+
List of patterns to ignore findings.
|
|
37
|
+
|
|
38
|
+
Each pattern is a dict with optional fields:
|
|
39
|
+
- filepath_regex: Regex to match file path
|
|
40
|
+
- action_matches: Regex to match action name
|
|
41
|
+
- resource_matches: Regex to match resource
|
|
42
|
+
- statement_sid: Exact SID to match (or regex if ends with .*)
|
|
43
|
+
- condition_key_matches: Regex to match condition key
|
|
44
|
+
|
|
45
|
+
Multiple fields in one pattern = AND logic
|
|
46
|
+
Multiple patterns = OR logic (any pattern matches → ignore)
|
|
47
|
+
|
|
48
|
+
Example:
|
|
49
|
+
ignore_patterns:
|
|
50
|
+
- filepath_regex: "test/.*|examples/.*"
|
|
51
|
+
- filepath_regex: "policies/readonly-.*"
|
|
52
|
+
action_matches: ".*:(Get|List|Describe).*"
|
|
53
|
+
- statement_sid: "AllowReadOnlyAccess"
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def should_ignore(self, issue: ValidationIssue, filepath: str = "") -> bool:
|
|
57
|
+
"""
|
|
58
|
+
Check if issue should be ignored based on ignore patterns.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
issue: The validation issue to check
|
|
62
|
+
filepath: Path to the policy file
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
True if the issue should be ignored
|
|
66
|
+
"""
|
|
67
|
+
if not self.ignore_patterns:
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
import re
|
|
71
|
+
|
|
72
|
+
for pattern in self.ignore_patterns:
|
|
73
|
+
if self._matches_pattern(pattern, issue, filepath, re):
|
|
74
|
+
return True
|
|
75
|
+
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
def _matches_pattern(
|
|
79
|
+
self,
|
|
80
|
+
pattern: dict[str, Any],
|
|
81
|
+
issue: ValidationIssue,
|
|
82
|
+
filepath: str,
|
|
83
|
+
re_module: Any,
|
|
84
|
+
) -> bool:
|
|
85
|
+
"""
|
|
86
|
+
Check if issue matches a single ignore pattern.
|
|
87
|
+
|
|
88
|
+
All fields in pattern must match (AND logic).
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
pattern: Pattern dict with optional fields
|
|
92
|
+
issue: ValidationIssue to check
|
|
93
|
+
filepath: Path to policy file
|
|
94
|
+
re_module: re module for regex matching
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
True if all fields in pattern match the issue
|
|
98
|
+
"""
|
|
99
|
+
for field_name, regex_pattern in pattern.items():
|
|
100
|
+
actual_value = None
|
|
101
|
+
|
|
102
|
+
if field_name == "filepath_regex":
|
|
103
|
+
actual_value = filepath
|
|
104
|
+
elif field_name == "action_matches":
|
|
105
|
+
actual_value = issue.action or ""
|
|
106
|
+
elif field_name == "resource_matches":
|
|
107
|
+
actual_value = issue.resource or ""
|
|
108
|
+
elif field_name == "statement_sid":
|
|
109
|
+
# For SID, support both exact match and regex
|
|
110
|
+
if isinstance(regex_pattern, str) and "*" in regex_pattern:
|
|
111
|
+
# Treat as regex if contains wildcard
|
|
112
|
+
actual_value = issue.statement_sid or ""
|
|
113
|
+
else:
|
|
114
|
+
# Exact match
|
|
115
|
+
if issue.statement_sid != regex_pattern:
|
|
116
|
+
return False
|
|
117
|
+
continue
|
|
118
|
+
elif field_name == "condition_key_matches":
|
|
119
|
+
actual_value = issue.condition_key or ""
|
|
120
|
+
else:
|
|
121
|
+
# Unknown field, skip
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
# Check regex match (case-insensitive)
|
|
125
|
+
if actual_value is None:
|
|
126
|
+
return False
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
if not re_module.search(
|
|
130
|
+
str(regex_pattern),
|
|
131
|
+
str(actual_value),
|
|
132
|
+
re_module.IGNORECASE,
|
|
133
|
+
):
|
|
134
|
+
return False
|
|
135
|
+
except re_module.error:
|
|
136
|
+
# Invalid regex, don't match
|
|
137
|
+
return False
|
|
138
|
+
|
|
139
|
+
return True # All fields matched
|
|
34
140
|
|
|
35
141
|
|
|
36
142
|
class PolicyCheck(ABC):
|
|
@@ -264,6 +370,7 @@ class CheckRegistry:
|
|
|
264
370
|
statement: Statement,
|
|
265
371
|
statement_idx: int,
|
|
266
372
|
fetcher: AWSServiceFetcher,
|
|
373
|
+
filepath: str = "",
|
|
267
374
|
) -> list[ValidationIssue]:
|
|
268
375
|
"""
|
|
269
376
|
Execute all enabled checks in parallel for maximum performance.
|
|
@@ -275,9 +382,10 @@ class CheckRegistry:
|
|
|
275
382
|
statement: The IAM policy statement to validate
|
|
276
383
|
statement_idx: Index of the statement in the policy
|
|
277
384
|
fetcher: AWS service fetcher for API calls
|
|
385
|
+
filepath: Path to the policy file (for ignore_patterns filtering)
|
|
278
386
|
|
|
279
387
|
Returns:
|
|
280
|
-
List of all ValidationIssue objects from all checks
|
|
388
|
+
List of all ValidationIssue objects from all checks (filtered by ignore_patterns)
|
|
281
389
|
"""
|
|
282
390
|
enabled_checks = self.get_enabled_checks()
|
|
283
391
|
|
|
@@ -291,21 +399,27 @@ class CheckRegistry:
|
|
|
291
399
|
config = self.get_config(check.check_id)
|
|
292
400
|
if config:
|
|
293
401
|
issues = await check.execute(statement, statement_idx, fetcher, config)
|
|
294
|
-
|
|
402
|
+
# Filter issues based on ignore_patterns
|
|
403
|
+
filtered_issues = [
|
|
404
|
+
issue for issue in issues if not config.should_ignore(issue, filepath)
|
|
405
|
+
]
|
|
406
|
+
all_issues.extend(filtered_issues)
|
|
295
407
|
return all_issues
|
|
296
408
|
|
|
297
409
|
# Execute all checks in parallel
|
|
298
410
|
tasks = []
|
|
411
|
+
configs = []
|
|
299
412
|
for check in enabled_checks:
|
|
300
413
|
config = self.get_config(check.check_id)
|
|
301
414
|
if config:
|
|
302
415
|
task = check.execute(statement, statement_idx, fetcher, config)
|
|
303
416
|
tasks.append(task)
|
|
417
|
+
configs.append(config)
|
|
304
418
|
|
|
305
419
|
# Wait for all checks to complete
|
|
306
420
|
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
307
421
|
|
|
308
|
-
# Collect all issues, handling any exceptions
|
|
422
|
+
# Collect all issues, handling any exceptions and applying ignore_patterns
|
|
309
423
|
all_issues = []
|
|
310
424
|
for idx, result in enumerate(results):
|
|
311
425
|
if isinstance(result, Exception):
|
|
@@ -313,7 +427,12 @@ class CheckRegistry:
|
|
|
313
427
|
check = enabled_checks[idx]
|
|
314
428
|
print(f"Warning: Check '{check.check_id}' failed: {result}")
|
|
315
429
|
elif isinstance(result, list):
|
|
316
|
-
|
|
430
|
+
config = configs[idx]
|
|
431
|
+
# Filter issues based on ignore_patterns
|
|
432
|
+
filtered_issues = [
|
|
433
|
+
issue for issue in result if not config.should_ignore(issue, filepath)
|
|
434
|
+
]
|
|
435
|
+
all_issues.extend(filtered_issues)
|
|
317
436
|
|
|
318
437
|
return all_issues
|
|
319
438
|
|
|
@@ -440,30 +559,49 @@ def create_default_registry(
|
|
|
440
559
|
|
|
441
560
|
if include_builtin_checks:
|
|
442
561
|
# Import and register built-in checks
|
|
443
|
-
from iam_validator
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
)
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
registry.register(
|
|
458
|
-
registry.register(
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
registry.register(
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
#
|
|
466
|
-
#
|
|
467
|
-
#
|
|
562
|
+
from iam_validator import checks
|
|
563
|
+
|
|
564
|
+
# 1. POLICY STRUCTURE (Checks that examine the entire policy, not individual statements)
|
|
565
|
+
registry.register(
|
|
566
|
+
checks.SidUniquenessCheck()
|
|
567
|
+
) # Policy-level: Duplicate SID detection across statements
|
|
568
|
+
registry.register(checks.PolicySizeCheck()) # Policy-level: Size limit validation
|
|
569
|
+
|
|
570
|
+
# 2. IAM VALIDITY (AWS syntax validation - must pass before deeper checks)
|
|
571
|
+
registry.register(checks.ActionValidationCheck()) # Validate actions against AWS API
|
|
572
|
+
registry.register(checks.ResourceValidationCheck()) # Validate resource ARNs
|
|
573
|
+
registry.register(checks.ConditionKeyValidationCheck()) # Validate condition keys
|
|
574
|
+
|
|
575
|
+
# 3. TYPE VALIDATION (Condition operator type checking)
|
|
576
|
+
registry.register(checks.ConditionTypeMismatchCheck()) # Operator-value type compatibility
|
|
577
|
+
registry.register(checks.SetOperatorValidationCheck()) # ForAllValues/ForAnyValue usage
|
|
578
|
+
|
|
579
|
+
# 4. RESOURCE MATCHING (Action-resource relationship validation)
|
|
580
|
+
registry.register(
|
|
581
|
+
checks.ActionResourceMatchingCheck()
|
|
582
|
+
) # ARN type matching and resource constraints
|
|
583
|
+
|
|
584
|
+
# 5. SECURITY - WILDCARDS (Security best practices for wildcards)
|
|
585
|
+
registry.register(checks.WildcardActionCheck()) # Wildcard action detection
|
|
586
|
+
registry.register(checks.WildcardResourceCheck()) # Wildcard resource detection
|
|
587
|
+
registry.register(checks.FullWildcardCheck()) # Full wildcard (*) detection
|
|
588
|
+
registry.register(checks.ServiceWildcardCheck()) # Service-level wildcard detection
|
|
589
|
+
|
|
590
|
+
# 6. SECURITY - ADVANCED (Sensitive actions and condition enforcement)
|
|
591
|
+
registry.register(
|
|
592
|
+
checks.SensitiveActionCheck()
|
|
593
|
+
) # Policy-level: Privilege escalation detection (all_of across statements)
|
|
594
|
+
registry.register(
|
|
595
|
+
checks.ActionConditionEnforcementCheck()
|
|
596
|
+
) # Statement + Policy-level: Condition enforcement (any_of/all_of/none_of)
|
|
597
|
+
registry.register(checks.MFAConditionCheck()) # MFA anti-pattern detection
|
|
598
|
+
|
|
599
|
+
# 7. PRINCIPAL VALIDATION (Resource policy specific)
|
|
600
|
+
registry.register(
|
|
601
|
+
checks.PrincipalValidationCheck()
|
|
602
|
+
) # Principal validation (resource policies)
|
|
603
|
+
|
|
604
|
+
# Note: policy_type_validation is a standalone function (not a class-based check)
|
|
605
|
+
# and is called separately in the validation flow
|
|
468
606
|
|
|
469
607
|
return registry
|