iam-policy-validator 1.4.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 (56) hide show
  1. iam_policy_validator-1.4.0.dist-info/METADATA +1022 -0
  2. iam_policy_validator-1.4.0.dist-info/RECORD +56 -0
  3. iam_policy_validator-1.4.0.dist-info/WHEEL +4 -0
  4. iam_policy_validator-1.4.0.dist-info/entry_points.txt +2 -0
  5. iam_policy_validator-1.4.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 +27 -0
  10. iam_validator/checks/action_condition_enforcement.py +727 -0
  11. iam_validator/checks/action_resource_constraint.py +151 -0
  12. iam_validator/checks/action_validation.py +72 -0
  13. iam_validator/checks/condition_key_validation.py +70 -0
  14. iam_validator/checks/policy_size.py +151 -0
  15. iam_validator/checks/policy_type_validation.py +299 -0
  16. iam_validator/checks/principal_validation.py +282 -0
  17. iam_validator/checks/resource_validation.py +108 -0
  18. iam_validator/checks/security_best_practices.py +536 -0
  19. iam_validator/checks/sid_uniqueness.py +170 -0
  20. iam_validator/checks/utils/__init__.py +1 -0
  21. iam_validator/checks/utils/policy_level_checks.py +143 -0
  22. iam_validator/checks/utils/sensitive_action_matcher.py +252 -0
  23. iam_validator/checks/utils/wildcard_expansion.py +87 -0
  24. iam_validator/commands/__init__.py +25 -0
  25. iam_validator/commands/analyze.py +434 -0
  26. iam_validator/commands/base.py +48 -0
  27. iam_validator/commands/cache.py +392 -0
  28. iam_validator/commands/download_services.py +260 -0
  29. iam_validator/commands/post_to_pr.py +86 -0
  30. iam_validator/commands/validate.py +539 -0
  31. iam_validator/core/__init__.py +14 -0
  32. iam_validator/core/access_analyzer.py +666 -0
  33. iam_validator/core/access_analyzer_report.py +643 -0
  34. iam_validator/core/aws_fetcher.py +880 -0
  35. iam_validator/core/aws_global_conditions.py +137 -0
  36. iam_validator/core/check_registry.py +469 -0
  37. iam_validator/core/cli.py +134 -0
  38. iam_validator/core/config_loader.py +452 -0
  39. iam_validator/core/defaults.py +393 -0
  40. iam_validator/core/formatters/__init__.py +27 -0
  41. iam_validator/core/formatters/base.py +147 -0
  42. iam_validator/core/formatters/console.py +59 -0
  43. iam_validator/core/formatters/csv.py +170 -0
  44. iam_validator/core/formatters/enhanced.py +434 -0
  45. iam_validator/core/formatters/html.py +672 -0
  46. iam_validator/core/formatters/json.py +33 -0
  47. iam_validator/core/formatters/markdown.py +63 -0
  48. iam_validator/core/formatters/sarif.py +187 -0
  49. iam_validator/core/models.py +298 -0
  50. iam_validator/core/policy_checks.py +656 -0
  51. iam_validator/core/policy_loader.py +396 -0
  52. iam_validator/core/pr_commenter.py +338 -0
  53. iam_validator/core/report.py +859 -0
  54. iam_validator/integrations/__init__.py +28 -0
  55. iam_validator/integrations/github_integration.py +795 -0
  56. iam_validator/integrations/ms_teams.py +442 -0
