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,153 @@
1
+ """Set Operator Validation Check.
2
+
3
+ Validates proper usage of ForAllValues and ForAnyValue set operators in IAM policies.
4
+
5
+ Based on AWS IAM best practices:
6
+ https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-single-vs-multi-valued-context-keys.html
7
+ """
8
+
9
+ from typing import ClassVar
10
+
11
+ from iam_validator.core.aws_service import AWSServiceFetcher
12
+ from iam_validator.core.check_registry import CheckConfig, PolicyCheck
13
+ from iam_validator.core.condition_validators import (
14
+ is_multivalued_context_key,
15
+ normalize_operator,
16
+ )
17
+ from iam_validator.core.models import Statement, ValidationIssue
18
+
19
+
20
+ class SetOperatorValidationCheck(PolicyCheck):
21
+ """Check for proper usage of ForAllValues and ForAnyValue set operators."""
22
+
23
+ check_id: ClassVar[str] = "set_operator_validation"
24
+ description: ClassVar[str] = (
25
+ "Validates proper usage of ForAllValues and ForAnyValue set operators"
26
+ )
27
+ default_severity: ClassVar[str] = "error"
28
+
29
+ async def execute(
30
+ self,
31
+ statement: Statement,
32
+ statement_idx: int,
33
+ fetcher: AWSServiceFetcher,
34
+ config: CheckConfig,
35
+ ) -> list[ValidationIssue]:
36
+ """
37
+ Execute the set operator validation check.
38
+
39
+ Validates:
40
+ 1. ForAllValues/ForAnyValue not used with single-valued context keys (anti-pattern)
41
+ 2. ForAllValues with Allow effect includes Null condition check (security)
42
+ 3. ForAnyValue with Deny effect includes Null condition check (predictability)
43
+
44
+ Args:
45
+ statement: The IAM statement to check
46
+ statement_idx: Index of this statement in the policy
47
+ fetcher: AWS service fetcher (unused but required by interface)
48
+ config: Check configuration
49
+
50
+ Returns:
51
+ List of validation issues found
52
+ """
53
+ issues = []
54
+
55
+ # Only check statements with conditions
56
+ if not statement.condition:
57
+ return issues
58
+
59
+ statement_sid = statement.sid
60
+ line_number = statement.line_number
61
+ effect = statement.effect
62
+
63
+ # Track which condition keys have set operators and Null checks
64
+ set_operator_keys: dict[str, str] = {} # key -> operator prefix
65
+ null_checked_keys: set[str] = set()
66
+
67
+ # First pass: Identify set operators and Null checks
68
+ for operator, conditions in statement.condition.items():
69
+ base_operator, _operator_type, set_prefix = normalize_operator(operator)
70
+
71
+ # Track Null checks
72
+ if base_operator == "Null":
73
+ for condition_key in conditions.keys():
74
+ null_checked_keys.add(condition_key)
75
+
76
+ # Track set operators
77
+ if set_prefix in ["ForAllValues", "ForAnyValue"]:
78
+ for condition_key in conditions.keys():
79
+ set_operator_keys[condition_key] = set_prefix
80
+
81
+ # Second pass: Validate set operator usage
82
+ for operator, conditions in statement.condition.items():
83
+ base_operator, _operator_type, set_prefix = normalize_operator(operator)
84
+
85
+ if not set_prefix:
86
+ continue
87
+
88
+ # Check each condition key used with a set operator
89
+ for condition_key, _condition_values in conditions.items():
90
+ # Issue 1: Set operator used with single-valued context key (anti-pattern)
91
+ if not is_multivalued_context_key(condition_key):
92
+ issues.append(
93
+ ValidationIssue(
94
+ severity=self.get_severity(config),
95
+ message=(
96
+ f"Set operator `{set_prefix}` should not be used with single-valued "
97
+ f"condition key `{condition_key}`. This can lead to overly permissive policies. "
98
+ f"Set operators are designed for multivalued context keys like `aws:TagKeys`. "
99
+ f"See: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-single-vs-multi-valued-context-keys.html"
100
+ ),
101
+ statement_sid=statement_sid,
102
+ statement_index=statement_idx,
103
+ issue_type="set_operator_on_single_valued_key",
104
+ condition_key=condition_key,
105
+ line_number=line_number,
106
+ field_name="condition",
107
+ )
108
+ )
109
+
110
+ # Issue 2: ForAllValues with Allow effect without Null check (security risk)
111
+ if set_prefix == "ForAllValues" and effect == "Allow":
112
+ if condition_key not in null_checked_keys:
113
+ issues.append(
114
+ ValidationIssue(
115
+ severity="warning",
116
+ message=(
117
+ f"Security risk: `ForAllValues` with `Allow` effect on `{condition_key}` "
118
+ f"should include a `Null` condition check. Without it, requests with missing "
119
+ f'`{condition_key}` will be granted access. Add: `"Null": {{"{condition_key}": "false"}}`. '
120
+ f"See: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-single-vs-multi-valued-context-keys.html"
121
+ ),
122
+ statement_sid=statement_sid,
123
+ statement_index=statement_idx,
124
+ issue_type="forallvalues_allow_without_null_check",
125
+ condition_key=condition_key,
126
+ line_number=line_number,
127
+ field_name="condition",
128
+ )
129
+ )
130
+
131
+ # Issue 3: ForAnyValue with Deny effect without Null check (unpredictable)
132
+ if set_prefix == "ForAnyValue" and effect == "Deny":
133
+ if condition_key not in null_checked_keys:
134
+ issues.append(
135
+ ValidationIssue(
136
+ severity="warning",
137
+ message=(
138
+ f"Unpredictable behavior: `ForAnyValue` with `Deny` effect on `{condition_key}` "
139
+ f"should include a `Null` condition check. Without it, requests with missing "
140
+ f"`{condition_key}` will evaluate to `No match` instead of denying access. "
141
+ f'Add: `"Null": {{"{condition_key}": "false"}}`. '
142
+ f"See: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-single-vs-multi-valued-context-keys.html"
143
+ ),
144
+ statement_sid=statement_sid,
145
+ statement_index=statement_idx,
146
+ issue_type="foranyvalue_deny_without_null_check",
147
+ field_name="condition",
148
+ condition_key=condition_key,
149
+ line_number=line_number,
150
+ )
151
+ )
152
+
153
+ return issues
@@ -0,0 +1,146 @@
1
+ """Statement ID (SID) uniqueness and format check.
2
+
3
+ This check validates that Statement IDs (Sids):
4
+ 1. Are unique within a policy
5
+ 2. Follow AWS naming requirements (alphanumeric, hyphens, underscores only - no spaces)
6
+
7
+ According to AWS best practices, while not strictly required, having unique SIDs
8
+ makes it easier to reference specific statements and improves policy maintainability.
9
+
10
+ This is implemented as a policy-level check that runs once when processing the first
11
+ statement, examining all statements in the policy to find duplicates and format issues.
12
+ """
13
+
14
+ import re
15
+ from collections import Counter
16
+ from typing import ClassVar
17
+
18
+ from iam_validator.core.aws_service import AWSServiceFetcher
19
+ from iam_validator.core.check_registry import CheckConfig, PolicyCheck
20
+ from iam_validator.core.models import IAMPolicy, ValidationIssue
21
+
22
+
23
+ def _check_sid_uniqueness_impl(policy: IAMPolicy, severity: str) -> list[ValidationIssue]:
24
+ """Implementation of SID uniqueness and format checking.
25
+
26
+ Args:
27
+ policy: IAM policy to validate
28
+ severity: Severity level for issues found
29
+
30
+ Returns:
31
+ List of ValidationIssue objects for duplicate or invalid SIDs
32
+ """
33
+ issues: list[ValidationIssue] = []
34
+
35
+ # AWS SID requirements: alphanumeric characters, hyphens, and underscores only
36
+ # No spaces allowed
37
+ sid_pattern = re.compile(r"^[a-zA-Z0-9_-]+$")
38
+
39
+ # Handle policies with no statements
40
+ if not policy.statement:
41
+ return []
42
+
43
+ # Collect all SIDs (ignoring None/empty values) and check format
44
+ sids_with_indices: list[tuple[str, int]] = []
45
+ for idx, statement in enumerate(policy.statement):
46
+ if statement.sid: # Only check statements that have a SID
47
+ # Check SID format
48
+ if not sid_pattern.match(statement.sid):
49
+ # Identify the issue
50
+ if " " in statement.sid:
51
+ issue_msg = f"Statement ID `{statement.sid}` contains spaces, which are not allowed by AWS"
52
+ suggestion = (
53
+ f"Remove spaces from the SID. Example: `{statement.sid.replace(' ', '')}`"
54
+ )
55
+ else:
56
+ invalid_chars = "".join(
57
+ set(c for c in statement.sid if not c.isalnum() and c not in "_-")
58
+ )
59
+ issue_msg = f"Statement ID `{statement.sid}` contains invalid characters: `{invalid_chars}`"
60
+ suggestion = (
61
+ "SIDs must contain only alphanumeric characters, hyphens, and underscores"
62
+ )
63
+
64
+ issues.append(
65
+ ValidationIssue(
66
+ severity="error", # Invalid SID format is an error
67
+ statement_sid=statement.sid,
68
+ statement_index=idx,
69
+ issue_type="invalid_sid_format",
70
+ message=issue_msg,
71
+ suggestion=suggestion,
72
+ line_number=statement.line_number,
73
+ field_name="sid",
74
+ )
75
+ )
76
+
77
+ sids_with_indices.append((statement.sid, idx))
78
+
79
+ # Find duplicates
80
+ sid_counts = Counter(sid for sid, _ in sids_with_indices)
81
+ duplicate_sids = {sid: count for sid, count in sid_counts.items() if count > 1}
82
+
83
+ # Create issues for each duplicate SID
84
+ for duplicate_sid, count in duplicate_sids.items():
85
+ # Find all statement indices with this SID
86
+ indices = [idx for sid, idx in sids_with_indices if sid == duplicate_sid]
87
+
88
+ # Create an issue for each occurrence except the first
89
+ # (the first occurrence is "original", subsequent ones are "duplicates")
90
+ for idx in indices[1:]:
91
+ statement = policy.statement[idx]
92
+ # Convert to 1-indexed statement numbers for user-facing message
93
+ statement_numbers = ", ".join(f"#{i + 1}" for i in indices)
94
+ issues.append(
95
+ ValidationIssue(
96
+ severity=severity,
97
+ statement_sid=duplicate_sid,
98
+ statement_index=idx,
99
+ issue_type="duplicate_sid",
100
+ message=f"Statement ID `{duplicate_sid}` is used **{count} times** in this policy (found in statements `{statement_numbers}`)",
101
+ suggestion="Change this SID to a unique value. Statement IDs help identify and reference specific statements, so duplicates can cause confusion.",
102
+ line_number=statement.line_number,
103
+ field_name="sid",
104
+ )
105
+ )
106
+
107
+ return issues
108
+
109
+
110
+ class SidUniquenessCheck(PolicyCheck):
111
+ """Validates that Statement IDs (Sids) are unique within a policy.
112
+
113
+ This is a special policy-level check that examines all statements together.
114
+ It only runs once when processing the first statement to avoid duplicate work.
115
+ """
116
+
117
+ check_id: ClassVar[str] = "sid_uniqueness"
118
+ description: ClassVar[str] = (
119
+ "Validates that Statement IDs (Sids) are unique and follow AWS naming requirements (no spaces)"
120
+ )
121
+ default_severity: ClassVar[str] = "warning"
122
+
123
+ async def execute_policy(
124
+ self,
125
+ policy: IAMPolicy,
126
+ policy_file: str,
127
+ fetcher: AWSServiceFetcher,
128
+ config: CheckConfig,
129
+ **kwargs,
130
+ ) -> list[ValidationIssue]:
131
+ """Execute the SID uniqueness check on the entire policy.
132
+
133
+ This method examines all statements together to find duplicate SIDs.
134
+
135
+ Args:
136
+ policy: The complete IAM policy to validate
137
+ policy_file: Path to the policy file (unused, kept for API consistency)
138
+ fetcher: AWS service fetcher (unused for this check)
139
+ config: Configuration for this check instance
140
+
141
+ Returns:
142
+ List of ValidationIssue objects for duplicate SIDs
143
+ """
144
+ del policy_file, fetcher # Unused
145
+ severity = self.get_severity(config)
146
+ return _check_sid_uniqueness_impl(policy, severity)