iam-policy-validator 1.7.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.

Potentially problematic release.


This version of iam-policy-validator might be problematic. Click here for more details.

Files changed (83) hide show
  1. iam_policy_validator-1.7.0.dist-info/METADATA +1057 -0
  2. iam_policy_validator-1.7.0.dist-info/RECORD +83 -0
  3. iam_policy_validator-1.7.0.dist-info/WHEEL +4 -0
  4. iam_policy_validator-1.7.0.dist-info/entry_points.txt +2 -0
  5. iam_policy_validator-1.7.0.dist-info/licenses/LICENSE +21 -0
  6. iam_validator/__init__.py +27 -0
  7. iam_validator/__main__.py +11 -0
  8. iam_validator/__version__.py +7 -0
  9. iam_validator/checks/__init__.py +43 -0
  10. iam_validator/checks/action_condition_enforcement.py +884 -0
  11. iam_validator/checks/action_resource_matching.py +441 -0
  12. iam_validator/checks/action_validation.py +72 -0
  13. iam_validator/checks/condition_key_validation.py +92 -0
  14. iam_validator/checks/condition_type_mismatch.py +259 -0
  15. iam_validator/checks/full_wildcard.py +71 -0
  16. iam_validator/checks/mfa_condition_check.py +112 -0
  17. iam_validator/checks/policy_size.py +147 -0
  18. iam_validator/checks/policy_type_validation.py +305 -0
  19. iam_validator/checks/principal_validation.py +776 -0
  20. iam_validator/checks/resource_validation.py +138 -0
  21. iam_validator/checks/sensitive_action.py +254 -0
  22. iam_validator/checks/service_wildcard.py +107 -0
  23. iam_validator/checks/set_operator_validation.py +157 -0
  24. iam_validator/checks/sid_uniqueness.py +170 -0
  25. iam_validator/checks/utils/__init__.py +1 -0
  26. iam_validator/checks/utils/policy_level_checks.py +143 -0
  27. iam_validator/checks/utils/sensitive_action_matcher.py +294 -0
  28. iam_validator/checks/utils/wildcard_expansion.py +87 -0
  29. iam_validator/checks/wildcard_action.py +67 -0
  30. iam_validator/checks/wildcard_resource.py +135 -0
  31. iam_validator/commands/__init__.py +25 -0
  32. iam_validator/commands/analyze.py +531 -0
  33. iam_validator/commands/base.py +48 -0
  34. iam_validator/commands/cache.py +392 -0
  35. iam_validator/commands/download_services.py +255 -0
  36. iam_validator/commands/post_to_pr.py +86 -0
  37. iam_validator/commands/validate.py +600 -0
  38. iam_validator/core/__init__.py +14 -0
  39. iam_validator/core/access_analyzer.py +671 -0
  40. iam_validator/core/access_analyzer_report.py +640 -0
  41. iam_validator/core/aws_fetcher.py +940 -0
  42. iam_validator/core/check_registry.py +607 -0
  43. iam_validator/core/cli.py +134 -0
  44. iam_validator/core/condition_validators.py +626 -0
  45. iam_validator/core/config/__init__.py +81 -0
  46. iam_validator/core/config/aws_api.py +35 -0
  47. iam_validator/core/config/aws_global_conditions.py +160 -0
  48. iam_validator/core/config/category_suggestions.py +104 -0
  49. iam_validator/core/config/condition_requirements.py +155 -0
  50. iam_validator/core/config/config_loader.py +472 -0
  51. iam_validator/core/config/defaults.py +523 -0
  52. iam_validator/core/config/principal_requirements.py +421 -0
  53. iam_validator/core/config/sensitive_actions.py +672 -0
  54. iam_validator/core/config/service_principals.py +95 -0
  55. iam_validator/core/config/wildcards.py +124 -0
  56. iam_validator/core/constants.py +74 -0
  57. iam_validator/core/formatters/__init__.py +27 -0
  58. iam_validator/core/formatters/base.py +147 -0
  59. iam_validator/core/formatters/console.py +59 -0
  60. iam_validator/core/formatters/csv.py +170 -0
  61. iam_validator/core/formatters/enhanced.py +440 -0
  62. iam_validator/core/formatters/html.py +672 -0
  63. iam_validator/core/formatters/json.py +33 -0
  64. iam_validator/core/formatters/markdown.py +63 -0
  65. iam_validator/core/formatters/sarif.py +251 -0
  66. iam_validator/core/models.py +327 -0
  67. iam_validator/core/policy_checks.py +656 -0
  68. iam_validator/core/policy_loader.py +396 -0
  69. iam_validator/core/pr_commenter.py +424 -0
  70. iam_validator/core/report.py +872 -0
  71. iam_validator/integrations/__init__.py +28 -0
  72. iam_validator/integrations/github_integration.py +815 -0
  73. iam_validator/integrations/ms_teams.py +442 -0
  74. iam_validator/sdk/__init__.py +187 -0
  75. iam_validator/sdk/arn_matching.py +382 -0
  76. iam_validator/sdk/context.py +222 -0
  77. iam_validator/sdk/exceptions.py +48 -0
  78. iam_validator/sdk/helpers.py +177 -0
  79. iam_validator/sdk/policy_utils.py +425 -0
  80. iam_validator/sdk/shortcuts.py +283 -0
  81. iam_validator/utils/__init__.py +31 -0
  82. iam_validator/utils/cache.py +105 -0
  83. iam_validator/utils/regex.py +206 -0
