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.
- iam_policy_validator-1.14.0.dist-info/METADATA +782 -0
- iam_policy_validator-1.14.0.dist-info/RECORD +106 -0
- iam_policy_validator-1.14.0.dist-info/WHEEL +4 -0
- iam_policy_validator-1.14.0.dist-info/entry_points.txt +2 -0
- iam_policy_validator-1.14.0.dist-info/licenses/LICENSE +21 -0
- iam_validator/__init__.py +27 -0
- iam_validator/__main__.py +11 -0
- iam_validator/__version__.py +9 -0
- iam_validator/checks/__init__.py +45 -0
- iam_validator/checks/action_condition_enforcement.py +1442 -0
- iam_validator/checks/action_resource_matching.py +472 -0
- iam_validator/checks/action_validation.py +67 -0
- iam_validator/checks/condition_key_validation.py +88 -0
- iam_validator/checks/condition_type_mismatch.py +257 -0
- iam_validator/checks/full_wildcard.py +62 -0
- iam_validator/checks/mfa_condition_check.py +105 -0
- iam_validator/checks/policy_size.py +114 -0
- iam_validator/checks/policy_structure.py +556 -0
- iam_validator/checks/policy_type_validation.py +331 -0
- iam_validator/checks/principal_validation.py +708 -0
- iam_validator/checks/resource_validation.py +135 -0
- iam_validator/checks/sensitive_action.py +438 -0
- iam_validator/checks/service_wildcard.py +98 -0
- iam_validator/checks/set_operator_validation.py +153 -0
- iam_validator/checks/sid_uniqueness.py +146 -0
- iam_validator/checks/trust_policy_validation.py +509 -0
- iam_validator/checks/utils/__init__.py +17 -0
- iam_validator/checks/utils/action_parser.py +149 -0
- iam_validator/checks/utils/policy_level_checks.py +190 -0
- iam_validator/checks/utils/sensitive_action_matcher.py +293 -0
- iam_validator/checks/utils/wildcard_expansion.py +86 -0
- iam_validator/checks/wildcard_action.py +58 -0
- iam_validator/checks/wildcard_resource.py +374 -0
- iam_validator/commands/__init__.py +31 -0
- iam_validator/commands/analyze.py +549 -0
- iam_validator/commands/base.py +48 -0
- iam_validator/commands/cache.py +393 -0
- iam_validator/commands/completion.py +471 -0
- iam_validator/commands/download_services.py +255 -0
- iam_validator/commands/post_to_pr.py +86 -0
- iam_validator/commands/query.py +485 -0
- iam_validator/commands/validate.py +830 -0
- iam_validator/core/__init__.py +13 -0
- iam_validator/core/access_analyzer.py +671 -0
- iam_validator/core/access_analyzer_report.py +640 -0
- iam_validator/core/aws_fetcher.py +29 -0
- iam_validator/core/aws_service/__init__.py +21 -0
- iam_validator/core/aws_service/cache.py +108 -0
- iam_validator/core/aws_service/client.py +205 -0
- iam_validator/core/aws_service/fetcher.py +641 -0
- iam_validator/core/aws_service/parsers.py +149 -0
- iam_validator/core/aws_service/patterns.py +51 -0
- iam_validator/core/aws_service/storage.py +291 -0
- iam_validator/core/aws_service/validators.py +380 -0
- iam_validator/core/check_registry.py +679 -0
- iam_validator/core/cli.py +134 -0
- iam_validator/core/codeowners.py +245 -0
- iam_validator/core/condition_validators.py +626 -0
- iam_validator/core/config/__init__.py +81 -0
- iam_validator/core/config/aws_api.py +35 -0
- iam_validator/core/config/aws_global_conditions.py +160 -0
- iam_validator/core/config/category_suggestions.py +181 -0
- iam_validator/core/config/check_documentation.py +390 -0
- iam_validator/core/config/condition_requirements.py +258 -0
- iam_validator/core/config/config_loader.py +670 -0
- iam_validator/core/config/defaults.py +739 -0
- iam_validator/core/config/principal_requirements.py +421 -0
- iam_validator/core/config/sensitive_actions.py +672 -0
- iam_validator/core/config/service_principals.py +132 -0
- iam_validator/core/config/wildcards.py +127 -0
- iam_validator/core/constants.py +149 -0
- iam_validator/core/diff_parser.py +325 -0
- iam_validator/core/finding_fingerprint.py +131 -0
- iam_validator/core/formatters/__init__.py +27 -0
- iam_validator/core/formatters/base.py +147 -0
- iam_validator/core/formatters/console.py +68 -0
- iam_validator/core/formatters/csv.py +171 -0
- iam_validator/core/formatters/enhanced.py +481 -0
- iam_validator/core/formatters/html.py +672 -0
- iam_validator/core/formatters/json.py +33 -0
- iam_validator/core/formatters/markdown.py +64 -0
- iam_validator/core/formatters/sarif.py +251 -0
- iam_validator/core/ignore_patterns.py +297 -0
- iam_validator/core/ignore_processor.py +309 -0
- iam_validator/core/ignored_findings.py +400 -0
- iam_validator/core/label_manager.py +197 -0
- iam_validator/core/models.py +404 -0
- iam_validator/core/policy_checks.py +220 -0
- iam_validator/core/policy_loader.py +785 -0
- iam_validator/core/pr_commenter.py +780 -0
- iam_validator/core/report.py +942 -0
- iam_validator/integrations/__init__.py +28 -0
- iam_validator/integrations/github_integration.py +1821 -0
- iam_validator/integrations/ms_teams.py +442 -0
- iam_validator/sdk/__init__.py +220 -0
- iam_validator/sdk/arn_matching.py +382 -0
- iam_validator/sdk/context.py +222 -0
- iam_validator/sdk/exceptions.py +48 -0
- iam_validator/sdk/helpers.py +177 -0
- iam_validator/sdk/policy_utils.py +451 -0
- iam_validator/sdk/query_utils.py +454 -0
- iam_validator/sdk/shortcuts.py +283 -0
- iam_validator/utils/__init__.py +35 -0
- iam_validator/utils/cache.py +105 -0
- iam_validator/utils/regex.py +205 -0
- 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)
|