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.
- iam_policy_validator-1.14.0.dist-info/METADATA +782 -0
- iam_policy_validator-1.14.0.dist-info/RECORD +106 -0
- iam_policy_validator-1.14.0.dist-info/WHEEL +4 -0
- iam_policy_validator-1.14.0.dist-info/entry_points.txt +2 -0
- iam_policy_validator-1.14.0.dist-info/licenses/LICENSE +21 -0
- iam_validator/__init__.py +27 -0
- iam_validator/__main__.py +11 -0
- iam_validator/__version__.py +9 -0
- iam_validator/checks/__init__.py +45 -0
- iam_validator/checks/action_condition_enforcement.py +1442 -0
- iam_validator/checks/action_resource_matching.py +472 -0
- iam_validator/checks/action_validation.py +67 -0
- iam_validator/checks/condition_key_validation.py +88 -0
- iam_validator/checks/condition_type_mismatch.py +257 -0
- iam_validator/checks/full_wildcard.py +62 -0
- iam_validator/checks/mfa_condition_check.py +105 -0
- iam_validator/checks/policy_size.py +114 -0
- iam_validator/checks/policy_structure.py +556 -0
- iam_validator/checks/policy_type_validation.py +331 -0
- iam_validator/checks/principal_validation.py +708 -0
- iam_validator/checks/resource_validation.py +135 -0
- iam_validator/checks/sensitive_action.py +438 -0
- iam_validator/checks/service_wildcard.py +98 -0
- iam_validator/checks/set_operator_validation.py +153 -0
- iam_validator/checks/sid_uniqueness.py +146 -0
- iam_validator/checks/trust_policy_validation.py +509 -0
- iam_validator/checks/utils/__init__.py +17 -0
- iam_validator/checks/utils/action_parser.py +149 -0
- iam_validator/checks/utils/policy_level_checks.py +190 -0
- iam_validator/checks/utils/sensitive_action_matcher.py +293 -0
- iam_validator/checks/utils/wildcard_expansion.py +86 -0
- iam_validator/checks/wildcard_action.py +58 -0
- iam_validator/checks/wildcard_resource.py +374 -0
- iam_validator/commands/__init__.py +31 -0
- iam_validator/commands/analyze.py +549 -0
- iam_validator/commands/base.py +48 -0
- iam_validator/commands/cache.py +393 -0
- iam_validator/commands/completion.py +471 -0
- iam_validator/commands/download_services.py +255 -0
- iam_validator/commands/post_to_pr.py +86 -0
- iam_validator/commands/query.py +485 -0
- iam_validator/commands/validate.py +830 -0
- iam_validator/core/__init__.py +13 -0
- iam_validator/core/access_analyzer.py +671 -0
- iam_validator/core/access_analyzer_report.py +640 -0
- iam_validator/core/aws_fetcher.py +29 -0
- iam_validator/core/aws_service/__init__.py +21 -0
- iam_validator/core/aws_service/cache.py +108 -0
- iam_validator/core/aws_service/client.py +205 -0
- iam_validator/core/aws_service/fetcher.py +641 -0
- iam_validator/core/aws_service/parsers.py +149 -0
- iam_validator/core/aws_service/patterns.py +51 -0
- iam_validator/core/aws_service/storage.py +291 -0
- iam_validator/core/aws_service/validators.py +380 -0
- iam_validator/core/check_registry.py +679 -0
- iam_validator/core/cli.py +134 -0
- iam_validator/core/codeowners.py +245 -0
- 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 +181 -0
- iam_validator/core/config/check_documentation.py +390 -0
- iam_validator/core/config/condition_requirements.py +258 -0
- iam_validator/core/config/config_loader.py +670 -0
- iam_validator/core/config/defaults.py +739 -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 +132 -0
- iam_validator/core/config/wildcards.py +127 -0
- iam_validator/core/constants.py +149 -0
- iam_validator/core/diff_parser.py +325 -0
- iam_validator/core/finding_fingerprint.py +131 -0
- iam_validator/core/formatters/__init__.py +27 -0
- iam_validator/core/formatters/base.py +147 -0
- iam_validator/core/formatters/console.py +68 -0
- iam_validator/core/formatters/csv.py +171 -0
- iam_validator/core/formatters/enhanced.py +481 -0
- iam_validator/core/formatters/html.py +672 -0
- iam_validator/core/formatters/json.py +33 -0
- iam_validator/core/formatters/markdown.py +64 -0
- iam_validator/core/formatters/sarif.py +251 -0
- iam_validator/core/ignore_patterns.py +297 -0
- iam_validator/core/ignore_processor.py +309 -0
- iam_validator/core/ignored_findings.py +400 -0
- iam_validator/core/label_manager.py +197 -0
- iam_validator/core/models.py +404 -0
- iam_validator/core/policy_checks.py +220 -0
- iam_validator/core/policy_loader.py +785 -0
- iam_validator/core/pr_commenter.py +780 -0
- iam_validator/core/report.py +942 -0
- iam_validator/integrations/__init__.py +28 -0
- iam_validator/integrations/github_integration.py +1821 -0
- iam_validator/integrations/ms_teams.py +442 -0
- iam_validator/sdk/__init__.py +220 -0
- iam_validator/sdk/arn_matching.py +382 -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 +451 -0
- iam_validator/sdk/query_utils.py +454 -0
- iam_validator/sdk/shortcuts.py +283 -0
- iam_validator/utils/__init__.py +35 -0
- iam_validator/utils/cache.py +105 -0
- iam_validator/utils/regex.py +205 -0
- 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
|