@@ -0,0 +1,607 @@
1
+ """
2
+ Check Registry for IAM Policy Validator.
3
+
4
+ This module provides a pluggable check system that allows:
5
+ 1. Registering built-in and custom checks
6
+ 2. Enabling/disabling checks via configuration
7
+ 3. Configuring check behavior
8
+ 4. Easy extension without modifying core code
9
+ 5. Parallel execution of checks for performance
10
+ """
11
+
12
+ import asyncio
13
+ from abc import ABC, abstractmethod
14
+ from dataclasses import dataclass, field
15
+ from typing import TYPE_CHECKING, Any
16
+
17
+ from iam_validator.core.aws_fetcher import AWSServiceFetcher
18
+ from iam_validator.core.models import Statement, ValidationIssue
19
+
20
+ if TYPE_CHECKING:
21
+ from iam_validator.core.models import IAMPolicy
22
+
23
+
24
+ @dataclass
25
+ class CheckConfig:
26
+ """Configuration for a single check."""
27
+
28
+ check_id: str
29
+ enabled: bool = True
30
+ severity: str | None = None # Override default severity
31
+ config: dict[str, Any] = field(default_factory=dict) # Check-specific config
32
+ description: str = ""
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
140
+
141
+
142
+ class PolicyCheck(ABC):
143
+ """
144
+ Base class for all policy checks.
145
+
146
+ To create a custom check:
147
+ 1. Inherit from this class
148
+ 2. Implement check_id, description, and execute()
149
+ 3. Register with CheckRegistry
150
+
151
+ Example:
152
+ class MyCustomCheck(PolicyCheck):
153
+ check_id = "my_custom_check"
154
+ description = "Validates custom compliance rules"
155
+
156
+ async def execute(self, statement, statement_idx, fetcher, config):
157
+ issues = []
158
+ # Your validation logic here
159
+ return issues
160
+ """
161
+
162
+ @property
163
+ @abstractmethod
164
+ def check_id(self) -> str:
165
+ """Unique identifier for this check (e.g., 'action_validation')."""
166
+ pass
167
+
168
+ @property
169
+ @abstractmethod
170
+ def description(self) -> str:
171
+ """Human-readable description of what this check does."""
172
+ pass
173
+
174
+ @property
175
+ def default_severity(self) -> str:
176
+ """Default severity level for issues found by this check."""
177
+ return "warning"
178
+
179
+ @abstractmethod
180
+ async def execute(
181
+ self,
182
+ statement: Statement,
183
+ statement_idx: int,
184
+ fetcher: AWSServiceFetcher,
185
+ config: CheckConfig,
186
+ ) -> list[ValidationIssue]:
187
+ """
188
+ Execute the check on a policy statement.
189
+
190
+ Args:
191
+ statement: The IAM policy statement to check
192
+ statement_idx: Index of the statement in the policy
193
+ fetcher: AWS service fetcher for validation against AWS APIs
194
+ config: Configuration for this check instance
195
+
196
+ Returns:
197
+ List of ValidationIssue objects found by this check
198
+ """
199
+ pass
200
+
201
+ async def execute_policy(
202
+ self,
203
+ policy: "IAMPolicy",
204
+ policy_file: str,
205
+ fetcher: AWSServiceFetcher,
206
+ config: CheckConfig,
207
+ **kwargs,
208
+ ) -> list[ValidationIssue]:
209
+ """
210
+ Execute the check on the entire policy (optional method).
211
+
212
+ This method is for checks that need to examine all statements together,
213
+ such as checking for duplicate SIDs or cross-statement relationships.
214
+
215
+ By default, this returns an empty list. Override this method if your
216
+ check needs access to the full policy.
217
+
218
+ Args:
219
+ policy: The complete IAM policy to check
220
+ policy_file: Path to the policy file (for context/reporting)
221
+ fetcher: AWS service fetcher for validation against AWS APIs
222
+ config: Configuration for this check instance
223
+ **kwargs: Additional context (policy_type, etc.)
224
+
225
+ Returns:
226
+ List of ValidationIssue objects found by this check
227
+ """
228
+ del policy, policy_file, fetcher, config # Unused in default implementation
229
+ return []
230
+
231
+ def get_severity(self, config: CheckConfig) -> str:
232
+ """Get the severity level, respecting config overrides."""
233
+ return config.severity or self.default_severity
234
+
235
+ def is_policy_level_check(self) -> bool:
236
+ """
237
+ Check if this is a policy-level check.
238
+
239
+ Returns True if the check overrides execute_policy() method.
240
+ This helps the registry know whether to call execute_policy() or execute().
241
+ """
242
+ # Check if execute_policy has been overridden from the base class
243
+ return type(self).execute_policy is not PolicyCheck.execute_policy
244
+
245
+
246
+ class CheckRegistry:
247
+ """
248
+ Registry for managing validation checks.
249
+
250
+ Supports parallel execution of checks for improved performance.
251
+
252
+ Usage:
253
+ registry = CheckRegistry()
254
+ registry.register(ActionValidationCheck())
255
+ registry.register(MyCustomCheck())
256
+
257
+ # Get all enabled checks
258
+ checks = registry.get_enabled_checks()
259
+
260
+ # Configure checks
261
+ registry.configure_check('action_validation', CheckConfig(
262
+ check_id='action_validation',
263
+ enabled=True,
264
+ severity='error'
265
+ ))
266
+
267
+ # Execute checks in parallel
268
+ issues = await registry.execute_checks_parallel(statement, idx, fetcher)
269
+ """
270
+
271
+ def __init__(self, enable_parallel: bool = True):
272
+ """
273
+ Initialize the registry.
274
+
275
+ Args:
276
+ enable_parallel: If True, execute checks in parallel (default: True)
277
+ """
278
+ self._checks: dict[str, PolicyCheck] = {}
279
+ self._configs: dict[str, CheckConfig] = {}
280
+ self.enable_parallel = enable_parallel
281
+
282
+ def register(self, check: PolicyCheck) -> None:
283
+ """
284
+ Register a new check.
285
+
286
+ Args:
287
+ check: PolicyCheck instance to register
288
+ """
289
+ self._checks[check.check_id] = check
290
+
291
+ # Create default config if not exists
292
+ if check.check_id not in self._configs:
293
+ self._configs[check.check_id] = CheckConfig(
294
+ check_id=check.check_id,
295
+ enabled=True,
296
+ description=check.description,
297
+ )
298
+
299
+ def unregister(self, check_id: str) -> None:
300
+ """
301
+ Unregister a check by ID.
302
+
303
+ Args:
304
+ check_id: ID of the check to unregister
305
+ """
306
+ if check_id in self._checks:
307
+ del self._checks[check_id]
308
+ if check_id in self._configs:
309
+ del self._configs[check_id]
310
+
311
+ def configure_check(self, check_id: str, config: CheckConfig) -> None:
312
+ """
313
+ Configure a registered check.
314
+
315
+ Args:
316
+ check_id: ID of the check to configure
317
+ config: Configuration to apply
318
+ """
319
+ if check_id not in self._checks:
320
+ raise ValueError(f"Check '{check_id}' is not registered")
321
+ self._configs[check_id] = config
322
+
323
+ def get_all_checks(self) -> list[PolicyCheck]:
324
+ """Get all registered checks (enabled and disabled)."""
325
+ return list(self._checks.values())
326
+
327
+ def get_enabled_checks(self) -> list[PolicyCheck]:
328
+ """Get only enabled checks."""
329
+ return [
330
+ check
331
+ for check_id, check in self._checks.items()
332
+ if self._configs.get(check_id, CheckConfig(check_id=check_id)).enabled
333
+ ]
334
+
335
+ def get_check(self, check_id: str) -> PolicyCheck | None:
336
+ """Get a specific check by ID."""
337
+ return self._checks.get(check_id)
338
+
339
+ def get_config(self, check_id: str) -> CheckConfig | None:
340
+ """Get configuration for a specific check."""
341
+ return self._configs.get(check_id)
342
+
343
+ def is_enabled(self, check_id: str) -> bool:
344
+ """Check if a specific check is enabled."""
345
+ config = self._configs.get(check_id)
346
+ return config.enabled if config else False
347
+
348
+ def list_checks(self) -> list[dict[str, Any]]:
349
+ """
350
+ List all checks with their status and description.
351
+
352
+ Returns:
353
+ List of dicts with check information
354
+ """
355
+ result = []
356
+ for check_id, check in self._checks.items():
357
+ config = self._configs.get(check_id, CheckConfig(check_id=check_id))
358
+ result.append(
359
+ {
360
+ "check_id": check_id,
361
+ "description": check.description,
362
+ "enabled": config.enabled,
363
+ "severity": config.severity or check.default_severity,
364
+ }
365
+ )
366
+ return result
367
+
368
+ async def execute_checks_parallel(
369
+ self,
370
+ statement: Statement,
371
+ statement_idx: int,
372
+ fetcher: AWSServiceFetcher,
373
+ filepath: str = "",
374
+ ) -> list[ValidationIssue]:
375
+ """
376
+ Execute all enabled checks in parallel for maximum performance.
377
+
378
+ This method runs all enabled checks concurrently using asyncio.gather(),
379
+ which can significantly speed up validation when multiple checks are enabled.
380
+
381
+ Args:
382
+ statement: The IAM policy statement to validate
383
+ statement_idx: Index of the statement in the policy
384
+ fetcher: AWS service fetcher for API calls
385
+ filepath: Path to the policy file (for ignore_patterns filtering)
386
+
387
+ Returns:
388
+ List of all ValidationIssue objects from all checks (filtered by ignore_patterns)
389
+ """
390
+ enabled_checks = self.get_enabled_checks()
391
+
392
+ if not enabled_checks:
393
+ return []
394
+
395
+ if not self.enable_parallel or len(enabled_checks) == 1:
396
+ # Run sequentially if parallel disabled or only one check
397
+ all_issues = []
398
+ for check in enabled_checks:
399
+ config = self.get_config(check.check_id)
400
+ if config:
401
+ issues = await check.execute(statement, statement_idx, fetcher, config)
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)
407
+ return all_issues
408
+
409
+ # Execute all checks in parallel
410
+ tasks = []
411
+ configs = []
412
+ for check in enabled_checks:
413
+ config = self.get_config(check.check_id)
414
+ if config:
415
+ task = check.execute(statement, statement_idx, fetcher, config)
416
+ tasks.append(task)
417
+ configs.append(config)
418
+
419
+ # Wait for all checks to complete
420
+ results = await asyncio.gather(*tasks, return_exceptions=True)
421
+
422
+ # Collect all issues, handling any exceptions and applying ignore_patterns
423
+ all_issues = []
424
+ for idx, result in enumerate(results):
425
+ if isinstance(result, Exception):
426
+ # Log error but continue with other checks
427
+ check = enabled_checks[idx]
428
+ print(f"Warning: Check '{check.check_id}' failed: {result}")
429
+ elif isinstance(result, list):
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)
436
+
437
+ return all_issues
438
+
439
+ async def execute_checks_sequential(
440
+ self,
441
+ statement: Statement,
442
+ statement_idx: int,
443
+ fetcher: AWSServiceFetcher,
444
+ ) -> list[ValidationIssue]:
445
+ """
446
+ Execute all enabled checks sequentially.
447
+
448
+ Useful for debugging or when parallel execution causes issues.
449
+
450
+ Args:
451
+ statement: The IAM policy statement to validate
452
+ statement_idx: Index of the statement in the policy
453
+ fetcher: AWS service fetcher for API calls
454
+
455
+ Returns:
456
+ List of all ValidationIssue objects from all checks
457
+ """
458
+ all_issues = []
459
+ enabled_checks = self.get_enabled_checks()
460
+
461
+ for check in enabled_checks:
462
+ config = self.get_config(check.check_id)
463
+ if config:
464
+ try:
465
+ issues = await check.execute(statement, statement_idx, fetcher, config)
466
+ all_issues.extend(issues)
467
+ except Exception as e:
468
+ print(f"Warning: Check '{check.check_id}' failed: {e}")
469
+
470
+ return all_issues
471
+
472
+ async def execute_policy_checks(
473
+ self,
474
+ policy: "IAMPolicy",
475
+ policy_file: str,
476
+ fetcher: AWSServiceFetcher,
477
+ policy_type: str = "IDENTITY_POLICY",
478
+ ) -> list[ValidationIssue]:
479
+ """
480
+ Execute all enabled policy-level checks.
481
+
482
+ Policy-level checks examine the entire policy at once, which is useful for
483
+ checks that need to see relationships between statements (e.g., duplicate SIDs).
484
+
485
+ Args:
486
+ policy: The complete IAM policy to validate
487
+ policy_file: Path to the policy file (for context/reporting)
488
+ fetcher: AWS service fetcher for API calls
489
+ policy_type: Type of policy (IDENTITY_POLICY, RESOURCE_POLICY, SERVICE_CONTROL_POLICY)
490
+
491
+ Returns:
492
+ List of all ValidationIssue objects from all policy-level checks
493
+ """
494
+ all_issues = []
495
+ enabled_checks = self.get_enabled_checks()
496
+
497
+ # Filter to only policy-level checks
498
+ policy_level_checks = [c for c in enabled_checks if c.is_policy_level_check()]
499
+
500
+ if not policy_level_checks:
501
+ return []
502
+
503
+ if not self.enable_parallel or len(policy_level_checks) == 1:
504
+ # Run sequentially if parallel disabled or only one check
505
+ for check in policy_level_checks:
506
+ config = self.get_config(check.check_id)
507
+ if config:
508
+ try:
509
+ issues = await check.execute_policy(
510
+ policy, policy_file, fetcher, config, policy_type=policy_type
511
+ )
512
+ all_issues.extend(issues)
513
+ except Exception as e:
514
+ print(f"Warning: Check '{check.check_id}' failed: {e}")
515
+ return all_issues
516
+
517
+ # Execute all policy-level checks in parallel
518
+ tasks = []
519
+ for check in policy_level_checks:
520
+ config = self.get_config(check.check_id)
521
+ if config:
522
+ task = check.execute_policy(
523
+ policy, policy_file, fetcher, config, policy_type=policy_type
524
+ )
525
+ tasks.append(task)
526
+
527
+ # Wait for all checks to complete
528
+ results = await asyncio.gather(*tasks, return_exceptions=True)
529
+
530
+ # Collect all issues, handling any exceptions
531
+ for idx, result in enumerate(results):
532
+ if isinstance(result, Exception):
533
+ # Log error but continue with other checks
534
+ check = policy_level_checks[idx]
535
+ print(f"Warning: Check '{check.check_id}' failed: {result}")
536
+ elif isinstance(result, list):
537
+ all_issues.extend(result)
538
+
539
+ return all_issues
540
+
541
+
542
+ def create_default_registry(
543
+ enable_parallel: bool = True, include_builtin_checks: bool = True
544
+ ) -> CheckRegistry:
545
+ """
546
+ Create a registry with all built-in checks registered.
547
+
548
+ This is a factory function that will be called when no custom
549
+ registry is provided.
550
+
551
+ Args:
552
+ enable_parallel: If True, checks will execute in parallel (default: True)
553
+ include_builtin_checks: If True, register built-in checks (default: True)
554
+
555
+ Returns:
556
+ CheckRegistry with all built-in checks registered (if include_builtin_checks=True)
557
+ """
558
+ registry = CheckRegistry(enable_parallel=enable_parallel)
559
+
560
+ if include_builtin_checks:
561
+ # Import and register built-in checks
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
606
+
607
+ return registry