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
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Default service principals for resource policy validation.
|
|
3
|
+
|
|
4
|
+
These are AWS service principals that are commonly used and considered safe
|
|
5
|
+
in resource-based policies (S3 bucket policies, SNS topic policies, etc.).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Final
|
|
9
|
+
|
|
10
|
+
# ============================================================================
|
|
11
|
+
# Allowed Service Principals
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# These AWS service principals are commonly used in resource policies
|
|
14
|
+
# and are generally considered safe to allow
|
|
15
|
+
|
|
16
|
+
DEFAULT_SERVICE_PRINCIPALS: Final[tuple[str, ...]] = (
|
|
17
|
+
"cloudfront.amazonaws.com",
|
|
18
|
+
"s3.amazonaws.com",
|
|
19
|
+
"sns.amazonaws.com",
|
|
20
|
+
"lambda.amazonaws.com",
|
|
21
|
+
"logs.amazonaws.com",
|
|
22
|
+
"events.amazonaws.com",
|
|
23
|
+
"elasticloadbalancing.amazonaws.com",
|
|
24
|
+
"cloudtrail.amazonaws.com",
|
|
25
|
+
"config.amazonaws.com",
|
|
26
|
+
"backup.amazonaws.com",
|
|
27
|
+
"cloudwatch.amazonaws.com",
|
|
28
|
+
"monitoring.amazonaws.com",
|
|
29
|
+
"ec2.amazonaws.com",
|
|
30
|
+
"ecs-tasks.amazonaws.com",
|
|
31
|
+
"eks.amazonaws.com",
|
|
32
|
+
"apigateway.amazonaws.com",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def get_service_principals() -> tuple[str, ...]:
|
|
37
|
+
"""
|
|
38
|
+
Get tuple of allowed service principals.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Tuple of AWS service principal names
|
|
42
|
+
"""
|
|
43
|
+
return DEFAULT_SERVICE_PRINCIPALS
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_allowed_service_principal(principal: str) -> bool:
|
|
47
|
+
"""
|
|
48
|
+
Check if a principal is an allowed service principal.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
principal: Principal to check (e.g., "lambda.amazonaws.com")
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
True if principal is in allowed list
|
|
55
|
+
|
|
56
|
+
Performance: O(n) but small list (~16 items)
|
|
57
|
+
"""
|
|
58
|
+
return principal in DEFAULT_SERVICE_PRINCIPALS
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_service_principals_by_category() -> dict[str, tuple[str, ...]]:
|
|
62
|
+
"""
|
|
63
|
+
Get service principals organized by service category.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Dictionary mapping categories to service principal tuples
|
|
67
|
+
"""
|
|
68
|
+
return {
|
|
69
|
+
"storage": (
|
|
70
|
+
"s3.amazonaws.com",
|
|
71
|
+
"backup.amazonaws.com",
|
|
72
|
+
),
|
|
73
|
+
"compute": (
|
|
74
|
+
"lambda.amazonaws.com",
|
|
75
|
+
"ec2.amazonaws.com",
|
|
76
|
+
"ecs-tasks.amazonaws.com",
|
|
77
|
+
"eks.amazonaws.com",
|
|
78
|
+
),
|
|
79
|
+
"networking": (
|
|
80
|
+
"cloudfront.amazonaws.com",
|
|
81
|
+
"elasticloadbalancing.amazonaws.com",
|
|
82
|
+
"apigateway.amazonaws.com",
|
|
83
|
+
),
|
|
84
|
+
"monitoring": (
|
|
85
|
+
"logs.amazonaws.com",
|
|
86
|
+
"cloudwatch.amazonaws.com",
|
|
87
|
+
"monitoring.amazonaws.com",
|
|
88
|
+
"cloudtrail.amazonaws.com",
|
|
89
|
+
),
|
|
90
|
+
"messaging": (
|
|
91
|
+
"sns.amazonaws.com",
|
|
92
|
+
"events.amazonaws.com",
|
|
93
|
+
),
|
|
94
|
+
"management": ("config.amazonaws.com",),
|
|
95
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Default wildcard configurations for security best practices checks.
|
|
3
|
+
|
|
4
|
+
These wildcards define which actions are considered "safe" to use with
|
|
5
|
+
Resource: "*" (e.g., read-only describe operations).
|
|
6
|
+
|
|
7
|
+
Using Python tuples instead of YAML lists provides:
|
|
8
|
+
- Zero parsing overhead
|
|
9
|
+
- Immutable by default (tuples)
|
|
10
|
+
- Better performance
|
|
11
|
+
- Easy PyPI packaging
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Final
|
|
15
|
+
|
|
16
|
+
# ============================================================================
|
|
17
|
+
# Allowed Wildcards for Resource: "*"
|
|
18
|
+
# ============================================================================
|
|
19
|
+
# These action patterns are considered safe to use with wildcard resources
|
|
20
|
+
# They are typically read-only operations that need broad resource access
|
|
21
|
+
|
|
22
|
+
DEFAULT_ALLOWED_WILDCARDS: Final[tuple[str, ...]] = (
|
|
23
|
+
# Auto Scaling
|
|
24
|
+
"autoscaling:Describe*",
|
|
25
|
+
# CloudWatch
|
|
26
|
+
"cloudwatch:Describe*",
|
|
27
|
+
"cloudwatch:Get*",
|
|
28
|
+
"cloudwatch:List*",
|
|
29
|
+
# DynamoDB
|
|
30
|
+
"dynamodb:Describe*",
|
|
31
|
+
# EC2
|
|
32
|
+
"ec2:Describe*",
|
|
33
|
+
# Elastic Load Balancing
|
|
34
|
+
"elasticloadbalancing:Describe*",
|
|
35
|
+
# IAM (non-sensitive read operations)
|
|
36
|
+
"iam:Get*",
|
|
37
|
+
"iam:List*",
|
|
38
|
+
# KMS
|
|
39
|
+
"kms:Describe*",
|
|
40
|
+
# Lambda
|
|
41
|
+
"lambda:Get*",
|
|
42
|
+
"lambda:List*",
|
|
43
|
+
# CloudWatch Logs
|
|
44
|
+
"logs:Describe*",
|
|
45
|
+
"logs:Filter*",
|
|
46
|
+
"logs:Get*",
|
|
47
|
+
# RDS
|
|
48
|
+
"rds:Describe*",
|
|
49
|
+
# Route53
|
|
50
|
+
"route53:Get*",
|
|
51
|
+
"route53:List*",
|
|
52
|
+
# S3 (safe read operations only)
|
|
53
|
+
"s3:Describe*",
|
|
54
|
+
"s3:GetBucket*",
|
|
55
|
+
"s3:GetM*",
|
|
56
|
+
"s3:List*",
|
|
57
|
+
# SQS
|
|
58
|
+
"sqs:Get*",
|
|
59
|
+
"sqs:List*",
|
|
60
|
+
# API Gateway
|
|
61
|
+
"apigateway:GET",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# ============================================================================
|
|
65
|
+
# Service-Level Wildcards (Allowed Services)
|
|
66
|
+
# ============================================================================
|
|
67
|
+
# Services that are allowed to use service-level wildcards like "logs:*"
|
|
68
|
+
# These are typically low-risk monitoring/logging services
|
|
69
|
+
|
|
70
|
+
DEFAULT_SERVICE_WILDCARDS: Final[tuple[str, ...]] = (
|
|
71
|
+
"logs",
|
|
72
|
+
"cloudwatch",
|
|
73
|
+
"xray",
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_allowed_wildcards() -> tuple[str, ...]:
|
|
78
|
+
"""
|
|
79
|
+
Get tuple of allowed wildcard action patterns.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Tuple of action patterns that are safe to use with Resource: "*"
|
|
83
|
+
"""
|
|
84
|
+
return DEFAULT_ALLOWED_WILDCARDS
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_allowed_service_wildcards() -> tuple[str, ...]:
|
|
88
|
+
"""
|
|
89
|
+
Get tuple of services allowed to use service-level wildcards.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Tuple of service names (e.g., "logs", "cloudwatch")
|
|
93
|
+
"""
|
|
94
|
+
return DEFAULT_SERVICE_WILDCARDS
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def is_allowed_wildcard(pattern: str) -> bool:
|
|
98
|
+
"""
|
|
99
|
+
Check if a wildcard pattern is in the allowed list.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
pattern: Action pattern to check (e.g., "s3:List*")
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
True if pattern is in allowed wildcards
|
|
106
|
+
|
|
107
|
+
Performance: O(n) but typically small list (~25 items)
|
|
108
|
+
"""
|
|
109
|
+
return pattern in DEFAULT_ALLOWED_WILDCARDS
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def is_allowed_service_wildcard(service: str) -> bool:
|
|
113
|
+
"""
|
|
114
|
+
Check if a service is allowed to use service-level wildcards.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
service: Service name (e.g., "logs", "s3")
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
True if service is in allowed list
|
|
121
|
+
|
|
122
|
+
Performance: O(n) but very small list (~3 items)
|
|
123
|
+
"""
|
|
124
|
+
return service in DEFAULT_SERVICE_WILDCARDS
|
|
@@ -36,13 +36,18 @@ class EnhancedFormatter(OutputFormatter):
|
|
|
36
36
|
|
|
37
37
|
Args:
|
|
38
38
|
report: Validation report to format
|
|
39
|
-
**kwargs: Additional options
|
|
39
|
+
**kwargs: Additional options:
|
|
40
|
+
- color (bool): Enable color output (default: True)
|
|
41
|
+
- show_summary (bool): Show Executive Summary panel (default: True)
|
|
42
|
+
- show_severity_breakdown (bool): Show Issue Severity Breakdown panel (default: True)
|
|
40
43
|
|
|
41
44
|
Returns:
|
|
42
45
|
Formatted string with ANSI codes for console display
|
|
43
46
|
"""
|
|
44
47
|
# Allow disabling color for plain text output
|
|
45
48
|
color = kwargs.get("color", True)
|
|
49
|
+
show_summary = kwargs.get("show_summary", True)
|
|
50
|
+
show_severity_breakdown = kwargs.get("show_severity_breakdown", True)
|
|
46
51
|
|
|
47
52
|
# Use StringIO to capture Rich console output
|
|
48
53
|
string_buffer = StringIO()
|
|
@@ -58,11 +63,12 @@ class EnhancedFormatter(OutputFormatter):
|
|
|
58
63
|
console.print(Panel(title, border_style="bright_blue", padding=(1, 0)))
|
|
59
64
|
console.print()
|
|
60
65
|
|
|
61
|
-
# Executive Summary with progress bars
|
|
62
|
-
|
|
66
|
+
# Executive Summary with progress bars (optional)
|
|
67
|
+
if show_summary:
|
|
68
|
+
self._print_summary_panel(console, report)
|
|
63
69
|
|
|
64
|
-
# Severity breakdown if there are issues
|
|
65
|
-
if report.total_issues > 0:
|
|
70
|
+
# Severity breakdown if there are issues (optional)
|
|
71
|
+
if show_severity_breakdown and report.total_issues > 0:
|
|
66
72
|
console.print()
|
|
67
73
|
self._print_severity_breakdown(console, report)
|
|
68
74
|
|
|
@@ -100,7 +100,7 @@ class SARIFFormatter(OutputFormatter):
|
|
|
100
100
|
"text": "The specified condition key is not valid for this action"
|
|
101
101
|
},
|
|
102
102
|
"helpUri": "https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html",
|
|
103
|
-
"defaultConfiguration": {"level": "
|
|
103
|
+
"defaultConfiguration": {"level": "error"},
|
|
104
104
|
},
|
|
105
105
|
{
|
|
106
106
|
"id": "invalid-resource",
|
|
@@ -110,18 +110,55 @@ class SARIFFormatter(OutputFormatter):
|
|
|
110
110
|
"defaultConfiguration": {"level": "error"},
|
|
111
111
|
},
|
|
112
112
|
{
|
|
113
|
-
"id": "
|
|
114
|
-
"shortDescription": {"text": "
|
|
113
|
+
"id": "duplicate-sid",
|
|
114
|
+
"shortDescription": {"text": "Duplicate Statement ID"},
|
|
115
|
+
"fullDescription": {
|
|
116
|
+
"text": "Multiple statements use the same Statement ID (Sid), which can cause confusion"
|
|
117
|
+
},
|
|
118
|
+
"helpUri": "https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_sid.html",
|
|
119
|
+
"defaultConfiguration": {"level": "error"},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"id": "overly-permissive",
|
|
123
|
+
"shortDescription": {"text": "Overly Permissive Policy"},
|
|
115
124
|
"fullDescription": {
|
|
116
|
-
"text": "
|
|
125
|
+
"text": "Policy grants overly broad permissions using wildcards in actions or resources"
|
|
117
126
|
},
|
|
118
|
-
"helpUri": "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html",
|
|
127
|
+
"helpUri": "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege",
|
|
119
128
|
"defaultConfiguration": {"level": "warning"},
|
|
120
129
|
},
|
|
121
130
|
{
|
|
122
|
-
"id": "
|
|
123
|
-
"shortDescription": {"text": "
|
|
124
|
-
"fullDescription": {
|
|
131
|
+
"id": "missing-condition",
|
|
132
|
+
"shortDescription": {"text": "Missing Condition Restrictions"},
|
|
133
|
+
"fullDescription": {
|
|
134
|
+
"text": "Sensitive actions should include condition restrictions to limit when they can be used"
|
|
135
|
+
},
|
|
136
|
+
"helpUri": "https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#use-policy-conditions",
|
|
137
|
+
"defaultConfiguration": {"level": "warning"},
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
"id": "missing-required-condition",
|
|
141
|
+
"shortDescription": {"text": "Missing Required Condition"},
|
|
142
|
+
"fullDescription": {
|
|
143
|
+
"text": "Specific actions require certain conditions to prevent privilege escalation or security issues"
|
|
144
|
+
},
|
|
145
|
+
"helpUri": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html",
|
|
146
|
+
"defaultConfiguration": {"level": "error"},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"id": "invalid-principal",
|
|
150
|
+
"shortDescription": {"text": "Invalid Principal"},
|
|
151
|
+
"fullDescription": {
|
|
152
|
+
"text": "The specified principal is invalid or improperly formatted"
|
|
153
|
+
},
|
|
154
|
+
"helpUri": "https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html",
|
|
155
|
+
"defaultConfiguration": {"level": "error"},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"id": "general-issue",
|
|
159
|
+
"shortDescription": {"text": "IAM Policy Issue"},
|
|
160
|
+
"fullDescription": {"text": "General IAM policy validation issue"},
|
|
161
|
+
"helpUri": "https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html",
|
|
125
162
|
"defaultConfiguration": {"level": "warning"},
|
|
126
163
|
},
|
|
127
164
|
]
|
|
@@ -170,18 +207,45 @@ class SARIFFormatter(OutputFormatter):
|
|
|
170
207
|
return results
|
|
171
208
|
|
|
172
209
|
def _get_rule_id(self, issue: ValidationIssue) -> str:
|
|
173
|
-
"""Map issue to SARIF rule ID.
|
|
210
|
+
"""Map issue to SARIF rule ID.
|
|
211
|
+
|
|
212
|
+
Uses the issue_type field directly, converting underscores to hyphens
|
|
213
|
+
for SARIF rule ID format. Falls back to heuristic matching for unknown types.
|
|
214
|
+
"""
|
|
215
|
+
# Map common issue types directly
|
|
216
|
+
issue_type_map = {
|
|
217
|
+
"invalid_action": "invalid-action",
|
|
218
|
+
"invalid_condition_key": "invalid-condition-key",
|
|
219
|
+
"invalid_resource": "invalid-resource",
|
|
220
|
+
"duplicate_sid": "duplicate-sid",
|
|
221
|
+
"overly_permissive": "overly-permissive",
|
|
222
|
+
"missing_condition": "missing-condition",
|
|
223
|
+
"missing_required_condition": "missing-required-condition",
|
|
224
|
+
"invalid_principal": "invalid-principal",
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
# Try direct mapping from issue_type
|
|
228
|
+
if issue.issue_type in issue_type_map:
|
|
229
|
+
return issue_type_map[issue.issue_type]
|
|
230
|
+
|
|
231
|
+
# Fallback: heuristic matching based on message
|
|
174
232
|
message_lower = issue.message.lower()
|
|
175
233
|
|
|
176
234
|
if "action" in message_lower and "not found" in message_lower:
|
|
177
235
|
return "invalid-action"
|
|
178
236
|
elif "condition key" in message_lower:
|
|
179
237
|
return "invalid-condition-key"
|
|
180
|
-
elif "
|
|
238
|
+
elif "duplicate" in message_lower and "sid" in message_lower:
|
|
239
|
+
return "duplicate-sid"
|
|
240
|
+
elif "wildcard" in message_lower or "overly permissive" in message_lower:
|
|
241
|
+
return "overly-permissive"
|
|
242
|
+
elif "missing" in message_lower and "condition" in message_lower:
|
|
243
|
+
if "required" in message_lower:
|
|
244
|
+
return "missing-required-condition"
|
|
245
|
+
return "missing-condition"
|
|
246
|
+
elif "principal" in message_lower:
|
|
247
|
+
return "invalid-principal"
|
|
248
|
+
elif "resource" in message_lower or "arn" in message_lower:
|
|
181
249
|
return "invalid-resource"
|
|
182
|
-
elif "wildcard" in message_lower or "*" in issue.message:
|
|
183
|
-
return "security-wildcard"
|
|
184
|
-
elif "sensitive" in message_lower:
|
|
185
|
-
return "security-sensitive-action"
|
|
186
250
|
else:
|
|
187
251
|
return "general-issue"
|
iam_validator/core/models.py
CHANGED
|
@@ -45,9 +45,22 @@ class ResourceType(BaseModel):
|
|
|
45
45
|
model_config = ConfigDict(populate_by_name=True)
|
|
46
46
|
|
|
47
47
|
name: str = Field(alias="Name")
|
|
48
|
-
|
|
48
|
+
arn_formats: list[str] | None = Field(default=None, alias="ARNFormats")
|
|
49
49
|
condition_keys: list[str] | None = Field(default_factory=list, alias="ConditionKeys")
|
|
50
50
|
|
|
51
|
+
@property
|
|
52
|
+
def arn_pattern(self) -> str | None:
|
|
53
|
+
"""
|
|
54
|
+
Get the first ARN format for backwards compatibility.
|
|
55
|
+
|
|
56
|
+
AWS provides ARN formats as an array (ARNFormats), but most code
|
|
57
|
+
just needs a single pattern. This property returns the first one.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
First ARN format string, or None if no formats are defined
|
|
61
|
+
"""
|
|
62
|
+
return self.arn_formats[0] if self.arn_formats else None
|
|
63
|
+
|
|
51
64
|
|
|
52
65
|
class ConditionKey(BaseModel):
|
|
53
66
|
"""Details about an AWS condition key."""
|
|
@@ -491,7 +491,7 @@ async def validate_policies(
|
|
|
491
491
|
if not use_registry:
|
|
492
492
|
# Legacy path - use old PolicyValidator
|
|
493
493
|
# Load config for cache settings even in legacy mode
|
|
494
|
-
from iam_validator.core.config_loader import ConfigLoader
|
|
494
|
+
from iam_validator.core.config.config_loader import ConfigLoader
|
|
495
495
|
|
|
496
496
|
config = ConfigLoader.load_config(explicit_path=config_path, allow_missing=True)
|
|
497
497
|
cache_enabled = config.get_setting("cache_enabled", True)
|
|
@@ -519,7 +519,7 @@ async def validate_policies(
|
|
|
519
519
|
|
|
520
520
|
# New path - use CheckRegistry system
|
|
521
521
|
from iam_validator.core.check_registry import create_default_registry
|
|
522
|
-
from iam_validator.core.config_loader import ConfigLoader
|
|
522
|
+
from iam_validator.core.config.config_loader import ConfigLoader
|
|
523
523
|
|
|
524
524
|
# Load configuration
|
|
525
525
|
config = ConfigLoader.load_config(explicit_path=config_path, allow_missing=True)
|
|
@@ -630,8 +630,8 @@ async def _validate_policy_with_registry(
|
|
|
630
630
|
|
|
631
631
|
# Execute all statement-level checks for each statement
|
|
632
632
|
for idx, statement in enumerate(policy.statement):
|
|
633
|
-
# Execute all registered checks in parallel
|
|
634
|
-
issues = await registry.execute_checks_parallel(statement, idx, fetcher)
|
|
633
|
+
# Execute all registered checks in parallel (with ignore_patterns filtering)
|
|
634
|
+
issues = await registry.execute_checks_parallel(statement, idx, fetcher, policy_file)
|
|
635
635
|
|
|
636
636
|
# Add issues to result
|
|
637
637
|
result.issues.extend(issues)
|
|
@@ -313,7 +313,7 @@ async def post_report_to_pr(
|
|
|
313
313
|
report = ValidationReport.model_validate(report_data)
|
|
314
314
|
|
|
315
315
|
# Load config to get fail_on_severity setting
|
|
316
|
-
from iam_validator.core.config_loader import ConfigLoader
|
|
316
|
+
from iam_validator.core.config.config_loader import ConfigLoader
|
|
317
317
|
|
|
318
318
|
config = ConfigLoader.load_config(config_path)
|
|
319
319
|
fail_on_severities = config.get_setting("fail_on_severity", ["error", "critical"])
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""
|
|
2
|
+
IAM Policy Validator SDK - Public API for library usage.
|
|
3
|
+
|
|
4
|
+
This module provides the complete public API for using IAM Policy Validator
|
|
5
|
+
as a Python library. It exposes both high-level convenience functions and
|
|
6
|
+
low-level components for custom integrations.
|
|
7
|
+
|
|
8
|
+
Quick Start:
|
|
9
|
+
Basic validation:
|
|
10
|
+
>>> from iam_validator.sdk import validate_file
|
|
11
|
+
>>> result = await validate_file("policy.json")
|
|
12
|
+
>>> print(f"Valid: {result.is_valid}")
|
|
13
|
+
|
|
14
|
+
With context manager:
|
|
15
|
+
>>> from iam_validator.sdk import validator
|
|
16
|
+
>>> async with validator() as v:
|
|
17
|
+
... result = await v.validate_file("policy.json")
|
|
18
|
+
... v.generate_report([result])
|
|
19
|
+
|
|
20
|
+
Policy manipulation:
|
|
21
|
+
>>> from iam_validator.sdk import parse_policy, get_policy_summary
|
|
22
|
+
>>> policy = parse_policy(policy_json)
|
|
23
|
+
>>> summary = get_policy_summary(policy)
|
|
24
|
+
>>> print(f"Actions: {summary['action_count']}")
|
|
25
|
+
|
|
26
|
+
Custom check development:
|
|
27
|
+
>>> from iam_validator.sdk import PolicyCheck, CheckHelper
|
|
28
|
+
>>> class MyCheck(PolicyCheck):
|
|
29
|
+
... @property
|
|
30
|
+
... def check_id(self) -> str:
|
|
31
|
+
... return "my_check"
|
|
32
|
+
... async def execute(self, statement, idx, fetcher, config):
|
|
33
|
+
... helper = CheckHelper(fetcher)
|
|
34
|
+
... # Use helper.arn_matches(), helper.create_issue(), etc.
|
|
35
|
+
... return []
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
# === High-level validation functions (shortcuts) ===
|
|
39
|
+
# === AWS utilities ===
|
|
40
|
+
from iam_validator.core.aws_fetcher import AWSServiceFetcher
|
|
41
|
+
|
|
42
|
+
# === Core validation components (for advanced usage) ===
|
|
43
|
+
from iam_validator.core.check_registry import CheckRegistry, PolicyCheck
|
|
44
|
+
|
|
45
|
+
# === ValidatorConfiguration ===
|
|
46
|
+
from iam_validator.core.config.config_loader import ValidatorConfig, load_validator_config
|
|
47
|
+
|
|
48
|
+
# === Reporting ===
|
|
49
|
+
from iam_validator.core.formatters.csv import CSVFormatter
|
|
50
|
+
from iam_validator.core.formatters.html import HTMLFormatter
|
|
51
|
+
from iam_validator.core.formatters.json import JSONFormatter
|
|
52
|
+
from iam_validator.core.formatters.markdown import MarkdownFormatter
|
|
53
|
+
from iam_validator.core.formatters.sarif import SARIFFormatter
|
|
54
|
+
|
|
55
|
+
# === Models (for type hints and inspection) ===
|
|
56
|
+
from iam_validator.core.models import (
|
|
57
|
+
IAMPolicy,
|
|
58
|
+
PolicyValidationResult,
|
|
59
|
+
Statement,
|
|
60
|
+
ValidationIssue,
|
|
61
|
+
)
|
|
62
|
+
from iam_validator.core.policy_checks import validate_policies
|
|
63
|
+
from iam_validator.core.policy_loader import PolicyLoader
|
|
64
|
+
from iam_validator.core.report import ReportGenerator
|
|
65
|
+
|
|
66
|
+
# === ARN matching utilities ===
|
|
67
|
+
from iam_validator.sdk.arn_matching import (
|
|
68
|
+
arn_matches,
|
|
69
|
+
arn_strictly_valid,
|
|
70
|
+
convert_aws_pattern_to_wildcard,
|
|
71
|
+
is_glob_match,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# === Context managers ===
|
|
75
|
+
from iam_validator.sdk.context import (
|
|
76
|
+
ValidationContext,
|
|
77
|
+
validator,
|
|
78
|
+
validator_from_config,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# === Public exceptions ===
|
|
82
|
+
from iam_validator.sdk.exceptions import (
|
|
83
|
+
AWSServiceError,
|
|
84
|
+
ConfigurationError,
|
|
85
|
+
IAMValidatorError,
|
|
86
|
+
InvalidPolicyFormatError,
|
|
87
|
+
PolicyLoadError,
|
|
88
|
+
PolicyValidationError,
|
|
89
|
+
UnsupportedPolicyTypeError,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# === Custom check development ===
|
|
93
|
+
from iam_validator.sdk.helpers import CheckHelper, expand_actions
|
|
94
|
+
|
|
95
|
+
# === Policy manipulation utilities ===
|
|
96
|
+
from iam_validator.sdk.policy_utils import (
|
|
97
|
+
extract_actions,
|
|
98
|
+
extract_condition_keys,
|
|
99
|
+
extract_resources,
|
|
100
|
+
find_statements_with_action,
|
|
101
|
+
find_statements_with_resource,
|
|
102
|
+
get_policy_summary,
|
|
103
|
+
has_public_access,
|
|
104
|
+
is_resource_policy,
|
|
105
|
+
merge_policies,
|
|
106
|
+
normalize_policy,
|
|
107
|
+
parse_policy,
|
|
108
|
+
policy_to_dict,
|
|
109
|
+
policy_to_json,
|
|
110
|
+
)
|
|
111
|
+
from iam_validator.sdk.shortcuts import (
|
|
112
|
+
count_issues_by_severity,
|
|
113
|
+
get_issues,
|
|
114
|
+
quick_validate,
|
|
115
|
+
validate_directory,
|
|
116
|
+
validate_file,
|
|
117
|
+
validate_json,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
__all__ = [
|
|
121
|
+
# === High-level shortcuts ===
|
|
122
|
+
"validate_file",
|
|
123
|
+
"validate_directory",
|
|
124
|
+
"validate_json",
|
|
125
|
+
"quick_validate",
|
|
126
|
+
"get_issues",
|
|
127
|
+
"count_issues_by_severity",
|
|
128
|
+
# === Context managers ===
|
|
129
|
+
"validator",
|
|
130
|
+
"validator_from_config",
|
|
131
|
+
"ValidationContext",
|
|
132
|
+
# === Policy utilities ===
|
|
133
|
+
"parse_policy",
|
|
134
|
+
"normalize_policy",
|
|
135
|
+
"extract_actions",
|
|
136
|
+
"extract_resources",
|
|
137
|
+
"extract_condition_keys",
|
|
138
|
+
"find_statements_with_action",
|
|
139
|
+
"find_statements_with_resource",
|
|
140
|
+
"merge_policies",
|
|
141
|
+
"get_policy_summary",
|
|
142
|
+
"policy_to_json",
|
|
143
|
+
"policy_to_dict",
|
|
144
|
+
"is_resource_policy",
|
|
145
|
+
"has_public_access",
|
|
146
|
+
# === ARN utilities ===
|
|
147
|
+
"arn_matches",
|
|
148
|
+
"arn_strictly_valid",
|
|
149
|
+
"is_glob_match",
|
|
150
|
+
"convert_aws_pattern_to_wildcard",
|
|
151
|
+
# === Custom check development ===
|
|
152
|
+
"PolicyCheck",
|
|
153
|
+
"CheckRegistry",
|
|
154
|
+
"CheckHelper",
|
|
155
|
+
"expand_actions",
|
|
156
|
+
# === Core validation (advanced) ===
|
|
157
|
+
"validate_policies",
|
|
158
|
+
"PolicyLoader",
|
|
159
|
+
# === Reporting ===
|
|
160
|
+
"ReportGenerator",
|
|
161
|
+
"JSONFormatter",
|
|
162
|
+
"HTMLFormatter",
|
|
163
|
+
"CSVFormatter",
|
|
164
|
+
"MarkdownFormatter",
|
|
165
|
+
"SARIFFormatter",
|
|
166
|
+
# === Models ===
|
|
167
|
+
"ValidationIssue",
|
|
168
|
+
"PolicyValidationResult",
|
|
169
|
+
"IAMPolicy",
|
|
170
|
+
"Statement",
|
|
171
|
+
# === ValidatorConfiguration ===
|
|
172
|
+
"ValidatorConfig",
|
|
173
|
+
"load_validator_config",
|
|
174
|
+
# === AWS utilities ===
|
|
175
|
+
"AWSServiceFetcher",
|
|
176
|
+
# === Exceptions ===
|
|
177
|
+
"IAMValidatorError",
|
|
178
|
+
"PolicyLoadError",
|
|
179
|
+
"PolicyValidationError",
|
|
180
|
+
"ConfigurationError",
|
|
181
|
+
"AWSServiceError",
|
|
182
|
+
"InvalidPolicyFormatError",
|
|
183
|
+
"UnsupportedPolicyTypeError",
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
# SDK version
|
|
187
|
+
__version__ = "0.1.0"
|