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,81 @@
1
+ """
2
+ Core configuration modules for IAM Policy Validator.
3
+
4
+ This package contains default configuration data used by validators, organized into
5
+ logical modules for better maintainability and performance.
6
+
7
+ All configuration is defined as Python code (not YAML/JSON) for:
8
+ - Faster loading (no parsing overhead)
9
+ - Better PyPI packaging (no data files to manage)
10
+ - Type hints and IDE support
11
+ - Compiled to .pyc for even faster imports
12
+
13
+ Performance benefits:
14
+ - 5-10x faster than YAML parsing
15
+ - Zero runtime parsing overhead
16
+ - Lazy loading support
17
+ - O(1) frozenset lookups
18
+ """
19
+
20
+ from iam_validator.core.config.aws_api import (
21
+ AWS_SERVICE_REFERENCE_BASE_URL,
22
+ get_service_reference_url,
23
+ )
24
+ from iam_validator.core.config.aws_global_conditions import (
25
+ AWS_GLOBAL_CONDITION_KEYS,
26
+ AWSGlobalConditions,
27
+ get_global_conditions,
28
+ )
29
+ from iam_validator.core.config.condition_requirements import CONDITION_REQUIREMENTS
30
+ from iam_validator.core.config.defaults import DEFAULT_CONFIG
31
+ from iam_validator.core.config.principal_requirements import (
32
+ ALL_PRINCIPAL_REQUIREMENTS,
33
+ DEFAULT_ENABLED_REQUIREMENTS,
34
+ get_all_principal_requirement_names,
35
+ get_default_principal_requirements,
36
+ get_principal_requirement,
37
+ get_principal_requirements_by_names,
38
+ get_principal_requirements_by_severity,
39
+ )
40
+ from iam_validator.core.config.sensitive_actions import (
41
+ DEFAULT_SENSITIVE_ACTIONS,
42
+ get_sensitive_actions,
43
+ )
44
+ from iam_validator.core.config.service_principals import DEFAULT_SERVICE_PRINCIPALS
45
+ from iam_validator.core.config.wildcards import (
46
+ DEFAULT_ALLOWED_WILDCARDS,
47
+ DEFAULT_SERVICE_WILDCARDS,
48
+ )
49
+
50
+ # NOTE: ConfigLoader is NOT imported here to avoid circular imports
51
+ # Import it directly from iam_validator.core.config.config_loader when needed
52
+
53
+ __all__ = [
54
+ # Default configuration
55
+ "DEFAULT_CONFIG",
56
+ # AWS API endpoints
57
+ "AWS_SERVICE_REFERENCE_BASE_URL",
58
+ "get_service_reference_url",
59
+ # AWS Global Conditions
60
+ "AWS_GLOBAL_CONDITION_KEYS",
61
+ "AWSGlobalConditions",
62
+ "get_global_conditions",
63
+ # Sensitive actions
64
+ "DEFAULT_SENSITIVE_ACTIONS",
65
+ "get_sensitive_actions",
66
+ # Condition requirements (for actions)
67
+ "CONDITION_REQUIREMENTS",
68
+ # Principal requirements (for principals)
69
+ "ALL_PRINCIPAL_REQUIREMENTS",
70
+ "DEFAULT_ENABLED_REQUIREMENTS",
71
+ "get_default_principal_requirements",
72
+ "get_principal_requirement",
73
+ "get_all_principal_requirement_names",
74
+ "get_principal_requirements_by_names",
75
+ "get_principal_requirements_by_severity",
76
+ # Wildcards
77
+ "DEFAULT_ALLOWED_WILDCARDS",
78
+ "DEFAULT_SERVICE_WILDCARDS",
79
+ # Service principals
80
+ "DEFAULT_SERVICE_PRINCIPALS",
81
+ ]
@@ -0,0 +1,35 @@
1
+ """
2
+ AWS API configuration constants.
3
+
4
+ This module centralizes AWS API endpoints and related configuration
5
+ used throughout the IAM Policy Validator.
6
+ """
7
+
8
+ # AWS Service Reference API base URL
9
+ # This is the official AWS service reference that provides action, resource, and condition key metadata
10
+ AWS_SERVICE_REFERENCE_BASE_URL = "https://servicereference.us-east-1.amazonaws.com/"
11
+
12
+ # Alternative endpoints for different regions (currently not used, but available for future expansion)
13
+ AWS_SERVICE_REFERENCE_ENDPOINTS = {
14
+ "us-east-1": "https://servicereference.us-east-1.amazonaws.com/",
15
+ # Add other regional endpoints if they become available
16
+ }
17
+
18
+
19
+ def get_service_reference_url(region: str = "us-east-1") -> str:
20
+ """
21
+ Get the AWS Service Reference API URL for a specific region.
22
+
23
+ Args:
24
+ region: AWS region (default: us-east-1)
25
+
26
+ Returns:
27
+ The service reference base URL for the specified region
28
+
29
+ Example:
30
+ >>> get_service_reference_url()
31
+ 'https://servicereference.us-east-1.amazonaws.com/'
32
+ >>> get_service_reference_url("us-east-1")
33
+ 'https://servicereference.us-east-1.amazonaws.com/'
34
+ """
35
+ return AWS_SERVICE_REFERENCE_ENDPOINTS.get(region, AWS_SERVICE_REFERENCE_BASE_URL)
@@ -0,0 +1,160 @@
1
+ """
2
+ AWS Global Condition Keys Management.
3
+
4
+ Provides access to the list of valid AWS global condition keys
5
+ that can be used across all AWS services.
6
+
7
+ Reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-keys.html
8
+ Last updated: 2025-01-17
9
+ """
10
+
11
+ import re
12
+ from typing import Any
13
+
14
+ # AWS Global Condition Keys with Type Information
15
+ # These condition keys are available for use in IAM policies across all AWS services
16
+ # Format: {key: type} where type is one of: String, ARN, Bool, Date, IPAddress, Numeric
17
+ AWS_GLOBAL_CONDITION_KEYS = {
18
+ # Properties of the Principal
19
+ "aws:PrincipalArn": "ARN", # ARN of the principal making the request
20
+ "aws:PrincipalAccount": "String", # Account to which the requesting principal belongs
21
+ "aws:PrincipalOrgPaths": "String", # AWS Organizations path for the principal
22
+ "aws:PrincipalOrgID": "String", # Organization identifier of the principal
23
+ "aws:PrincipalIsAWSService": "Bool", # Checks if call is made directly by AWS service principal
24
+ "aws:PrincipalServiceName": "String", # Service principal name making the request
25
+ "aws:PrincipalServiceNamesList": "String", # List of all service principal names
26
+ "aws:PrincipalType": "String", # Type of principal making the request
27
+ "aws:userid": "String", # Principal identifier of the requester
28
+ "aws:username": "String", # User name of the requester
29
+ # Properties of a Role Session
30
+ "aws:AssumedRoot": "Bool", # Checks if request used AssumeRoot for privileged access
31
+ "aws:FederatedProvider": "String", # Principal's issuing identity provider
32
+ "aws:TokenIssueTime": "Date", # When temporary security credentials were issued
33
+ "aws:MultiFactorAuthAge": "Numeric", # Seconds since MFA authorization
34
+ "aws:MultiFactorAuthPresent": "Bool", # Whether MFA was used for temporary credentials
35
+ "aws:ChatbotSourceArn": "ARN", # Source chat configuration ARN
36
+ "aws:Ec2InstanceSourceVpc": "String", # VPC where EC2 IAM role credentials were delivered
37
+ "aws:Ec2InstanceSourcePrivateIPv4": "IPAddress", # Private IPv4 of EC2 instance
38
+ "aws:SourceIdentity": "String", # Source identity set when assuming a role
39
+ "ec2:RoleDelivery": "Numeric", # Instance metadata service version
40
+ # Network Properties
41
+ "aws:SourceIp": "IPAddress", # Requester's IP address (IPv4/IPv6)
42
+ "aws:SourceVpc": "String", # VPC through which request travels
43
+ "aws:SourceVpce": "String", # VPC endpoint identifier
44
+ "aws:VpceAccount": "String", # AWS account owning the VPC endpoint
45
+ "aws:VpceOrgID": "String", # Organization ID of VPC endpoint owner
46
+ "aws:VpceOrgPaths": "String", # AWS Organizations path of VPC endpoint
47
+ "aws:VpcSourceIp": "IPAddress", # IP address from VPC endpoint request
48
+ # Resource Properties
49
+ "aws:ResourceAccount": "String", # Resource owner's AWS account ID
50
+ "aws:ResourceOrgID": "String", # Organization ID of resource owner
51
+ "aws:ResourceOrgPaths": "String", # AWS Organizations path of resource
52
+ # Request Properties
53
+ "aws:CurrentTime": "Date", # Current date and time
54
+ "aws:EpochTime": "Date", # Request timestamp in epoch format (also accepts Numeric)
55
+ "aws:referer": "String", # HTTP referer header value (note: lowercase 'r')
56
+ "aws:Referer": "String", # HTTP referer header value (alternate capitalization)
57
+ "aws:RequestedRegion": "String", # AWS Region for the request
58
+ "aws:TagKeys": "String", # Tag keys present in request
59
+ "aws:SecureTransport": "Bool", # Whether HTTPS was used
60
+ "aws:SourceAccount": "String", # Account making the request
61
+ "aws:SourceArn": "ARN", # ARN of request source
62
+ "aws:SourceOrgID": "String", # Organization ID of request source
63
+ "aws:SourceOrgPaths": "String", # Organization paths of request source
64
+ "aws:UserAgent": "String", # HTTP user agent string
65
+ # Cross-Service Keys
66
+ "aws:CalledVia": "String", # Services called in request chain
67
+ "aws:CalledViaFirst": "String", # First service in call chain
68
+ "aws:CalledViaLast": "String", # Last service in call chain
69
+ "aws:ViaAWSService": "Bool", # Whether AWS service made the request
70
+ }
71
+
72
+ # Patterns that should be recognized (wildcards and tag-based keys)
73
+ # These allow things like aws:RequestTag/Department or aws:PrincipalTag/Environment
74
+ AWS_CONDITION_KEY_PATTERNS = [
75
+ {
76
+ "pattern": r"^aws:RequestTag/[a-zA-Z0-9+\-=._:/@]+$",
77
+ "description": "Tag keys in the request (for tag-based access control)",
78
+ },
79
+ {
80
+ "pattern": r"^aws:ResourceTag/[a-zA-Z0-9+\-=._:/@]+$",
81
+ "description": "Tags on the resource being accessed",
82
+ },
83
+ {
84
+ "pattern": r"^aws:PrincipalTag/[a-zA-Z0-9+\-=._:/@]+$",
85
+ "description": "Tags attached to the principal making the request",
86
+ },
87
+ ]
88
+
89
+
90
+ class AWSGlobalConditions:
91
+ """Manages AWS global condition keys."""
92
+
93
+ def __init__(self):
94
+ """Initialize with global condition keys."""
95
+ self._global_keys: dict[str, str] = AWS_GLOBAL_CONDITION_KEYS.copy()
96
+ self._patterns: list[dict[str, Any]] = AWS_CONDITION_KEY_PATTERNS.copy()
97
+
98
+ def is_valid_global_key(self, condition_key: str) -> bool:
99
+ """
100
+ Check if a condition key is a valid AWS global condition key.
101
+
102
+ Args:
103
+ condition_key: The condition key to validate (e.g., "aws:SourceIp")
104
+
105
+ Returns:
106
+ True if valid global condition key, False otherwise
107
+ """
108
+ # Check exact matches first
109
+ if condition_key in self._global_keys:
110
+ return True
111
+
112
+ # Check patterns (for tags and wildcards)
113
+ for pattern_config in self._patterns:
114
+ pattern = pattern_config["pattern"]
115
+ if re.match(pattern, condition_key):
116
+ return True
117
+
118
+ return False
119
+
120
+ def get_key_type(self, condition_key: str) -> str | None:
121
+ """
122
+ Get the expected type for a global condition key.
123
+
124
+ Args:
125
+ condition_key: The condition key (e.g., "aws:SourceIp")
126
+
127
+ Returns:
128
+ Type string (String, ARN, Bool, Date, IPAddress, Numeric) or None if not found
129
+ """
130
+ # Check exact matches
131
+ if condition_key in self._global_keys:
132
+ return self._global_keys[condition_key]
133
+
134
+ # Check patterns - all tag-based keys are String type
135
+ for pattern_config in self._patterns:
136
+ pattern = pattern_config["pattern"]
137
+ if re.match(pattern, condition_key):
138
+ return "String"
139
+
140
+ return None
141
+
142
+ def get_all_keys(self) -> dict[str, str]:
143
+ """Get all explicit global condition keys with their types."""
144
+ return self._global_keys.copy()
145
+
146
+ def get_patterns(self) -> list[dict[str, Any]]:
147
+ """Get all condition key patterns."""
148
+ return self._patterns.copy()
149
+
150
+
151
+ # Singleton instance
152
+ _global_conditions_instance = None
153
+
154
+
155
+ def get_global_conditions() -> AWSGlobalConditions:
156
+ """Get singleton instance of AWSGlobalConditions."""
157
+ global _global_conditions_instance
158
+ if _global_conditions_instance is None:
159
+ _global_conditions_instance = AWSGlobalConditions()
160
+ return _global_conditions_instance
@@ -0,0 +1,104 @@
1
+ """
2
+ Category-specific suggestions for sensitive actions.
3
+
4
+ This module defines ABAC-focused (Attribute-Based Access Control) suggestions
5
+ and examples for each sensitive action category. These provide actionable
6
+ guidance for securing sensitive AWS actions.
7
+
8
+ ABAC is the recommended approach as it:
9
+ - Scales across all AWS services
10
+ - Reduces policy maintenance overhead
11
+ - Provides fine-grained access control
12
+ - Enables self-service resource management
13
+ """
14
+
15
+ from typing import Any, Final
16
+
17
+ # ============================================================================
18
+ # ABAC-Focused Category Suggestions
19
+ # ============================================================================
20
+ # Each category provides tailored guidance based on the security risk profile
21
+ # ============================================================================
22
+
23
+ DEFAULT_CATEGORY_SUGGESTIONS: Final[dict[str, dict[str, Any]]] = {
24
+ "credential_exposure": {
25
+ "suggestion": (
26
+ "This action can expose credentials or secrets. Use ABAC to restrict access:\n"
27
+ "• Match principal tags to resource tags (aws:PrincipalTag/team = aws:ResourceTag/team)\n"
28
+ "• Require MFA (aws:MultiFactorAuthPresent = true)\n"
29
+ "• Restrict to trusted networks (aws:SourceIp)\n"
30
+ "• Limit to business hours (aws:CurrentTime)"
31
+ ),
32
+ "example": (
33
+ '"Condition": {\n'
34
+ ' "StringEquals": {\n'
35
+ ' "aws:PrincipalTag/owner": "${aws:ResourceTag/owner}"\n'
36
+ " },\n"
37
+ ' "Bool": {"aws:MultiFactorAuthPresent": "true"}\n'
38
+ "}"
39
+ ),
40
+ },
41
+ "data_access": {
42
+ "suggestion": (
43
+ "This action retrieves sensitive data. Use ABAC to control data access:\n"
44
+ "• Match principal tags to resource tags (aws:PrincipalTag/data-access = aws:ResourceTag/data-classification)\n"
45
+ "• Limit by department/team (aws:PrincipalTag/department = aws:ResourceTag/owner)\n"
46
+ "• Restrict data exfiltration (aws:SourceIp or aws:SourceVpc)\n"
47
+ "• Consider data classification levels"
48
+ ),
49
+ "example": (
50
+ '"Condition": {\n'
51
+ ' "StringEquals": {\n'
52
+ ' "aws:PrincipalTag/owner": "${aws:ResourceTag/owner}",\n'
53
+ ' "aws:ResourceTag/data-classification": ["public", "internal"]\n'
54
+ " }\n"
55
+ "}"
56
+ ),
57
+ },
58
+ "priv_esc": {
59
+ "suggestion": (
60
+ "This action enables privilege escalation. Use ABAC + strong controls:\n"
61
+ "• Require specific role tags (aws:PrincipalTag/role = admin)\n"
62
+ "• Enforce permissions boundary (iam:PermissionsBoundary)\n"
63
+ "• Require MFA (aws:MultiFactorAuthPresent = true) - CRITICAL\n"
64
+ "• Limit request tags (aws:RequestTag/environment != production)"
65
+ ),
66
+ "example": (
67
+ '"Condition": {\n'
68
+ ' "StringEquals": {\n'
69
+ ' "aws:PrincipalTag/role": "security-admin",\n'
70
+ ' "iam:PermissionsBoundary": "arn:aws:iam::*:policy/MaxPermissions"\n'
71
+ " },\n"
72
+ ' "Bool": {"aws:MultiFactorAuthPresent": "true"}\n'
73
+ "}"
74
+ ),
75
+ },
76
+ "resource_exposure": {
77
+ "suggestion": (
78
+ "This action modifies resource policies. Use ABAC to prevent unauthorized changes:\n"
79
+ "• Match principal tags to resource tags (aws:PrincipalTag/team = aws:ResourceTag/managed-by)\n"
80
+ "• Restrict by environment (aws:ResourceTag/environment = development)\n"
81
+ "• Prevent external access (aws:PrincipalOrgID)\n"
82
+ "• Require approval tags (aws:RequestTag/change-approved = true)"
83
+ ),
84
+ "example": (
85
+ '"Condition": {\n'
86
+ ' "StringEquals": {\n'
87
+ ' "aws:PrincipalTag/owner": "${aws:ResourceTag/managed-by}",\n'
88
+ ' "aws:ResourceTag/environment": "${aws:PrincipalTag/environment}",\n'
89
+ ' "aws:PrincipalOrgID": "o-xxxxxxxxxx"\n'
90
+ " }\n"
91
+ "}"
92
+ ),
93
+ },
94
+ }
95
+
96
+
97
+ def get_category_suggestions() -> dict[str, dict[str, Any]]:
98
+ """
99
+ Get default category suggestions.
100
+
101
+ Returns:
102
+ Dictionary mapping category IDs to suggestion/example dictionaries
103
+ """
104
+ return DEFAULT_CATEGORY_SUGGESTIONS.copy()
@@ -0,0 +1,155 @@
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
+ "required_conditions": [
32
+ {
33
+ "condition_key": "iam:PassedToService",
34
+ "description": (
35
+ "Restrict which AWS services can assume the passed role to prevent privilege escalation"
36
+ ),
37
+ "example": (
38
+ '"Condition": {\n'
39
+ ' "StringEquals": {\n'
40
+ ' "iam:PassedToService": [\n'
41
+ ' "lambda.amazonaws.com",\n'
42
+ ' "ecs-tasks.amazonaws.com",\n'
43
+ ' "ec2.amazonaws.com",\n'
44
+ ' "glue.amazonaws.com"\n'
45
+ " ]\n"
46
+ " }\n"
47
+ "}"
48
+ ),
49
+ },
50
+ ],
51
+ }
52
+
53
+ # S3 Write Operations - Require organization ID
54
+ S3_WRITE_ORG_ID: Final[dict[str, Any]] = {
55
+ "actions": ["s3:PutObject"],
56
+ "severity": "medium",
57
+ "required_conditions": [
58
+ {
59
+ "condition_key": "aws:ResourceOrgId",
60
+ "description": (
61
+ "Require aws:ResourceAccount, aws:ResourceOrgID or aws:ResourceOrgPaths condition(s) for S3 write actions to enforce organization-level access control"
62
+ ),
63
+ "example": (
64
+ "{\n"
65
+ ' "Condition": {\n'
66
+ ' "StringEquals": {\n'
67
+ ' "aws:ResourceOrgId": "${aws:PrincipalOrgID}"\n'
68
+ " }\n"
69
+ " }\n"
70
+ "}"
71
+ ),
72
+ },
73
+ ],
74
+ }
75
+
76
+ # IP Restrictions - Source IP requirements
77
+ SOURCE_IP_RESTRICTIONS: Final[dict[str, Any]] = {
78
+ "action_patterns": [
79
+ "^ssm:StartSession$",
80
+ "^ssm:Run.*$",
81
+ "^s3:GetObject$",
82
+ "^rds-db:Connect$",
83
+ ],
84
+ "severity": "low",
85
+ "required_conditions": [
86
+ {
87
+ "condition_key": "aws:SourceIp",
88
+ "description": "Restrict access to corporate IP ranges",
89
+ "example": (
90
+ "{\n"
91
+ ' "Condition": {\n'
92
+ ' "IpAddress": {\n'
93
+ ' "aws:SourceIp": [\n'
94
+ ' "10.0.0.0/8",\n'
95
+ ' "172.16.0.0/12"\n'
96
+ " ]\n"
97
+ " }\n"
98
+ " }\n"
99
+ "}"
100
+ ),
101
+ },
102
+ ],
103
+ }
104
+
105
+ # S3 Secure Transport - Never allow insecure transport
106
+ S3_SECURE_TRANSPORT: Final[dict[str, Any]] = {
107
+ "actions": ["s3:GetObject", "s3:PutObject"],
108
+ "severity": "critical",
109
+ "required_conditions": {
110
+ "none_of": [
111
+ {
112
+ "condition_key": "aws:SecureTransport",
113
+ "expected_value": False,
114
+ "description": "Never allow insecure transport to be explicitly permitted",
115
+ "example": (
116
+ "# Set this condition to true to enforce secure transport or remove it entirely\n"
117
+ "{\n"
118
+ ' "Condition": {\n'
119
+ ' "Bool": {\n'
120
+ ' "aws:SecureTransport": "true"\n'
121
+ " }\n"
122
+ " }\n"
123
+ "}"
124
+ ),
125
+ },
126
+ ],
127
+ },
128
+ }
129
+
130
+ # Prevent overly permissive IP ranges
131
+ PREVENT_PUBLIC_IP: Final[dict[str, Any]] = {
132
+ "action_patterns": ["^s3:.*"],
133
+ "severity": "high",
134
+ "required_conditions": {
135
+ "none_of": [
136
+ {
137
+ "condition_key": "aws:SourceIp",
138
+ "expected_value": "0.0.0.0/0",
139
+ "description": "Do not allow access from any IP address",
140
+ },
141
+ ],
142
+ },
143
+ }
144
+
145
+ # ============================================================================
146
+ # Condition Requirements
147
+ # ============================================================================
148
+
149
+ CONDITION_REQUIREMENTS: Final[list[dict[str, Any]]] = [
150
+ IAM_PASS_ROLE_REQUIREMENT,
151
+ S3_WRITE_ORG_ID,
152
+ SOURCE_IP_RESTRICTIONS,
153
+ S3_SECURE_TRANSPORT,
154
+ PREVENT_PUBLIC_IP,
155
+ ]