iam-policy-validator 1.14.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.
Files changed (106) hide show
  1. iam_policy_validator-1.14.0.dist-info/METADATA +782 -0
  2. iam_policy_validator-1.14.0.dist-info/RECORD +106 -0
  3. iam_policy_validator-1.14.0.dist-info/WHEEL +4 -0
  4. iam_policy_validator-1.14.0.dist-info/entry_points.txt +2 -0
  5. iam_policy_validator-1.14.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 +9 -0
  9. iam_validator/checks/__init__.py +45 -0
  10. iam_validator/checks/action_condition_enforcement.py +1442 -0
  11. iam_validator/checks/action_resource_matching.py +472 -0
  12. iam_validator/checks/action_validation.py +67 -0
  13. iam_validator/checks/condition_key_validation.py +88 -0
  14. iam_validator/checks/condition_type_mismatch.py +257 -0
  15. iam_validator/checks/full_wildcard.py +62 -0
  16. iam_validator/checks/mfa_condition_check.py +105 -0
  17. iam_validator/checks/policy_size.py +114 -0
  18. iam_validator/checks/policy_structure.py +556 -0
  19. iam_validator/checks/policy_type_validation.py +331 -0
  20. iam_validator/checks/principal_validation.py +708 -0
  21. iam_validator/checks/resource_validation.py +135 -0
  22. iam_validator/checks/sensitive_action.py +438 -0
  23. iam_validator/checks/service_wildcard.py +98 -0
  24. iam_validator/checks/set_operator_validation.py +153 -0
  25. iam_validator/checks/sid_uniqueness.py +146 -0
  26. iam_validator/checks/trust_policy_validation.py +509 -0
  27. iam_validator/checks/utils/__init__.py +17 -0
  28. iam_validator/checks/utils/action_parser.py +149 -0
  29. iam_validator/checks/utils/policy_level_checks.py +190 -0
  30. iam_validator/checks/utils/sensitive_action_matcher.py +293 -0
  31. iam_validator/checks/utils/wildcard_expansion.py +86 -0
  32. iam_validator/checks/wildcard_action.py +58 -0
  33. iam_validator/checks/wildcard_resource.py +374 -0
  34. iam_validator/commands/__init__.py +31 -0
  35. iam_validator/commands/analyze.py +549 -0
  36. iam_validator/commands/base.py +48 -0
  37. iam_validator/commands/cache.py +393 -0
  38. iam_validator/commands/completion.py +471 -0
  39. iam_validator/commands/download_services.py +255 -0
  40. iam_validator/commands/post_to_pr.py +86 -0
  41. iam_validator/commands/query.py +485 -0
  42. iam_validator/commands/validate.py +830 -0
  43. iam_validator/core/__init__.py +13 -0
  44. iam_validator/core/access_analyzer.py +671 -0
  45. iam_validator/core/access_analyzer_report.py +640 -0
  46. iam_validator/core/aws_fetcher.py +29 -0
  47. iam_validator/core/aws_service/__init__.py +21 -0
  48. iam_validator/core/aws_service/cache.py +108 -0
  49. iam_validator/core/aws_service/client.py +205 -0
  50. iam_validator/core/aws_service/fetcher.py +641 -0
  51. iam_validator/core/aws_service/parsers.py +149 -0
  52. iam_validator/core/aws_service/patterns.py +51 -0
  53. iam_validator/core/aws_service/storage.py +291 -0
  54. iam_validator/core/aws_service/validators.py +380 -0
  55. iam_validator/core/check_registry.py +679 -0
  56. iam_validator/core/cli.py +134 -0
  57. iam_validator/core/codeowners.py +245 -0
  58. iam_validator/core/condition_validators.py +626 -0
  59. iam_validator/core/config/__init__.py +81 -0
  60. iam_validator/core/config/aws_api.py +35 -0
  61. iam_validator/core/config/aws_global_conditions.py +160 -0
  62. iam_validator/core/config/category_suggestions.py +181 -0
  63. iam_validator/core/config/check_documentation.py +390 -0
  64. iam_validator/core/config/condition_requirements.py +258 -0
  65. iam_validator/core/config/config_loader.py +670 -0
  66. iam_validator/core/config/defaults.py +739 -0
  67. iam_validator/core/config/principal_requirements.py +421 -0
  68. iam_validator/core/config/sensitive_actions.py +672 -0
  69. iam_validator/core/config/service_principals.py +132 -0
  70. iam_validator/core/config/wildcards.py +127 -0
  71. iam_validator/core/constants.py +149 -0
  72. iam_validator/core/diff_parser.py +325 -0
  73. iam_validator/core/finding_fingerprint.py +131 -0
  74. iam_validator/core/formatters/__init__.py +27 -0
  75. iam_validator/core/formatters/base.py +147 -0
  76. iam_validator/core/formatters/console.py +68 -0
  77. iam_validator/core/formatters/csv.py +171 -0
  78. iam_validator/core/formatters/enhanced.py +481 -0
  79. iam_validator/core/formatters/html.py +672 -0
  80. iam_validator/core/formatters/json.py +33 -0
  81. iam_validator/core/formatters/markdown.py +64 -0
  82. iam_validator/core/formatters/sarif.py +251 -0
  83. iam_validator/core/ignore_patterns.py +297 -0
  84. iam_validator/core/ignore_processor.py +309 -0
  85. iam_validator/core/ignored_findings.py +400 -0
  86. iam_validator/core/label_manager.py +197 -0
  87. iam_validator/core/models.py +404 -0
  88. iam_validator/core/policy_checks.py +220 -0
  89. iam_validator/core/policy_loader.py +785 -0
  90. iam_validator/core/pr_commenter.py +780 -0
  91. iam_validator/core/report.py +942 -0
  92. iam_validator/integrations/__init__.py +28 -0
  93. iam_validator/integrations/github_integration.py +1821 -0
  94. iam_validator/integrations/ms_teams.py +442 -0
  95. iam_validator/sdk/__init__.py +220 -0
  96. iam_validator/sdk/arn_matching.py +382 -0
  97. iam_validator/sdk/context.py +222 -0
  98. iam_validator/sdk/exceptions.py +48 -0
  99. iam_validator/sdk/helpers.py +177 -0
  100. iam_validator/sdk/policy_utils.py +451 -0
  101. iam_validator/sdk/query_utils.py +454 -0
  102. iam_validator/sdk/shortcuts.py +283 -0
  103. iam_validator/utils/__init__.py +35 -0
  104. iam_validator/utils/cache.py +105 -0
  105. iam_validator/utils/regex.py +205 -0
  106. iam_validator/utils/terminal.py +22 -0
