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