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,390 @@
1
+ """Check Documentation Registry.
2
+
3
+ This module provides centralized documentation for all built-in checks,
4
+ including risk explanations, AWS documentation links, and remediation steps.
5
+
6
+ Used to enhance ValidationIssue objects with actionable guidance.
7
+ """
8
+
9
+ from dataclasses import dataclass, field
10
+ from typing import ClassVar
11
+
12
+
13
+ @dataclass
14
+ class CheckDocumentation:
15
+ """Documentation for a single check.
16
+
17
+ Attributes:
18
+ check_id: Unique check identifier (e.g., "wildcard_action")
19
+ risk_explanation: Why this issue is a security risk
20
+ documentation_url: Link to relevant AWS docs or runbook
21
+ remediation_steps: Step-by-step fix guidance
22
+ """
23
+
24
+ check_id: str
25
+ risk_explanation: str
26
+ documentation_url: str
27
+ remediation_steps: list[str] = field(default_factory=list)
28
+
29
+
30
+ class CheckDocumentationRegistry:
31
+ """Registry for check documentation.
32
+
33
+ Provides centralized lookup for risk explanations, documentation links,
34
+ and remediation steps for all built-in checks.
35
+ """
36
+
37
+ # AWS IAM documentation base URLs
38
+ AWS_IAM_DOCS = "https://docs.aws.amazon.com/IAM/latest/UserGuide"
39
+ AWS_IAM_REFERENCE = "https://docs.aws.amazon.com/service-authorization/latest/reference"
40
+
41
+ # Registry of all check documentation
42
+ _registry: ClassVar[dict[str, CheckDocumentation]] = {}
43
+
44
+ @classmethod
45
+ def register(cls, doc: CheckDocumentation) -> None:
46
+ """Register documentation for a check."""
47
+ cls._registry[doc.check_id] = doc
48
+
49
+ @classmethod
50
+ def get(cls, check_id: str) -> CheckDocumentation | None:
51
+ """Get documentation for a check by ID."""
52
+ return cls._registry.get(check_id)
53
+
54
+ @classmethod
55
+ def get_risk_explanation(cls, check_id: str) -> str | None:
56
+ """Get risk explanation for a check."""
57
+ doc = cls.get(check_id)
58
+ return doc.risk_explanation if doc else None
59
+
60
+ @classmethod
61
+ def get_documentation_url(cls, check_id: str) -> str | None:
62
+ """Get documentation URL for a check."""
63
+ doc = cls.get(check_id)
64
+ return doc.documentation_url if doc else None
65
+
66
+ @classmethod
67
+ def get_remediation_steps(cls, check_id: str) -> list[str] | None:
68
+ """Get remediation steps for a check."""
69
+ doc = cls.get(check_id)
70
+ return doc.remediation_steps if doc else None
71
+
72
+
73
+ # Register documentation for all built-in checks
74
+ # ==============================================
75
+
76
+ # AWS Validation Checks
77
+ # ---------------------
78
+
79
+ CheckDocumentationRegistry.register(
80
+ CheckDocumentation(
81
+ check_id="action_validation",
82
+ risk_explanation=(
83
+ "Invalid actions may silently fail to grant intended permissions, "
84
+ "or indicate a typo that could expose unintended access."
85
+ ),
86
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_REFERENCE}/reference_policies_actions-resources-contextkeys.html",
87
+ remediation_steps=[
88
+ "Verify the action name against AWS documentation for the target service",
89
+ "Use the IAM policy simulator to test your intended permissions",
90
+ "Check for common typos (e.g., 'S3' vs 's3', 'GetObjects' vs 'GetObject')",
91
+ ],
92
+ )
93
+ )
94
+
95
+ CheckDocumentationRegistry.register(
96
+ CheckDocumentation(
97
+ check_id="condition_key_validation",
98
+ risk_explanation=(
99
+ "Invalid condition keys are silently ignored by IAM, meaning your "
100
+ "intended access restrictions may not be enforced."
101
+ ),
102
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/reference_policies_condition-keys.html",
103
+ remediation_steps=[
104
+ "Verify the condition key exists for the target service",
105
+ "Check AWS documentation for the correct key name and format",
106
+ "Use global condition keys (aws:*) for cross-service restrictions",
107
+ ],
108
+ )
109
+ )
110
+
111
+ CheckDocumentationRegistry.register(
112
+ CheckDocumentation(
113
+ check_id="condition_type_mismatch",
114
+ risk_explanation=(
115
+ "Using the wrong condition operator type (e.g., StringEquals with a "
116
+ "numeric value) may cause unexpected behavior or silent failures."
117
+ ),
118
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/reference_policies_elements_condition_operators.html",
119
+ remediation_steps=[
120
+ "Match the condition operator to the key's data type",
121
+ "Use String operators for string keys, Numeric for numbers, Date for timestamps",
122
+ "Consider using IfExists variants for optional conditions",
123
+ ],
124
+ )
125
+ )
126
+
127
+ CheckDocumentationRegistry.register(
128
+ CheckDocumentation(
129
+ check_id="resource_validation",
130
+ risk_explanation=(
131
+ "Invalid resource ARNs may silently fail to match intended resources, "
132
+ "leaving permissions ineffective or overly broad."
133
+ ),
134
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_REFERENCE}/reference_policies_actions-resources-contextkeys.html",
135
+ remediation_steps=[
136
+ "Verify ARN format matches the target service's documentation",
137
+ "Ensure region and account ID are correct or use wildcards intentionally",
138
+ "Test the policy with IAM policy simulator before deployment",
139
+ ],
140
+ )
141
+ )
142
+
143
+ CheckDocumentationRegistry.register(
144
+ CheckDocumentation(
145
+ check_id="sid_uniqueness",
146
+ risk_explanation=(
147
+ "Duplicate SIDs can cause confusion and make policy auditing difficult. "
148
+ "Some AWS services may behave unexpectedly with duplicate SIDs."
149
+ ),
150
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/reference_policies_elements_sid.html",
151
+ remediation_steps=[
152
+ "Ensure each statement has a unique SID within the policy",
153
+ "Use descriptive SIDs that indicate the statement's purpose",
154
+ "Consider a naming convention like 'AllowS3ReadAccess' or 'DenyPublicAccess'",
155
+ ],
156
+ )
157
+ )
158
+
159
+ CheckDocumentationRegistry.register(
160
+ CheckDocumentation(
161
+ check_id="policy_size",
162
+ risk_explanation=(
163
+ "Policies exceeding AWS size limits cannot be attached to IAM entities. "
164
+ "Inline policies have a 2KB limit, managed policies have a 6KB limit."
165
+ ),
166
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/reference_iam-quotas.html",
167
+ remediation_steps=[
168
+ "Split large policies into multiple smaller policies",
169
+ "Use managed policies instead of inline policies for larger permissions",
170
+ "Remove redundant statements or consolidate similar actions",
171
+ "Consider using permission boundaries or SCPs for broad restrictions",
172
+ ],
173
+ )
174
+ )
175
+
176
+ CheckDocumentationRegistry.register(
177
+ CheckDocumentation(
178
+ check_id="policy_structure",
179
+ risk_explanation=(
180
+ "Malformed policy structure will cause IAM to reject the policy entirely, "
181
+ "preventing any permissions from being granted."
182
+ ),
183
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/reference_policies_grammar.html",
184
+ remediation_steps=[
185
+ "Verify the policy follows AWS IAM policy grammar",
186
+ "Ensure all required elements (Version, Statement) are present",
187
+ "Check that Effect, Action, and Resource are properly formatted",
188
+ ],
189
+ )
190
+ )
191
+
192
+ CheckDocumentationRegistry.register(
193
+ CheckDocumentation(
194
+ check_id="set_operator_validation",
195
+ risk_explanation=(
196
+ "Invalid ForAllValues/ForAnyValue operators may cause conditions to "
197
+ "behave unexpectedly, potentially granting or denying unintended access."
198
+ ),
199
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/reference_policies_multi-value-conditions.html",
200
+ remediation_steps=[
201
+ "Use ForAllValues when ALL values must match the condition",
202
+ "Use ForAnyValue when ANY value matching is sufficient",
203
+ "Consider the empty set behavior: ForAllValues returns true for empty sets",
204
+ ],
205
+ )
206
+ )
207
+
208
+ CheckDocumentationRegistry.register(
209
+ CheckDocumentation(
210
+ check_id="mfa_condition_check",
211
+ risk_explanation=(
212
+ "Sensitive operations without MFA requirements may be performed by "
213
+ "compromised credentials, increasing the blast radius of credential theft."
214
+ ),
215
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/id_credentials_mfa_configure-api-require.html",
216
+ remediation_steps=[
217
+ "Add 'aws:MultiFactorAuthPresent': 'true' condition for sensitive actions",
218
+ "Consider using 'aws:MultiFactorAuthAge' to require recent MFA",
219
+ "Ensure MFA is enforced at the identity level as well as policy level",
220
+ ],
221
+ )
222
+ )
223
+
224
+ CheckDocumentationRegistry.register(
225
+ CheckDocumentation(
226
+ check_id="principal_validation",
227
+ risk_explanation=(
228
+ "Invalid principals in resource policies may fail to grant access to "
229
+ "intended entities, or may inadvertently grant access to unintended parties."
230
+ ),
231
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/reference_policies_elements_principal.html",
232
+ remediation_steps=[
233
+ "Verify AWS account IDs and IAM ARNs are correct",
234
+ "Use specific principals instead of wildcards where possible",
235
+ "For service principals, use the canonical format (e.g., 's3.amazonaws.com')",
236
+ ],
237
+ )
238
+ )
239
+
240
+ CheckDocumentationRegistry.register(
241
+ CheckDocumentation(
242
+ check_id="policy_type_validation",
243
+ risk_explanation=(
244
+ "Using policy elements not supported by the policy type may cause "
245
+ "silent failures or unexpected behavior."
246
+ ),
247
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/access_policies.html",
248
+ remediation_steps=[
249
+ "Identity policies: Don't include Principal element",
250
+ "Resource policies: Include Principal element",
251
+ "SCPs: Use only Allow statements with specific conditions",
252
+ ],
253
+ )
254
+ )
255
+
256
+ CheckDocumentationRegistry.register(
257
+ CheckDocumentation(
258
+ check_id="action_resource_matching",
259
+ risk_explanation=(
260
+ "Actions that don't support the specified resources will silently fail, "
261
+ "resulting in permissions that don't work as intended."
262
+ ),
263
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_REFERENCE}/reference_policies_actions-resources-contextkeys.html",
264
+ remediation_steps=[
265
+ "Check AWS documentation for supported resource types per action",
266
+ "Use '*' for actions that don't support resource-level permissions",
267
+ "Split statements when actions require different resource types",
268
+ ],
269
+ )
270
+ )
271
+
272
+ CheckDocumentationRegistry.register(
273
+ CheckDocumentation(
274
+ check_id="trust_policy_validation",
275
+ risk_explanation=(
276
+ "Misconfigured trust policies can allow unauthorized principals to "
277
+ "assume roles, potentially leading to privilege escalation."
278
+ ),
279
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/id_roles_create_for-user.html",
280
+ remediation_steps=[
281
+ "Restrict Principal to specific accounts/roles/users",
282
+ "Add conditions to limit who can assume the role",
283
+ "Avoid wildcards in Principal unless absolutely necessary",
284
+ "Use ExternalId for cross-account role assumption",
285
+ ],
286
+ )
287
+ )
288
+
289
+ # Security Checks
290
+ # ---------------
291
+
292
+ CheckDocumentationRegistry.register(
293
+ CheckDocumentation(
294
+ check_id="wildcard_action",
295
+ risk_explanation=(
296
+ "Wildcard actions (e.g., 's3:*') grant all current AND future permissions "
297
+ "for a service, violating least privilege and increasing attack surface."
298
+ ),
299
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/best-practices.html#grant-least-privilege",
300
+ remediation_steps=[
301
+ "Replace wildcards with specific actions needed for the use case",
302
+ "Use action groups like 's3:Get*' for read-only access",
303
+ "Document why each action is required",
304
+ "Review and reduce permissions periodically",
305
+ ],
306
+ )
307
+ )
308
+
309
+ CheckDocumentationRegistry.register(
310
+ CheckDocumentation(
311
+ check_id="wildcard_resource",
312
+ risk_explanation=(
313
+ "Wildcard resources ('*') grant access to ALL resources of a type, "
314
+ "including resources created in the future, violating least privilege."
315
+ ),
316
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/best-practices.html#grant-least-privilege",
317
+ remediation_steps=[
318
+ "Specify exact resource ARNs when possible",
319
+ "Use resource tags and conditions for dynamic access control",
320
+ "Limit scope to specific accounts, regions, or resource prefixes",
321
+ ],
322
+ )
323
+ )
324
+
325
+ CheckDocumentationRegistry.register(
326
+ CheckDocumentation(
327
+ check_id="full_wildcard",
328
+ risk_explanation=(
329
+ "Full wildcard access ('Action': '*', 'Resource': '*') grants complete "
330
+ "control over all AWS resources, equivalent to administrator access."
331
+ ),
332
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/best-practices.html#grant-least-privilege",
333
+ remediation_steps=[
334
+ "Immediately restrict to specific services and actions needed",
335
+ "Use AWS managed policies like PowerUserAccess for broad access",
336
+ "Implement permission boundaries to limit maximum possible permissions",
337
+ "Consider using service control policies (SCPs) as guardrails",
338
+ ],
339
+ )
340
+ )
341
+
342
+ CheckDocumentationRegistry.register(
343
+ CheckDocumentation(
344
+ check_id="service_wildcard",
345
+ risk_explanation=(
346
+ "Service-level wildcards (e.g., 'iam:*') grant all permissions for "
347
+ "an entire service, including destructive and privilege escalation actions."
348
+ ),
349
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/best-practices.html#grant-least-privilege",
350
+ remediation_steps=[
351
+ "Replace with specific actions required for the use case",
352
+ "Use AWS managed policies for common patterns",
353
+ "Consider permission boundaries to limit sensitive actions",
354
+ ],
355
+ )
356
+ )
357
+
358
+ CheckDocumentationRegistry.register(
359
+ CheckDocumentation(
360
+ check_id="sensitive_action",
361
+ risk_explanation=(
362
+ "Sensitive actions (e.g., iam:*, sts:AssumeRole, kms:Decrypt) can lead "
363
+ "to privilege escalation, data exfiltration, or account compromise."
364
+ ),
365
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/best-practices.html#grant-least-privilege",
366
+ remediation_steps=[
367
+ "Add conditions to restrict when these actions can be used",
368
+ "Require MFA for sensitive operations",
369
+ "Limit to specific resources where possible",
370
+ "Implement monitoring and alerting for sensitive action usage",
371
+ ],
372
+ )
373
+ )
374
+
375
+ CheckDocumentationRegistry.register(
376
+ CheckDocumentation(
377
+ check_id="action_condition_enforcement",
378
+ risk_explanation=(
379
+ "Certain sensitive actions should always have conditions to prevent "
380
+ "misuse, such as IP restrictions, MFA requirements, or time-based access."
381
+ ),
382
+ documentation_url=f"{CheckDocumentationRegistry.AWS_IAM_DOCS}/reference_policies_elements_condition.html",
383
+ remediation_steps=[
384
+ "Add appropriate conditions based on the action type",
385
+ "Use aws:SourceIp for network-restricted actions",
386
+ "Use aws:MultiFactorAuthPresent for authentication-sensitive actions",
387
+ "Use aws:RequestedRegion to limit geographic scope",
388
+ ],
389
+ )
390
+ )
@@ -0,0 +1,258 @@
1
+ """
2
+ Condition requirement configurations for action_condition_enforcement check.
3
+
4
+ This module defines default condition requirements for sensitive actions,
5
+ making it easy to manage complex condition enforcement rules without
6
+ deeply nested YAML/dict structures.
7
+
8
+ Configuration Fields Reference:
9
+ - description: Technical description of what the requirement does (shown in output)
10
+ - example: Concrete code example showing proper condition usage
11
+ - condition_key: The IAM condition key to validate
12
+ - expected_value: (Optional) Expected value for the condition key
13
+ - severity: (Optional) Override default severity for this requirement
14
+
15
+ Field Progression: detect (condition_key) → explain (description) → demonstrate (example)
16
+
17
+ For detailed explanation of these fields and how to customize requirements,
18
+ see: docs/condition-requirements.md and docs/configuration.md#customizing-messages
19
+ """
20
+
21
+ from typing import Any, Final
22
+
23
+ # ============================================================================
24
+ # Condition Requirement Definitions
25
+ # ============================================================================
26
+
27
+ # IAM PassRole - CRITICAL: Prevent privilege escalation
28
+ IAM_PASS_ROLE_REQUIREMENT: Final[dict[str, Any]] = {
29
+ "actions": ["iam:PassRole"],
30
+ "severity": "high",
31
+ "suggestion_text": (
32
+ "This action allows passing IAM roles to AWS services, which can lead to privilege escalation. "
33
+ "Always restrict which services can receive roles:\n"
34
+ "• Use `iam:PassedToService` to limit specific AWS services (e.g., lambda.amazonaws.com, ecs-tasks.amazonaws.com)\n"
35
+ "• Consider adding `iam:AssociatedResourceArn` to restrict which resources can use the role\n"
36
+ "• Require MFA for sensitive role passing (`aws:MultiFactorAuthPresent` = `true`)"
37
+ ),
38
+ "required_conditions": [
39
+ {
40
+ "condition_key": "iam:PassedToService",
41
+ "description": (
42
+ "Restrict which AWS services can assume the passed role to prevent privilege escalation"
43
+ ),
44
+ "example": (
45
+ '"Condition": {\n'
46
+ ' "StringEquals": {\n'
47
+ ' "iam:PassedToService": [\n'
48
+ ' "lambda.amazonaws.com",\n'
49
+ ' "ecs-tasks.amazonaws.com",\n'
50
+ ' "ec2.amazonaws.com",\n'
51
+ ' "glue.amazonaws.com"\n'
52
+ " ]\n"
53
+ " }\n"
54
+ "}"
55
+ ),
56
+ },
57
+ ],
58
+ }
59
+
60
+ # S3 Organization Boundary - Prevent data exfiltration for both reads and writes
61
+ # Enforces that S3 operations only access resources within organizational boundaries
62
+ S3_ORG_BOUNDARY: Final[dict[str, Any]] = {
63
+ "actions": ["s3:GetObject", "s3:GetObjectVersion", "s3:PutObject"],
64
+ "severity": "medium",
65
+ "suggestion_text": (
66
+ "These S3 actions can read or write data. Prevent data exfiltration by ensuring operations only access organization-owned buckets:\n"
67
+ "• Use organization ID (`aws:ResourceOrgID` = `${aws:PrincipalOrgID}`)\n"
68
+ "• OR use organization paths (`aws:ResourceOrgPaths` = `${aws:PrincipalOrgPaths}`)\n"
69
+ "• OR restrict by network boundary (IP/VPC/VPCe) + same account (`aws:ResourceAccount` = `${aws:PrincipalAccount}`)"
70
+ ),
71
+ "required_conditions": {
72
+ "any_of": [
73
+ # Option 1: Restrict to organization resources (strongest)
74
+ {
75
+ "condition_key": "aws:ResourceOrgID",
76
+ "description": "Restrict S3 operations to resources within your AWS Organization",
77
+ "expected_value": "${aws:PrincipalOrgID}",
78
+ "example": (
79
+ "{\n"
80
+ ' "Condition": {\n'
81
+ ' "StringEquals": {\n'
82
+ ' "aws:ResourceOrgID": "${aws:PrincipalOrgID}"\n'
83
+ " }\n"
84
+ " }\n"
85
+ "}"
86
+ ),
87
+ },
88
+ # Option 2: Restrict to organization paths
89
+ {
90
+ "condition_key": "aws:ResourceOrgPaths",
91
+ "description": "Restrict S3 operations to resources within your AWS Organization path",
92
+ "expected_value": "${aws:PrincipalOrgPaths}",
93
+ "example": (
94
+ "{\n"
95
+ ' "Condition": {\n'
96
+ ' "StringEquals": {\n'
97
+ ' "aws:ResourceOrgPaths": "${aws:PrincipalOrgPaths}"\n'
98
+ " }\n"
99
+ " }\n"
100
+ "}"
101
+ ),
102
+ },
103
+ # Option 3: Network boundary - Source IP + same account
104
+ {
105
+ "condition_key": "aws:SourceIp",
106
+ "description": "Restrict S3 operations by source IP address and same account",
107
+ "example": (
108
+ "{\n"
109
+ ' "Condition": {\n'
110
+ ' "IpAddress": {"aws:SourceIp": "10.0.0.0/8"},\n'
111
+ ' "StringEquals": {"aws:ResourceAccount": "${aws:PrincipalAccount}"}\n'
112
+ " }\n"
113
+ "}"
114
+ ),
115
+ },
116
+ # Option 4: Network boundary - Source VPC + same account
117
+ {
118
+ "condition_key": "aws:SourceVpc",
119
+ "description": "Restrict S3 operations by source VPC and same account",
120
+ "example": (
121
+ "{\n"
122
+ ' "Condition": {\n'
123
+ ' "StringEquals": {\n'
124
+ ' "aws:SourceVpc": "vpc-12345678",\n'
125
+ ' "aws:ResourceAccount": "${aws:PrincipalAccount}"\n'
126
+ " }\n"
127
+ " }\n"
128
+ "}"
129
+ ),
130
+ },
131
+ # Option 5: Network boundary - VPC Endpoint + same account
132
+ {
133
+ "condition_key": "aws:SourceVpce",
134
+ "description": "Restrict S3 operations by VPC endpoint and same account",
135
+ "example": (
136
+ "{\n"
137
+ ' "Condition": {\n'
138
+ ' "StringEquals": {\n'
139
+ ' "aws:SourceVpce": "vpce-12345678",\n'
140
+ ' "aws:ResourceAccount": "${aws:PrincipalAccount}"\n'
141
+ " }\n"
142
+ " }\n"
143
+ "}"
144
+ ),
145
+ },
146
+ # Option 6: Minimum - at least require same account
147
+ {
148
+ "condition_key": "aws:ResourceAccount",
149
+ "description": "Restrict S3 operations to resources within the same AWS account",
150
+ "expected_value": "${aws:PrincipalAccount}",
151
+ "example": (
152
+ "{\n"
153
+ ' "Condition": {\n'
154
+ ' "StringEquals": {\n'
155
+ ' "aws:ResourceAccount": "${aws:PrincipalAccount}"\n'
156
+ " }\n"
157
+ " }\n"
158
+ "}"
159
+ ),
160
+ },
161
+ ],
162
+ },
163
+ }
164
+
165
+ # IP Restrictions - Source IP requirements
166
+ SOURCE_IP_RESTRICTIONS: Final[dict[str, Any]] = {
167
+ "action_patterns": [
168
+ "^ssm:StartSession$",
169
+ "^ssm:Run.*$",
170
+ "^rds-db:Connect$",
171
+ ],
172
+ "severity": "low",
173
+ "suggestion_text": (
174
+ "This action accesses sensitive resources or data. Restrict network access to trusted locations:\n"
175
+ "• Use `aws:SourceIp` to limit to corporate IP ranges (e.g., office networks, VPN endpoints)\n"
176
+ "• Alternative: Use `aws:SourceVpc` or `aws:SourceVpce` for VPC-based restrictions\n"
177
+ "• Consider combining with secure transport requirements\n"
178
+ "• For S3: Ensure account ownership (`aws:ResourceAccount` = `${aws:PrincipalAccount}`)"
179
+ ),
180
+ "required_conditions": [
181
+ {
182
+ "condition_key": "aws:SourceIp",
183
+ "description": "Restrict access to corporate IP ranges",
184
+ "example": (
185
+ "{\n"
186
+ ' "Condition": {\n'
187
+ ' "IpAddress": {\n'
188
+ ' "aws:SourceIp": [\n'
189
+ ' "10.0.0.0/8",\n'
190
+ ' "172.16.0.0/12"\n'
191
+ " ]\n"
192
+ " },\n"
193
+ ' "Bool": {"aws:SecureTransport": "true"},\n'
194
+ ' "StringEquals": {"aws:ResourceAccount": "${aws:PrincipalAccount}"}\n'
195
+ " }\n"
196
+ "}"
197
+ ),
198
+ },
199
+ ],
200
+ }
201
+
202
+ # S3 Secure Transport - Never allow insecure transport
203
+ S3_SECURE_TRANSPORT: Final[dict[str, Any]] = {
204
+ "actions": ["s3:GetObject", "s3:PutObject"],
205
+ "severity": "critical",
206
+ "suggestion_text": (
207
+ "CRITICAL: This S3 action must enforce encrypted connections. Unencrypted HTTP connections expose data in transit:\n"
208
+ "• Set `aws:SecureTransport` to `true` to enforce HTTPS/TLS\n"
209
+ "• NEVER set `aws:SecureTransport` to `false` (this explicitly allows unencrypted connections)\n"
210
+ "• Combine with other controls (IP restrictions, account boundaries) for defense in depth"
211
+ ),
212
+ "required_conditions": {
213
+ "none_of": [
214
+ {
215
+ "condition_key": "aws:SecureTransport",
216
+ "expected_value": False,
217
+ "description": "Never allow insecure transport to be explicitly permitted",
218
+ "example": (
219
+ "# Set this condition to true to enforce secure transport or remove it entirely\n"
220
+ "{\n"
221
+ ' "Condition": {\n'
222
+ ' "Bool": {\n'
223
+ ' "aws:SecureTransport": "true"\n'
224
+ " }\n"
225
+ " }\n"
226
+ "}"
227
+ ),
228
+ },
229
+ ],
230
+ },
231
+ }
232
+
233
+ # Prevent overly permissive IP ranges
234
+ PREVENT_PUBLIC_IP: Final[dict[str, Any]] = {
235
+ "action_patterns": ["^s3:.*"],
236
+ "severity": "high",
237
+ "required_conditions": {
238
+ "none_of": [
239
+ {
240
+ "condition_key": "aws:SourceIp",
241
+ "expected_value": "0.0.0.0/0",
242
+ "description": "Do not allow access from any IP address",
243
+ },
244
+ ],
245
+ },
246
+ }
247
+
248
+ # ============================================================================
249
+ # Condition Requirements
250
+ # ============================================================================
251
+
252
+ CONDITION_REQUIREMENTS: Final[list[dict[str, Any]]] = [
253
+ IAM_PASS_ROLE_REQUIREMENT,
254
+ S3_ORG_BOUNDARY, # Unified S3 read/write organization boundary enforcement
255
+ SOURCE_IP_RESTRICTIONS,
256
+ S3_SECURE_TRANSPORT,
257
+ PREVENT_PUBLIC_IP,
258
+ ]