@@ -0,0 +1,220 @@
1
+ """IAM Policy Validation Module.
2
+
3
+ This module provides comprehensive validation of IAM policies including:
4
+ - Action validation against AWS Service Reference API
5
+ - Condition key validation
6
+ - Resource ARN format validation
7
+ - Security best practices checks
8
+ """
9
+
10
+ import asyncio
11
+ import logging
12
+ from pathlib import Path
13
+
14
+ from iam_validator.core import constants
15
+ from iam_validator.core.aws_service import AWSServiceFetcher
16
+ from iam_validator.core.check_registry import CheckRegistry, create_default_registry
17
+ from iam_validator.core.config.config_loader import ConfigLoader
18
+ from iam_validator.core.models import (
19
+ IAMPolicy,
20
+ PolicyType,
21
+ PolicyValidationResult,
22
+ ValidationIssue,
23
+ )
24
+ from iam_validator.core.policy_loader import PolicyLoader
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ def _should_fail_on_issue(
30
+ issue: ValidationIssue, fail_on_severities: list[str] | None = None
31
+ ) -> bool:
32
+ """Determine if an issue should cause validation to fail.
33
+
34
+ Args:
35
+ issue: Validation issue to check
36
+ fail_on_severities: List of severity levels that should cause failure
37
+ Defaults to ["error"] if not specified
38
+
39
+ Returns:
40
+ True if the issue should cause validation to fail
41
+ """
42
+ if not fail_on_severities:
43
+ fail_on_severities = ["error"] # Default: only fail on errors
44
+
45
+ # Check if issue severity is in the fail list
46
+ return issue.severity in fail_on_severities
47
+
48
+
49
+ async def validate_policies(
50
+ policies: list[tuple[str, IAMPolicy]] | list[tuple[str, IAMPolicy, dict]],
51
+ config_path: str | None = None,
52
+ custom_checks_dir: str | None = None,
53
+ policy_type: PolicyType = "IDENTITY_POLICY",
54
+ aws_services_dir: str | None = None,
55
+ ) -> list[PolicyValidationResult]:
56
+ """Validate multiple policies concurrently.
57
+
58
+ Args:
59
+ policies: List of (file_path, policy) or (file_path, policy, raw_dict) tuples
60
+ config_path: Optional path to configuration file
61
+ custom_checks_dir: Optional path to directory containing custom checks for auto-discovery
62
+ policy_type: Type of policy (IDENTITY_POLICY, RESOURCE_POLICY, SERVICE_CONTROL_POLICY)
63
+ aws_services_dir: Optional path to directory containing pre-downloaded AWS service definitions
64
+ (enables offline mode, overrides config setting)
65
+
66
+ Returns:
67
+ List of validation results
68
+ """
69
+ # Load configuration
70
+ config = ConfigLoader.load_config(explicit_path=config_path, allow_missing=True)
71
+
72
+ # Create registry with or without built-in checks based on configuration
73
+ enable_parallel = config.get_setting("parallel_execution", True)
74
+ enable_builtin_checks = config.get_setting("enable_builtin_checks", True)
75
+
76
+ registry = create_default_registry(
77
+ enable_parallel=enable_parallel, include_builtin_checks=enable_builtin_checks
78
+ )
79
+
80
+ if not enable_builtin_checks:
81
+ logger.info("Built-in checks disabled - using only custom checks")
82
+
83
+ # Apply configuration to built-in checks (if they were registered)
84
+ if enable_builtin_checks:
85
+ ConfigLoader.apply_config_to_registry(config, registry)
86
+
87
+ # Load custom checks from explicit module paths (old method)
88
+ custom_checks = ConfigLoader.load_custom_checks(config, registry)
89
+ if custom_checks:
90
+ logger.info(
91
+ f"Loaded {len(custom_checks)} custom checks from modules: {', '.join(custom_checks)}"
92
+ )
93
+
94
+ # Auto-discover custom checks from directory (new method)
95
+ # Priority: CLI arg > config file > default None
96
+ checks_dir = custom_checks_dir or config.custom_checks_dir
97
+ if checks_dir:
98
+ checks_dir_path = Path(checks_dir).resolve()
99
+ discovered_checks = ConfigLoader.discover_checks_in_directory(checks_dir_path, registry)
100
+ if discovered_checks:
101
+ logger.info(
102
+ f"Auto-discovered {len(discovered_checks)} custom checks from {checks_dir_path}"
103
+ )
104
+
105
+ # Apply configuration again to include custom checks
106
+ # This allows configuring auto-discovered checks via the config file
107
+ ConfigLoader.apply_config_to_registry(config, registry)
108
+
109
+ # Get fail_on_severity setting from config
110
+ fail_on_severities = config.get_setting("fail_on_severity", ["error"])
111
+
112
+ # Get cache settings from config
113
+ cache_enabled = config.get_setting("cache_enabled", True)
114
+ cache_ttl_hours = config.get_setting("cache_ttl_hours", constants.DEFAULT_CACHE_TTL_HOURS)
115
+ cache_directory = config.get_setting("cache_directory", None)
116
+ # CLI argument takes precedence over config file
117
+ services_dir = aws_services_dir or config.get_setting("aws_services_dir", None)
118
+ cache_ttl_seconds = cache_ttl_hours * constants.SECONDS_PER_HOUR
119
+
120
+ # Validate policies using registry
121
+ async with AWSServiceFetcher(
122
+ enable_cache=cache_enabled,
123
+ cache_ttl=cache_ttl_seconds,
124
+ cache_dir=cache_directory,
125
+ aws_services_dir=services_dir,
126
+ ) as fetcher:
127
+ tasks = [
128
+ _validate_policy_with_registry(
129
+ item[1], # policy
130
+ item[0], # file_path
131
+ registry,
132
+ fetcher,
133
+ fail_on_severities,
134
+ policy_type,
135
+ item[2] if len(item) == 3 else None, # raw_dict (optional)
136
+ )
137
+ for item in policies
138
+ ]
139
+
140
+ results = await asyncio.gather(*tasks)
141
+
142
+ return list(results)
143
+
144
+
145
+ async def _validate_policy_with_registry(
146
+ policy: IAMPolicy,
147
+ policy_file: str,
148
+ registry: CheckRegistry,
149
+ fetcher: AWSServiceFetcher,
150
+ fail_on_severities: list[str] | None = None,
151
+ policy_type: PolicyType = "IDENTITY_POLICY",
152
+ raw_policy_dict: dict | None = None,
153
+ ) -> PolicyValidationResult:
154
+ """Validate a single policy using the CheckRegistry system.
155
+
156
+ Args:
157
+ policy: IAM policy to validate
158
+ policy_file: Path to the policy file
159
+ registry: CheckRegistry instance with configured checks
160
+ fetcher: AWS service fetcher instance
161
+ fail_on_severities: List of severity levels that should cause validation to fail
162
+ policy_type: Type of policy (IDENTITY_POLICY, RESOURCE_POLICY, SERVICE_CONTROL_POLICY)
163
+ raw_policy_dict: Raw policy dictionary for structural validation (optional, will be loaded if not provided)
164
+
165
+ Returns:
166
+ PolicyValidationResult with all findings
167
+ """
168
+ result = PolicyValidationResult(policy_file=policy_file, is_valid=True, policy_type=policy_type)
169
+
170
+ # Load raw dict if not provided (for structural validation)
171
+ if raw_policy_dict is None:
172
+ loader = PolicyLoader()
173
+ loaded_result = loader.load_from_file(policy_file, return_raw_dict=True)
174
+ if loaded_result and isinstance(loaded_result, tuple):
175
+ raw_policy_dict = loaded_result[1]
176
+
177
+ # Apply automatic policy-type validation (not configurable - always runs)
178
+ # Note: Import here to avoid circular import (policy_checks -> checks -> sdk -> policy_checks)
179
+ from iam_validator.checks import ( # pylint: disable=import-outside-toplevel
180
+ policy_type_validation,
181
+ )
182
+
183
+ policy_type_issues = await policy_type_validation.execute_policy(
184
+ policy, policy_file, policy_type=policy_type
185
+ )
186
+ result.issues.extend(policy_type_issues) # pylint: disable=no-member
187
+
188
+ # Run policy-level checks first (checks that need to see the entire policy)
189
+ # These checks examine relationships between statements, not individual statements
190
+ policy_level_issues = await registry.execute_policy_checks(
191
+ policy, policy_file, fetcher, policy_type, raw_policy_dict=raw_policy_dict
192
+ )
193
+ result.issues.extend(policy_level_issues) # pylint: disable=no-member
194
+
195
+ # Execute all statement-level checks for each statement
196
+ for idx, statement in enumerate(policy.statement or []):
197
+ # Execute all registered checks in parallel (with ignore_patterns filtering)
198
+ issues = await registry.execute_checks_parallel(statement, idx, fetcher, policy_file)
199
+
200
+ # Add issues to result
201
+ result.issues.extend(issues) # pylint: disable=no-member
202
+
203
+ # Update counters (approximate based on what was checked)
204
+ actions = statement.get_actions()
205
+ resources = statement.get_resources()
206
+
207
+ result.actions_checked += len([a for a in actions if a != "*"])
208
+ result.resources_checked += len([r for r in resources if r != "*"])
209
+
210
+ # Count condition keys if present
211
+ if statement.condition:
212
+ for conditions in statement.condition.values():
213
+ result.condition_keys_checked += len(conditions)
214
+
215
+ # Update final validation status based on fail_on_severities configuration
216
+ result.is_valid = (
217
+ len([i for i in result.issues if _should_fail_on_issue(i, fail_on_severities)]) == 0
218
+ )
219
+
220
+ return result