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,305 @@
1
+ """Policy Type Validation Check.
2
+
3
+ This check validates policy-type-specific requirements:
4
+ - Resource policies (RESOURCE_POLICY) must have a Principal element
5
+ - Identity policies (IDENTITY_POLICY) should not have a Principal element
6
+ - Service Control Policies (SERVICE_CONTROL_POLICY) have specific requirements
7
+ - Resource Control Policies (RESOURCE_CONTROL_POLICY) have strict requirements
8
+
9
+ This check runs automatically based on:
10
+ 1. The --policy-type flag value
11
+ 2. Auto-detection: If any statement has a Principal, provides helpful guidance
12
+ """
13
+
14
+ from iam_validator.core.models import IAMPolicy, ValidationIssue
15
+
16
+
17
+ async def execute_policy(
18
+ policy: IAMPolicy, policy_file: str, policy_type: str = "IDENTITY_POLICY", **kwargs
19
+ ) -> list[ValidationIssue]:
20
+ """Validate policy-type-specific requirements.
21
+
22
+ Args:
23
+ policy: IAM policy document
24
+ policy_file: Path to policy file
25
+ policy_type: Type of policy (IDENTITY_POLICY, RESOURCE_POLICY, SERVICE_CONTROL_POLICY)
26
+ **kwargs: Additional context (fetcher, statement index, etc.)
27
+
28
+ Returns:
29
+ List of validation issues
30
+ """
31
+ issues = []
32
+
33
+ # Check if any statement has Principal
34
+ has_any_principal = any(
35
+ stmt.principal is not None or stmt.not_principal is not None for stmt in policy.statement
36
+ )
37
+
38
+ # If policy has Principal but type is IDENTITY_POLICY (default), provide helpful info
39
+ if has_any_principal and policy_type == "IDENTITY_POLICY":
40
+ issues.append(
41
+ ValidationIssue(
42
+ severity="info",
43
+ issue_type="policy_type_hint",
44
+ message="Policy contains Principal element - this suggests it's a resource policy. "
45
+ "Use --policy-type RESOURCE_POLICY for proper validation.",
46
+ statement_index=0,
47
+ statement_sid=None,
48
+ line_number=None,
49
+ suggestion="If this is a resource policy (S3 bucket policy, SNS topic policy, etc.), "
50
+ "run validation with: iam-validator validate --path <file> --policy-type RESOURCE_POLICY",
51
+ )
52
+ )
53
+ # Don't run further checks if we're just hinting
54
+ return issues
55
+
56
+ # Resource policies MUST have Principal
57
+ if policy_type == "RESOURCE_POLICY":
58
+ for idx, statement in enumerate(policy.statement):
59
+ has_principal = statement.principal is not None or statement.not_principal is not None
60
+
61
+ if not has_principal:
62
+ issues.append(
63
+ ValidationIssue(
64
+ severity="error",
65
+ issue_type="missing_principal",
66
+ message="Resource policy statement missing required Principal element. "
67
+ "Resource-based policies (S3 bucket policies, SNS topic policies, etc.) "
68
+ "must include a Principal element to specify who can access the resource.",
69
+ statement_index=idx,
70
+ statement_sid=statement.sid,
71
+ line_number=statement.line_number,
72
+ suggestion="Add a Principal element to specify who can access this resource.\n"
73
+ "Example:\n"
74
+ "```json\n"
75
+ "{\n"
76
+ ' "Effect": "Allow",\n'
77
+ ' "Principal": {\n'
78
+ ' "AWS": "arn:aws:iam::123456789012:root"\n'
79
+ " },\n"
80
+ ' "Action": "s3:GetObject",\n'
81
+ ' "Resource": "arn:aws:s3:::bucket/*"\n'
82
+ "}\n"
83
+ "```",
84
+ )
85
+ )
86
+
87
+ # Identity policies should NOT have Principal (warning, not error)
88
+ elif policy_type == "IDENTITY_POLICY":
89
+ for idx, statement in enumerate(policy.statement):
90
+ has_principal = statement.principal is not None or statement.not_principal is not None
91
+
92
+ if has_principal:
93
+ issues.append(
94
+ ValidationIssue(
95
+ severity="warning",
96
+ issue_type="unexpected_principal",
97
+ message="Identity policy should not contain Principal element. "
98
+ "Identity-based policies (attached to IAM users, groups, or roles) "
99
+ "do not need a Principal element because the principal is implicit "
100
+ "(the entity the policy is attached to).",
101
+ statement_index=idx,
102
+ statement_sid=statement.sid,
103
+ line_number=statement.line_number,
104
+ suggestion="Remove the Principal element from this identity policy statement.\n"
105
+ "Example:\n"
106
+ "```json\n"
107
+ "{\n"
108
+ ' "Effect": "Allow",\n'
109
+ ' "Action": "s3:GetObject",\n'
110
+ ' "Resource": "arn:aws:s3:::bucket/*"\n'
111
+ "}\n"
112
+ "```",
113
+ )
114
+ )
115
+
116
+ # Service Control Policies (SCPs) should not have Principal
117
+ elif policy_type == "SERVICE_CONTROL_POLICY":
118
+ for idx, statement in enumerate(policy.statement):
119
+ has_principal = statement.principal is not None or statement.not_principal is not None
120
+
121
+ if has_principal:
122
+ issues.append(
123
+ ValidationIssue(
124
+ severity="error",
125
+ issue_type="invalid_principal",
126
+ message="Service Control Policy must not contain Principal element. "
127
+ "Service Control Policies (SCPs) in AWS Organizations do not support "
128
+ "the Principal element. They apply to all principals in the organization or OU.",
129
+ statement_index=idx,
130
+ statement_sid=statement.sid,
131
+ line_number=statement.line_number,
132
+ suggestion="Remove the Principal element from this SCP statement.\n"
133
+ "Example:\n"
134
+ "```json\n"
135
+ "{\n"
136
+ ' "Effect": "Deny",\n'
137
+ ' "Action": "ec2:*",\n'
138
+ ' "Resource": "*",\n'
139
+ ' "Condition": {\n'
140
+ ' "StringNotEquals": {\n'
141
+ ' "ec2:Region": ["us-east-1", "us-west-2"]\n'
142
+ " }\n"
143
+ " }\n"
144
+ "}\n"
145
+ "```",
146
+ )
147
+ )
148
+
149
+ # Resource Control Policies (RCPs) have very strict requirements
150
+ elif policy_type == "RESOURCE_CONTROL_POLICY":
151
+ # RCP supported services (as of 2025)
152
+ rcp_supported_services = {"s3", "sts", "sqs", "secretsmanager", "kms"}
153
+
154
+ for idx, statement in enumerate(policy.statement):
155
+ # 1. Effect MUST be Deny (only RCPFullAWSAccess can use Allow)
156
+ if statement.effect and statement.effect.lower() != "deny":
157
+ issues.append(
158
+ ValidationIssue(
159
+ severity="error",
160
+ issue_type="invalid_rcp_effect",
161
+ message="Resource Control Policy statement must have Effect: Deny. "
162
+ "For RCPs that you create, the Effect value must be 'Deny'. "
163
+ "Only the AWS-managed RCPFullAWSAccess policy can use 'Allow'.",
164
+ statement_index=idx,
165
+ statement_sid=statement.sid,
166
+ line_number=statement.line_number,
167
+ suggestion='Change the Effect to "Deny" for this RCP statement.',
168
+ )
169
+ )
170
+
171
+ # 2. Principal MUST be "*" (and only "*")
172
+ has_principal = statement.principal is not None
173
+ has_not_principal = statement.not_principal is not None
174
+
175
+ if has_not_principal:
176
+ issues.append(
177
+ ValidationIssue(
178
+ severity="error",
179
+ issue_type="invalid_rcp_not_principal",
180
+ message="Resource Control Policy must not contain NotPrincipal element. "
181
+ "RCPs only support Principal with value '*'. Use Condition elements "
182
+ "to restrict specific principals.",
183
+ statement_index=idx,
184
+ statement_sid=statement.sid,
185
+ line_number=statement.line_number,
186
+ suggestion="Remove NotPrincipal and use Principal: '*' with Condition "
187
+ "elements to restrict access.",
188
+ )
189
+ )
190
+ elif not has_principal:
191
+ issues.append(
192
+ ValidationIssue(
193
+ severity="error",
194
+ issue_type="missing_rcp_principal",
195
+ message="Resource Control Policy statement must have Principal: '*'. "
196
+ "RCPs require the Principal element with value '*'. Use Condition "
197
+ "elements to restrict specific principals.",
198
+ statement_index=idx,
199
+ statement_sid=statement.sid,
200
+ line_number=statement.line_number,
201
+ suggestion='Add Principal: "*" to this RCP statement.',
202
+ )
203
+ )
204
+ elif statement.principal != "*":
205
+ # Check if it's the dict format {"AWS": "*"} or other variations
206
+ principal_str = str(statement.principal)
207
+ if principal_str != "*":
208
+ issues.append(
209
+ ValidationIssue(
210
+ severity="error",
211
+ issue_type="invalid_rcp_principal",
212
+ message="Resource Control Policy Principal must be '*'. "
213
+ f"Found: {statement.principal}. RCPs can only specify '*' in the "
214
+ "Principal element. Use Condition elements to restrict specific principals.",
215
+ statement_index=idx,
216
+ statement_sid=statement.sid,
217
+ line_number=statement.line_number,
218
+ suggestion='Change Principal to "*" and use Condition elements to restrict access.',
219
+ )
220
+ )
221
+
222
+ # 3. Check for unsupported actions (actions not in supported services)
223
+ if statement.action:
224
+ actions = (
225
+ statement.action if isinstance(statement.action, list) else [statement.action]
226
+ )
227
+ unsupported_actions = []
228
+
229
+ for action in actions:
230
+ if isinstance(action, str):
231
+ # Check if action uses wildcard "*" alone (not allowed in customer RCPs)
232
+ if action == "*":
233
+ issues.append(
234
+ ValidationIssue(
235
+ severity="error",
236
+ issue_type="invalid_rcp_wildcard_action",
237
+ message="Resource Control Policy must not use '*' alone in Action element. "
238
+ "Customer-managed RCPs cannot use '*' as the action wildcard. "
239
+ "Use service-specific wildcards like 's3:*' instead.",
240
+ statement_index=idx,
241
+ statement_sid=statement.sid,
242
+ line_number=statement.line_number,
243
+ suggestion="Replace '*' with service-specific actions from supported "
244
+ f"services: {', '.join(sorted(rcp_supported_services))}",
245
+ )
246
+ )
247
+ else:
248
+ # Extract service from action (format: service:ActionName)
249
+ service = action.split(":")[0] if ":" in action else action
250
+ # Handle wildcards in service name
251
+ service_base = service.rstrip("*")
252
+
253
+ if service_base and service_base not in rcp_supported_services:
254
+ unsupported_actions.append(action)
255
+
256
+ if unsupported_actions:
257
+ issues.append(
258
+ ValidationIssue(
259
+ severity="error",
260
+ issue_type="unsupported_rcp_service",
261
+ message=f"Resource Control Policy contains actions from unsupported services: "
262
+ f"{', '.join(unsupported_actions)}. RCPs only support these services: "
263
+ f"{', '.join(sorted(rcp_supported_services))}",
264
+ statement_index=idx,
265
+ statement_sid=statement.sid,
266
+ line_number=statement.line_number,
267
+ suggestion=f"Use only actions from supported RCP services: "
268
+ f"{', '.join(sorted(rcp_supported_services))}",
269
+ )
270
+ )
271
+
272
+ # 4. NotAction is not supported in RCPs
273
+ if statement.not_action:
274
+ issues.append(
275
+ ValidationIssue(
276
+ severity="error",
277
+ issue_type="invalid_rcp_not_action",
278
+ message="Resource Control Policy must not contain NotAction element. "
279
+ "RCPs do not support NotAction. Use Action element instead.",
280
+ statement_index=idx,
281
+ statement_sid=statement.sid,
282
+ line_number=statement.line_number,
283
+ suggestion="Replace NotAction with Action element listing the specific "
284
+ "actions to deny.",
285
+ )
286
+ )
287
+
288
+ # 5. Resource or NotResource is required
289
+ has_resource = statement.resource is not None
290
+ has_not_resource = statement.not_resource is not None
291
+
292
+ if not has_resource and not has_not_resource:
293
+ issues.append(
294
+ ValidationIssue(
295
+ severity="error",
296
+ issue_type="missing_rcp_resource",
297
+ message="Resource Control Policy statement must have Resource or NotResource element.",
298
+ statement_index=idx,
299
+ statement_sid=statement.sid,
300
+ line_number=statement.line_number,
301
+ suggestion='Add Resource: "*" or specify specific resource ARNs.',
302
+ )
303
+ )
304
+
305
+ return issues