@@ -0,0 +1,151 @@
1
+ """Action resource constraint check - validates resource constraints for actions.
2
+
3
+ This check ensures that:
4
+ - Actions WITHOUT required resource types (empty or missing Resources field in AWS API)
5
+ MUST use Resource: "*" because they are account-level operations
6
+
7
+ Examples of actions without required resources:
8
+ - s3:ListAllMyBuckets (lists all buckets in account)
9
+ - iam:ListRoles (lists all roles in account)
10
+ - ec2:DescribeInstances (describes instances across all regions)
11
+
12
+ These actions cannot target specific resources because they operate at the account level.
13
+ """
14
+
15
+ from iam_validator.core.aws_fetcher import AWSServiceFetcher
16
+ from iam_validator.core.check_registry import CheckConfig, PolicyCheck
17
+ from iam_validator.core.models import Statement, ValidationIssue
18
+
19
+
20
+ class ActionResourceConstraintCheck(PolicyCheck):
21
+ """Validates resource constraints based on action requirements.
22
+ This check ensures that actions without required resource types use Resource: "*".
23
+
24
+ Examples of such actions include s3:ListAllMyBuckets, iam:ListRoles, etc.
25
+ """
26
+
27
+ @property
28
+ def check_id(self) -> str:
29
+ return "action_resource_constraint"
30
+
31
+ @property
32
+ def description(self) -> str:
33
+ return "Validates that actions without required resource types use Resource: '*'"
34
+
35
+ @property
36
+ def default_severity(self) -> str:
37
+ return "error"
38
+
39
+ async def execute(
40
+ self,
41
+ statement: Statement,
42
+ statement_idx: int,
43
+ fetcher: AWSServiceFetcher,
44
+ config: CheckConfig,
45
+ ) -> list[ValidationIssue]:
46
+ """Execute action resource constraint validation on a statement."""
47
+ issues = []
48
+
49
+ # Only check Allow statements
50
+ if statement.effect != "Allow":
51
+ return issues
52
+
53
+ # Get actions and resources from statement
54
+ actions = statement.get_actions()
55
+ resources = statement.get_resources()
56
+ statement_sid = statement.sid
57
+ line_number = statement.line_number
58
+
59
+ # Skip if no actions or wildcard action
60
+ if not actions or "*" in actions:
61
+ return issues
62
+
63
+ # Skip if already using wildcard resource (this is correct for these actions)
64
+ if "*" in resources:
65
+ return issues
66
+
67
+ # Check each action for resource requirements
68
+ actions_without_required_resources = []
69
+
70
+ for action in actions:
71
+ # Skip wildcard actions
72
+ if "*" in action:
73
+ continue
74
+
75
+ try:
76
+ # Parse action to get service and action name
77
+ service_prefix, action_name = fetcher.parse_action(action)
78
+
79
+ # Fetch service detail to check resource requirements
80
+ service_detail = await fetcher.fetch_service_by_name(service_prefix)
81
+
82
+ # Check if action exists
83
+ if action_name not in service_detail.actions:
84
+ # Action doesn't exist - skip (will be caught by action_validation_check)
85
+ continue
86
+
87
+ action_detail = service_detail.actions[action_name]
88
+
89
+ # Check if action has NO required resources (empty or missing Resources field)
90
+ has_no_required_resources = (
91
+ not action_detail.resources or len(action_detail.resources) == 0
92
+ )
93
+
94
+ if has_no_required_resources:
95
+ actions_without_required_resources.append(action)
96
+
97
+ except ValueError:
98
+ # Invalid action format - skip (will be caught by action_validation_check)
99
+ continue
100
+ except Exception:
101
+ # Service not found or other error - skip
102
+ continue
103
+
104
+ # If we found actions without required resources, report the issue
105
+ if actions_without_required_resources:
106
+ # Get a sample of the resources to show in error message
107
+ resource_sample = resources[:3] if len(resources) > 3 else resources
108
+ resource_display = ", ".join(f'"{r}"' for r in resource_sample)
109
+ if len(resources) > 3:
110
+ resource_display += f", ... ({len(resources) - 3} more)"
111
+
112
+ # Format action list
113
+ action_list = ", ".join(f'"{a}"' for a in actions_without_required_resources)
114
+
115
+ # Determine message based on how many actions are affected
116
+ if len(actions_without_required_resources) == 1:
117
+ message = (
118
+ f"Action {action_list} does not operate on specific resources "
119
+ f'and requires Resource: "*"'
120
+ )
121
+ suggestion = (
122
+ f"Action {action_list} is an account-level operation that cannot target "
123
+ 'specific resources. Move this action to a separate statement with Resource: "*", '
124
+ "and keep resource-specific actions in another statement with their specific ARNs"
125
+ )
126
+ else:
127
+ message = (
128
+ f"Actions {action_list} do not operate on specific resources "
129
+ f'and require Resource: "*"'
130
+ )
131
+ suggestion = (
132
+ "These actions are account-level operations that cannot target "
133
+ 'specific resources. Move these actions to a dedicated statement with Resource: "*", '
134
+ "and keep resource-specific actions in separate statements with their specific ARNs"
135
+ )
136
+
137
+ issues.append(
138
+ ValidationIssue(
139
+ severity=self.get_severity(config),
140
+ statement_sid=statement_sid,
141
+ statement_index=statement_idx,
142
+ issue_type="invalid_resource_constraint",
143
+ message=message,
144
+ action=action_list,
145
+ resource=resource_display,
146
+ suggestion=suggestion,
147
+ line_number=line_number,
148
+ )
149
+ )
150
+
151
+ return issues
@@ -0,0 +1,72 @@
1
+ """Action validation check - validates IAM actions against AWS service definitions.
2
+
3
+ This check ensures that all actions specified in IAM policies are valid actions
4
+ defined by AWS services. It helps identify typos or deprecated actions that may
5
+ lead to unintended access permissions.
6
+
7
+ This check is not necessary when using Access Analyzer, as it performs similar
8
+ validations. However, it can be useful in environments where Access Analyzer is
9
+ not available or for pre-deployment policy validation to catch errors early.
10
+ """
11
+
12
+ from iam_validator.core.aws_fetcher import AWSServiceFetcher
13
+ from iam_validator.core.check_registry import CheckConfig, PolicyCheck
14
+ from iam_validator.core.models import Statement, ValidationIssue
15
+
16
+
17
+ class ActionValidationCheck(PolicyCheck):
18
+ """Validates that IAM actions exist in AWS services."""
19
+
20
+ @property
21
+ def check_id(self) -> str:
22
+ return "action_validation"
23
+
24
+ @property
25
+ def description(self) -> str:
26
+ return "Validates that actions exist in AWS service definitions"
27
+
28
+ @property
29
+ def default_severity(self) -> str:
30
+ return "error"
31
+
32
+ async def execute(
33
+ self,
34
+ statement: Statement,
35
+ statement_idx: int,
36
+ fetcher: AWSServiceFetcher,
37
+ config: CheckConfig,
38
+ ) -> list[ValidationIssue]:
39
+ """Execute action validation on a statement.
40
+
41
+ This check ONLY validates that actions exist in AWS service definitions.
42
+ Wildcard security checks are handled by security_best_practices_check.
43
+ """
44
+ issues = []
45
+
46
+ # Get actions from statement
47
+ actions = statement.get_actions()
48
+ statement_sid = statement.sid
49
+ line_number = statement.line_number
50
+
51
+ for action in actions:
52
+ # Skip wildcard actions - they're handled by security_best_practices_check
53
+ if action == "*" or "*" in action:
54
+ continue
55
+
56
+ # Validate the action exists in AWS
57
+ is_valid, error_msg, _is_wildcard = await fetcher.validate_action(action)
58
+
59
+ if not is_valid:
60
+ issues.append(
61
+ ValidationIssue(
62
+ severity=self.get_severity(config),
63
+ statement_sid=statement_sid,
64
+ statement_index=statement_idx,
65
+ issue_type="invalid_action",
66
+ message=error_msg or f"Invalid action: {action}",
67
+ action=action,
68
+ line_number=line_number,
69
+ )
70
+ )
71
+
72
+ return issues
@@ -0,0 +1,70 @@
1
+ """Condition key validation check - validates condition keys against AWS definitions."""
2
+
3
+ from iam_validator.core.aws_fetcher import AWSServiceFetcher
4
+ from iam_validator.core.check_registry import CheckConfig, PolicyCheck
5
+ from iam_validator.core.models import Statement, ValidationIssue
6
+
7
+
8
+ class ConditionKeyValidationCheck(PolicyCheck):
9
+ """Validates condition keys against AWS service definitions and global keys."""
10
+
11
+ @property
12
+ def check_id(self) -> str:
13
+ return "condition_key_validation"
14
+
15
+ @property
16
+ def description(self) -> str:
17
+ return "Validates condition keys against AWS service definitions"
18
+
19
+ @property
20
+ def default_severity(self) -> str:
21
+ return "error" # Invalid condition keys are IAM policy errors
22
+
23
+ async def execute(
24
+ self,
25
+ statement: Statement,
26
+ statement_idx: int,
27
+ fetcher: AWSServiceFetcher,
28
+ config: CheckConfig,
29
+ ) -> list[ValidationIssue]:
30
+ """Execute condition key validation on a statement."""
31
+ issues = []
32
+
33
+ # Get conditions from statement
34
+ if not statement.condition:
35
+ return issues
36
+
37
+ statement_sid = statement.sid
38
+ line_number = statement.line_number
39
+ actions = statement.get_actions()
40
+
41
+ # Extract all condition keys from all condition operators
42
+ for operator, conditions in statement.condition.items():
43
+ for condition_key in conditions.keys():
44
+ # Validate this condition key against each action in the statement
45
+ for action in actions:
46
+ # Skip wildcard actions
47
+ if action == "*":
48
+ continue
49
+
50
+ is_valid, error_msg = await fetcher.validate_condition_key(
51
+ action, condition_key
52
+ )
53
+
54
+ if not is_valid:
55
+ issues.append(
56
+ ValidationIssue(
57
+ severity=self.get_severity(config),
58
+ statement_sid=statement_sid,
59
+ statement_index=statement_idx,
60
+ issue_type="invalid_condition_key",
61
+ message=error_msg or f"Invalid condition key: {condition_key}",
62
+ action=action,
63
+ condition_key=condition_key,
64
+ line_number=line_number,
65
+ )
66
+ )
67
+ # Only report once per condition key (not per action)
68
+ break
69
+
70
+ return issues
@@ -0,0 +1,151 @@
1
+ """Policy size validation check.
2
+
3
+ This check validates that IAM policies don't exceed AWS's maximum size limits.
4
+ AWS enforces different size limits based on policy type:
5
+ - Managed policies: 6,144 characters maximum
6
+ - Inline policies for users: 2,048 characters maximum
7
+ - Inline policies for groups: 5,120 characters maximum
8
+ - Inline policies for roles: 10,240 characters maximum
9
+
10
+ Note: AWS does not count whitespace when calculating policy size.
11
+ """
12
+
13
+ import json
14
+ import re
15
+ from typing import TYPE_CHECKING
16
+
17
+ from iam_validator.core.aws_fetcher import AWSServiceFetcher
18
+ from iam_validator.core.check_registry import CheckConfig, PolicyCheck
19
+ from iam_validator.core.models import Statement, ValidationIssue
20
+
21
+ if TYPE_CHECKING:
22
+ from iam_validator.core.models import IAMPolicy
23
+
24
+
25
+ class PolicySizeCheck(PolicyCheck):
26
+ """Validates that IAM policies don't exceed AWS size limits."""
27
+
28
+ # AWS IAM policy size limits (in characters, excluding whitespace)
29
+ DEFAULT_LIMITS = {
30
+ "managed": 6144,
31
+ "inline_user": 2048,
32
+ "inline_group": 5120,
33
+ "inline_role": 10240,
34
+ }
35
+
36
+ @property
37
+ def check_id(self) -> str:
38
+ return "policy_size"
39
+
40
+ @property
41
+ def description(self) -> str:
42
+ return "Validates that IAM policies don't exceed AWS size limits"
43
+
44
+ @property
45
+ def default_severity(self) -> str:
46
+ return "error"
47
+
48
+ async def execute(
49
+ self,
50
+ statement: Statement,
51
+ statement_idx: int,
52
+ fetcher: AWSServiceFetcher,
53
+ config: CheckConfig,
54
+ ) -> list[ValidationIssue]:
55
+ """Execute the policy size check at statement level.
56
+
57
+ This is a policy-level check, so statement-level execution returns empty.
58
+ The actual check runs in execute_policy() which has access to the full policy.
59
+
60
+ Args:
61
+ statement: The IAM policy statement (unused)
62
+ statement_idx: Index of the statement in the policy (unused)
63
+ fetcher: AWS service fetcher (unused for this check)
64
+ config: Configuration for this check instance (unused)
65
+
66
+ Returns:
67
+ Empty list (actual check runs in execute_policy())
68
+ """
69
+ del statement, statement_idx, fetcher, config # Unused
70
+ # This is a policy-level check - execution happens in execute_policy()
71
+ return []
72
+
73
+ async def execute_policy(
74
+ self,
75
+ policy: "IAMPolicy",
76
+ policy_file: str,
77
+ fetcher: AWSServiceFetcher,
78
+ config: CheckConfig,
79
+ **kwargs,
80
+ ) -> list[ValidationIssue]:
81
+ """Execute the policy size check on the entire policy.
82
+
83
+ This method calculates the policy size (excluding whitespace) and validates
84
+ it against AWS limits based on the configured policy type.
85
+
86
+ Args:
87
+ policy: The complete IAM policy to validate
88
+ policy_file: Path to the policy file (for context/reporting)
89
+ fetcher: AWS service fetcher (unused for this check)
90
+ config: Configuration for this check instance
91
+
92
+ Returns:
93
+ List of ValidationIssue objects if policy exceeds size limits
94
+ """
95
+ del policy_file, fetcher # Unused
96
+ issues = []
97
+
98
+ # Get the policy type from config (default to managed)
99
+ policy_type = config.config.get("policy_type", "managed")
100
+
101
+ # Get custom limits if provided in config, otherwise use defaults
102
+ size_limits = config.config.get("size_limits", self.DEFAULT_LIMITS.copy())
103
+
104
+ # Determine the applicable limit
105
+ limit_key = policy_type
106
+ if limit_key not in size_limits:
107
+ # If custom policy_type not found, default to managed
108
+ limit_key = "managed"
109
+
110
+ max_size = size_limits[limit_key]
111
+
112
+ # Convert policy to JSON and calculate size (excluding whitespace)
113
+ policy_json = policy.model_dump(by_alias=True, exclude_none=True)
114
+ policy_string = json.dumps(policy_json, separators=(",", ":"))
115
+
116
+ # Remove all whitespace as AWS doesn't count it
117
+ policy_size = len(re.sub(r"\s+", "", policy_string))
118
+
119
+ # Check if policy exceeds the limit
120
+ if policy_size > max_size:
121
+ severity = self.get_severity(config)
122
+
123
+ # Calculate percentage over limit
124
+ percentage_over = ((policy_size - max_size) / max_size) * 100
125
+
126
+ # Determine policy type description
127
+ policy_type_desc = {
128
+ "managed": "managed policy",
129
+ "inline_user": "inline policy for users",
130
+ "inline_group": "inline policy for groups",
131
+ "inline_role": "inline policy for roles",
132
+ }.get(policy_type, policy_type)
133
+
134
+ issues.append(
135
+ ValidationIssue(
136
+ severity=severity,
137
+ statement_sid=None, # Policy-level issue
138
+ statement_index=-1, # -1 indicates policy-level issue
139
+ issue_type="policy_size_exceeded",
140
+ message=f"Policy size ({policy_size:,} characters) exceeds AWS limit for {policy_type_desc} ({max_size:,} characters)",
141
+ suggestion=f"The policy is {policy_size - max_size:,} characters over the limit ({percentage_over:.1f}% too large). Consider:\n"
142
+ f" 1. Splitting the policy into multiple smaller policies\n"
143
+ f" 2. Using more concise action/resource patterns with wildcards\n"
144
+ f" 3. Removing unnecessary statements or conditions\n"
145
+ f" 4. For inline policies, consider using managed policies instead\n"
146
+ f"\nNote: AWS does not count whitespace in the size calculation.",
147
+ line_number=None,
148
+ )
149
+ )
150
+
151
+ return issues