iam-policy-validator 1.7.2__py3-none-any.whl → 1.9.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 (56) hide show
  1. {iam_policy_validator-1.7.2.dist-info → iam_policy_validator-1.9.0.dist-info}/METADATA +127 -6
  2. iam_policy_validator-1.9.0.dist-info/RECORD +95 -0
  3. iam_validator/__init__.py +1 -1
  4. iam_validator/__version__.py +1 -1
  5. iam_validator/checks/__init__.py +5 -3
  6. iam_validator/checks/action_condition_enforcement.py +559 -207
  7. iam_validator/checks/action_resource_matching.py +12 -15
  8. iam_validator/checks/action_validation.py +7 -13
  9. iam_validator/checks/condition_key_validation.py +7 -13
  10. iam_validator/checks/condition_type_mismatch.py +15 -22
  11. iam_validator/checks/full_wildcard.py +9 -13
  12. iam_validator/checks/mfa_condition_check.py +8 -17
  13. iam_validator/checks/policy_size.py +6 -39
  14. iam_validator/checks/policy_structure.py +547 -0
  15. iam_validator/checks/policy_type_validation.py +61 -46
  16. iam_validator/checks/principal_validation.py +71 -148
  17. iam_validator/checks/resource_validation.py +13 -20
  18. iam_validator/checks/sensitive_action.py +15 -18
  19. iam_validator/checks/service_wildcard.py +8 -14
  20. iam_validator/checks/set_operator_validation.py +21 -28
  21. iam_validator/checks/sid_uniqueness.py +16 -42
  22. iam_validator/checks/trust_policy_validation.py +506 -0
  23. iam_validator/checks/utils/sensitive_action_matcher.py +26 -26
  24. iam_validator/checks/utils/wildcard_expansion.py +2 -2
  25. iam_validator/checks/wildcard_action.py +9 -13
  26. iam_validator/checks/wildcard_resource.py +9 -13
  27. iam_validator/commands/cache.py +4 -3
  28. iam_validator/commands/validate.py +15 -9
  29. iam_validator/core/__init__.py +2 -3
  30. iam_validator/core/access_analyzer.py +1 -1
  31. iam_validator/core/access_analyzer_report.py +2 -2
  32. iam_validator/core/aws_fetcher.py +24 -1028
  33. iam_validator/core/aws_service/__init__.py +21 -0
  34. iam_validator/core/aws_service/cache.py +108 -0
  35. iam_validator/core/aws_service/client.py +205 -0
  36. iam_validator/core/aws_service/fetcher.py +612 -0
  37. iam_validator/core/aws_service/parsers.py +149 -0
  38. iam_validator/core/aws_service/patterns.py +51 -0
  39. iam_validator/core/aws_service/storage.py +291 -0
  40. iam_validator/core/aws_service/validators.py +379 -0
  41. iam_validator/core/check_registry.py +165 -93
  42. iam_validator/core/config/condition_requirements.py +69 -17
  43. iam_validator/core/config/defaults.py +58 -52
  44. iam_validator/core/config/service_principals.py +40 -3
  45. iam_validator/core/constants.py +17 -0
  46. iam_validator/core/ignore_patterns.py +297 -0
  47. iam_validator/core/models.py +15 -5
  48. iam_validator/core/policy_checks.py +38 -475
  49. iam_validator/core/policy_loader.py +27 -4
  50. iam_validator/sdk/__init__.py +1 -1
  51. iam_validator/sdk/context.py +1 -1
  52. iam_validator/sdk/helpers.py +1 -1
  53. iam_policy_validator-1.7.2.dist-info/RECORD +0 -84
  54. {iam_policy_validator-1.7.2.dist-info → iam_policy_validator-1.9.0.dist-info}/WHEEL +0 -0
  55. {iam_policy_validator-1.7.2.dist-info → iam_policy_validator-1.9.0.dist-info}/entry_points.txt +0 -0
  56. {iam_policy_validator-1.7.2.dist-info → iam_policy_validator-1.9.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,9 @@
1
1
  """Resource validation check - validates ARN formats."""
2
2
 
3
3
  import re
4
+ from typing import ClassVar
4
5
 
5
- from iam_validator.core.aws_fetcher import AWSServiceFetcher
6
+ from iam_validator.core.aws_service import AWSServiceFetcher
6
7
  from iam_validator.core.check_registry import CheckConfig, PolicyCheck
7
8
  from iam_validator.core.constants import DEFAULT_ARN_VALIDATION_PATTERN, MAX_ARN_LENGTH
8
9
  from iam_validator.core.models import Statement, ValidationIssue
@@ -15,17 +16,9 @@ from iam_validator.sdk.arn_matching import (
15
16
  class ResourceValidationCheck(PolicyCheck):
16
17
  """Validates ARN format for resources."""
17
18
 
18
- @property
19
- def check_id(self) -> str:
20
- return "resource_validation"
21
-
22
- @property
23
- def description(self) -> str:
24
- return "Validates ARN format for resources"
25
-
26
- @property
27
- def default_severity(self) -> str:
28
- return "error"
19
+ check_id: ClassVar[str] = "resource_validation"
20
+ description: ClassVar[str] = "Validates ARN format for resources"
21
+ default_severity: ClassVar[str] = "error"
29
22
 
30
23
  async def execute(
31
24
  self,
@@ -75,7 +68,7 @@ class ResourceValidationCheck(PolicyCheck):
75
68
  issue_type="invalid_resource",
76
69
  message=f"Resource ARN exceeds maximum length ({len(resource)} > {MAX_ARN_LENGTH}): {resource[:100]}...",
77
70
  resource=resource[:100] + "...",
78
- suggestion="ARN is too long and may be invalid",
71
+ suggestion="`ARN` is too long and may be invalid",
79
72
  line_number=line_number,
80
73
  )
81
74
  )
@@ -101,9 +94,9 @@ class ResourceValidationCheck(PolicyCheck):
101
94
  statement_sid=statement_sid,
102
95
  statement_index=statement_idx,
103
96
  issue_type="invalid_resource",
104
- message=f"Invalid ARN format even after normalizing template variables: {resource}",
97
+ message=f"Invalid `ARN` format even after normalizing template variables: `{resource}`",
105
98
  resource=resource,
106
- suggestion="ARN should follow format: arn:partition:service:region:account-id:resource (template variables like ${aws_account_id} are supported)",
99
+ suggestion="`ARN` should follow format: `arn:partition:service:region:account-id:resource` (template variables like `${aws_account_id}` are supported)",
107
100
  line_number=line_number,
108
101
  )
109
102
  )
@@ -114,13 +107,13 @@ class ResourceValidationCheck(PolicyCheck):
114
107
  statement_sid=statement_sid,
115
108
  statement_index=statement_idx,
116
109
  issue_type="invalid_resource",
117
- message=f"Invalid ARN format: {resource}",
110
+ message=f"Invalid `ARN` format: `{resource}`",
118
111
  resource=resource,
119
- suggestion="ARN should follow format: arn:partition:service:region:account-id:resource",
112
+ suggestion="`ARN` should follow format: `arn:partition:service:region:account-id:resource`",
120
113
  line_number=line_number,
121
114
  )
122
115
  )
123
- except Exception:
116
+ except Exception: # pylint: disable=broad-exception-caught
124
117
  # If regex matching fails (shouldn't happen with length check), treat as invalid
125
118
  issues.append(
126
119
  ValidationIssue(
@@ -128,9 +121,9 @@ class ResourceValidationCheck(PolicyCheck):
128
121
  statement_sid=statement_sid,
129
122
  statement_index=statement_idx,
130
123
  issue_type="invalid_resource",
131
- message=f"Could not validate ARN format: {resource}",
124
+ message=f"Could not validate `ARN` format: `{resource}`",
132
125
  resource=resource,
133
- suggestion="ARN validation failed - may contain unexpected characters",
126
+ suggestion="`ARN` validation failed - may contain unexpected characters",
134
127
  line_number=line_number,
135
128
  )
136
129
  )
@@ -1,6 +1,6 @@
1
1
  """Sensitive action check - detects sensitive actions without IAM conditions."""
2
2
 
3
- from typing import TYPE_CHECKING
3
+ from typing import TYPE_CHECKING, ClassVar
4
4
 
5
5
  from iam_validator.checks.utils.policy_level_checks import check_policy_level_actions
6
6
  from iam_validator.checks.utils.sensitive_action_matcher import (
@@ -8,7 +8,7 @@ from iam_validator.checks.utils.sensitive_action_matcher import (
8
8
  check_sensitive_actions,
9
9
  )
10
10
  from iam_validator.checks.utils.wildcard_expansion import expand_wildcard_actions
11
- from iam_validator.core.aws_fetcher import AWSServiceFetcher
11
+ from iam_validator.core.aws_service import AWSServiceFetcher
12
12
  from iam_validator.core.check_registry import CheckConfig, PolicyCheck
13
13
  from iam_validator.core.config.sensitive_actions import get_category_for_action
14
14
  from iam_validator.core.models import Statement, ValidationIssue
@@ -20,17 +20,9 @@ if TYPE_CHECKING:
20
20
  class SensitiveActionCheck(PolicyCheck):
21
21
  """Checks for sensitive actions without IAM conditions to limit their use."""
22
22
 
23
- @property
24
- def check_id(self) -> str:
25
- return "sensitive_action"
26
-
27
- @property
28
- def description(self) -> str:
29
- return "Checks for sensitive actions without conditions"
30
-
31
- @property
32
- def default_severity(self) -> str:
33
- return "medium"
23
+ check_id: ClassVar[str] = "sensitive_action"
24
+ description: ClassVar[str] = "Checks for sensitive actions without conditions"
25
+ default_severity: ClassVar[str] = "medium"
34
26
 
35
27
  def _get_severity_for_action(self, action: str, config: CheckConfig) -> str:
36
28
  """
@@ -85,9 +77,10 @@ class SensitiveActionCheck(PolicyCheck):
85
77
  # Generic ABAC fallback for uncategorized actions
86
78
  return (
87
79
  "Add IAM conditions to limit when this action can be used. Use ABAC for scalability:\n"
88
- "• Match principal tags to resource tags (aws:PrincipalTag/<tag-name> = aws:ResourceTag/<tag-name>)\n"
89
- "• Require MFA (aws:MultiFactorAuthPresent = true)\n"
90
- "• Restrict by IP (aws:SourceIp) or VPC (aws:SourceVpc)",
80
+ "• Match principal tags to resource tags (`aws:PrincipalTag/<tag-name>` = `aws:ResourceTag/<tag-name>`)\n"
81
+ "• Match organization principal tags to resource tags (`aws:PrincipalOrgID` = `aws:ResourceOrgID`)\n"
82
+ "• Require MFA (`aws:MultiFactorAuthPresent` = `true`)\n"
83
+ "• Restrict by IP (`aws:SourceIp`) or VPC (`aws:SourceVpc`)",
91
84
  '"Condition": {\n'
92
85
  ' "StringEquals": {\n'
93
86
  ' "aws:PrincipalTag/owner": "${aws:ResourceTag/owner}"\n'
@@ -125,14 +118,14 @@ class SensitiveActionCheck(PolicyCheck):
125
118
  if len(matched_actions) == 1:
126
119
  message_template = config.config.get(
127
120
  "message_single",
128
- "Sensitive action '{action}' should have conditions to limit when it can be used",
121
+ "Sensitive action `{action}` should have conditions to limit when it can be used",
129
122
  )
130
123
  message = message_template.format(action=matched_actions[0])
131
124
  else:
132
125
  action_list = "', '".join(matched_actions)
133
126
  message_template = config.config.get(
134
127
  "message_multiple",
135
- "Sensitive actions '{actions}' should have conditions to limit when they can be used",
128
+ "Sensitive actions `{actions}` should have conditions to limit when they can be used",
136
129
  )
137
130
  message = message_template.format(actions=action_list)
138
131
 
@@ -192,6 +185,10 @@ class SensitiveActionCheck(PolicyCheck):
192
185
  del policy_file, fetcher # Not used in current implementation
193
186
  issues = []
194
187
 
188
+ # Handle policies with no statements
189
+ if not policy.statement:
190
+ return []
191
+
195
192
  # Collect all actions from all Allow statements across the entire policy
196
193
  all_actions: set[str] = set()
197
194
  statement_map: dict[
@@ -1,6 +1,8 @@
1
1
  """Service wildcard check - detects service-level wildcards like 'iam:*', 's3:*'."""
2
2
 
3
- from iam_validator.core.aws_fetcher import AWSServiceFetcher
3
+ from typing import ClassVar
4
+
5
+ from iam_validator.core.aws_service import AWSServiceFetcher
4
6
  from iam_validator.core.check_registry import CheckConfig, PolicyCheck
5
7
  from iam_validator.core.models import Statement, ValidationIssue
6
8
 
@@ -8,17 +10,9 @@ from iam_validator.core.models import Statement, ValidationIssue
8
10
  class ServiceWildcardCheck(PolicyCheck):
9
11
  """Checks for service-level wildcards (e.g., 'iam:*', 's3:*') which grant all permissions for a service."""
10
12
 
11
- @property
12
- def check_id(self) -> str:
13
- return "service_wildcard"
14
-
15
- @property
16
- def description(self) -> str:
17
- return "Checks for service-level wildcards (e.g., 'iam:*', 's3:*')"
18
-
19
- @property
20
- def default_severity(self) -> str:
21
- return "high"
13
+ check_id: ClassVar[str] = "service_wildcard"
14
+ description: ClassVar[str] = "Checks for service-level wildcards (e.g., 'iam:*', 's3:*')"
15
+ default_severity: ClassVar[str] = "high"
22
16
 
23
17
  async def execute(
24
18
  self,
@@ -51,11 +45,11 @@ class ServiceWildcardCheck(PolicyCheck):
51
45
  # Get message template and replace placeholders
52
46
  message_template = config.config.get(
53
47
  "message",
54
- "Service-level wildcard '{action}' grants all permissions for {service} service",
48
+ "Service-level wildcard `{action}` grants all permissions for `{service}` service",
55
49
  )
56
50
  suggestion_template = config.config.get(
57
51
  "suggestion",
58
- "Consider specifying explicit actions instead of '{action}'. If you need multiple actions, list them individually or use more specific wildcards like '{service}:Get*' or '{service}:List*'.",
52
+ "Consider specifying explicit actions instead of `{action}`. If you need multiple actions, list them individually or use more specific wildcards like `{service}:Get*` or `{service}:List*`.",
59
53
  )
60
54
  example_template = config.config.get("example", "")
61
55
 
@@ -6,7 +6,9 @@ Based on AWS IAM best practices:
6
6
  https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-single-vs-multi-valued-context-keys.html
7
7
  """
8
8
 
9
- from iam_validator.core.aws_fetcher import AWSServiceFetcher
9
+ from typing import ClassVar
10
+
11
+ from iam_validator.core.aws_service import AWSServiceFetcher
10
12
  from iam_validator.core.check_registry import CheckConfig, PolicyCheck
11
13
  from iam_validator.core.condition_validators import (
12
14
  is_multivalued_context_key,
@@ -18,20 +20,11 @@ from iam_validator.core.models import Statement, ValidationIssue
18
20
  class SetOperatorValidationCheck(PolicyCheck):
19
21
  """Check for proper usage of ForAllValues and ForAnyValue set operators."""
20
22
 
21
- @property
22
- def check_id(self) -> str:
23
- """Unique identifier for this check."""
24
- return "set_operator_validation"
25
-
26
- @property
27
- def description(self) -> str:
28
- """Description of what this check does."""
29
- return "Validates proper usage of ForAllValues and ForAnyValue set operators"
30
-
31
- @property
32
- def default_severity(self) -> str:
33
- """Default severity level for issues found by this check."""
34
- return "error"
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"
35
28
 
36
29
  async def execute(
37
30
  self,
@@ -73,7 +66,7 @@ class SetOperatorValidationCheck(PolicyCheck):
73
66
 
74
67
  # First pass: Identify set operators and Null checks
75
68
  for operator, conditions in statement.condition.items():
76
- base_operator, operator_type, set_prefix = normalize_operator(operator)
69
+ base_operator, _operator_type, set_prefix = normalize_operator(operator)
77
70
 
78
71
  # Track Null checks
79
72
  if base_operator == "Null":
@@ -87,22 +80,22 @@ class SetOperatorValidationCheck(PolicyCheck):
87
80
 
88
81
  # Second pass: Validate set operator usage
89
82
  for operator, conditions in statement.condition.items():
90
- base_operator, operator_type, set_prefix = normalize_operator(operator)
83
+ base_operator, _operator_type, set_prefix = normalize_operator(operator)
91
84
 
92
85
  if not set_prefix:
93
86
  continue
94
87
 
95
88
  # Check each condition key used with a set operator
96
- for condition_key, condition_values in conditions.items():
89
+ for condition_key, _condition_values in conditions.items():
97
90
  # Issue 1: Set operator used with single-valued context key (anti-pattern)
98
91
  if not is_multivalued_context_key(condition_key):
99
92
  issues.append(
100
93
  ValidationIssue(
101
94
  severity=self.get_severity(config),
102
95
  message=(
103
- f"Set operator '{set_prefix}' should not be used with single-valued "
104
- f"condition key '{condition_key}'. This can lead to overly permissive policies. "
105
- f"Set operators are designed for multivalued context keys like 'aws:TagKeys'. "
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`. "
106
99
  f"See: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-single-vs-multi-valued-context-keys.html"
107
100
  ),
108
101
  statement_sid=statement_sid,
@@ -120,9 +113,9 @@ class SetOperatorValidationCheck(PolicyCheck):
120
113
  ValidationIssue(
121
114
  severity="warning",
122
115
  message=(
123
- f"Security risk: ForAllValues with Allow effect on '{condition_key}' "
124
- f"should include a Null condition check. Without it, requests with missing "
125
- f'\'{condition_key}\' will be granted access. Add: \'"Null": {{"{condition_key}": "false"}}\'. '
116
+ f"Security risk: `ForAllValues` with `Allow` effect on `{condition_key}` "
117
+ f"should include a `Null` condition check. Without it, requests with missing "
118
+ f'`{condition_key}` will be granted access. Add: `"Null": {{"{condition_key}": "false"}}`. '
126
119
  f"See: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-single-vs-multi-valued-context-keys.html"
127
120
  ),
128
121
  statement_sid=statement_sid,
@@ -140,10 +133,10 @@ class SetOperatorValidationCheck(PolicyCheck):
140
133
  ValidationIssue(
141
134
  severity="warning",
142
135
  message=(
143
- f"Unpredictable behavior: ForAnyValue with Deny effect on '{condition_key}' "
144
- f"should include a Null condition check. Without it, requests with missing "
145
- f"'{condition_key}' will evaluate to 'No match' instead of denying access. "
146
- f'Add: \'"Null": {{"{condition_key}": "false"}}\'. '
136
+ f"Unpredictable behavior: `ForAnyValue` with `Deny` effect on `{condition_key}` "
137
+ f"should include a `Null` condition check. Without it, requests with missing "
138
+ f"`{condition_key}` will evaluate to `No match` instead of denying access. "
139
+ f'Add: `"Null": {{"{condition_key}": "false"}}`. '
147
140
  f"See: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_condition-single-vs-multi-valued-context-keys.html"
148
141
  ),
149
142
  statement_sid=statement_sid,
@@ -13,10 +13,11 @@ statement, examining all statements in the policy to find duplicates and format
13
13
 
14
14
  import re
15
15
  from collections import Counter
16
+ from typing import ClassVar
16
17
 
17
- from iam_validator.core.aws_fetcher import AWSServiceFetcher
18
+ from iam_validator.core.aws_service import AWSServiceFetcher
18
19
  from iam_validator.core.check_registry import CheckConfig, PolicyCheck
19
- from iam_validator.core.models import IAMPolicy, Statement, ValidationIssue
20
+ from iam_validator.core.models import IAMPolicy, ValidationIssue
20
21
 
21
22
 
22
23
  def _check_sid_uniqueness_impl(policy: IAMPolicy, severity: str) -> list[ValidationIssue]:
@@ -35,6 +36,10 @@ def _check_sid_uniqueness_impl(policy: IAMPolicy, severity: str) -> list[Validat
35
36
  # No spaces allowed
36
37
  sid_pattern = re.compile(r"^[a-zA-Z0-9_-]+$")
37
38
 
39
+ # Handle policies with no statements
40
+ if not policy.statement:
41
+ return []
42
+
38
43
  # Collect all SIDs (ignoring None/empty values) and check format
39
44
  sids_with_indices: list[tuple[str, int]] = []
40
45
  for idx, statement in enumerate(policy.statement):
@@ -43,15 +48,15 @@ def _check_sid_uniqueness_impl(policy: IAMPolicy, severity: str) -> list[Validat
43
48
  if not sid_pattern.match(statement.sid):
44
49
  # Identify the issue
45
50
  if " " in statement.sid:
46
- issue_msg = f"Statement ID '{statement.sid}' contains spaces, which are not allowed by AWS"
51
+ issue_msg = f"Statement ID `{statement.sid}` contains spaces, which are not allowed by AWS"
47
52
  suggestion = (
48
- f"Remove spaces from the SID. Example: '{statement.sid.replace(' ', '')}'"
53
+ f"Remove spaces from the SID. Example: `{statement.sid.replace(' ', '')}`"
49
54
  )
50
55
  else:
51
56
  invalid_chars = "".join(
52
57
  set(c for c in statement.sid if not c.isalnum() and c not in "_-")
53
58
  )
54
- issue_msg = f"Statement ID '{statement.sid}' contains invalid characters: {invalid_chars}"
59
+ issue_msg = f"Statement ID `{statement.sid}` contains invalid characters: `{invalid_chars}`"
55
60
  suggestion = (
56
61
  "SIDs must contain only alphanumeric characters, hyphens, and underscores"
57
62
  )
@@ -91,7 +96,7 @@ def _check_sid_uniqueness_impl(policy: IAMPolicy, severity: str) -> list[Validat
91
96
  statement_sid=duplicate_sid,
92
97
  statement_index=idx,
93
98
  issue_type="duplicate_sid",
94
- message=f"Statement ID `{duplicate_sid}` is used **{count} times** in this policy (found in statements {statement_numbers})",
99
+ message=f"Statement ID `{duplicate_sid}` is used **{count} times** in this policy (found in statements `{statement_numbers}`)",
95
100
  suggestion="Change this SID to a unique value. Statement IDs help identify and reference specific statements, so duplicates can cause confusion.",
96
101
  line_number=statement.line_number,
97
102
  )
@@ -107,42 +112,11 @@ class SidUniquenessCheck(PolicyCheck):
107
112
  It only runs once when processing the first statement to avoid duplicate work.
108
113
  """
109
114
 
110
- @property
111
- def check_id(self) -> str:
112
- return "sid_uniqueness"
113
-
114
- @property
115
- def description(self) -> str:
116
- return "Validates that Statement IDs (Sids) are unique and follow AWS naming requirements (no spaces)"
117
-
118
- @property
119
- def default_severity(self) -> str:
120
- return "warning"
121
-
122
- async def execute(
123
- self,
124
- statement: Statement,
125
- statement_idx: int,
126
- fetcher: AWSServiceFetcher,
127
- config: CheckConfig,
128
- ) -> list[ValidationIssue]:
129
- """Execute the SID uniqueness check at statement level.
130
-
131
- This is a policy-level check, so statement-level execution returns empty.
132
- The actual check runs in execute_policy() which has access to all statements.
133
-
134
- Args:
135
- statement: The IAM policy statement (unused)
136
- statement_idx: Index of the statement in the policy (unused)
137
- fetcher: AWS service fetcher (unused for this check)
138
- config: Configuration for this check instance (unused)
139
-
140
- Returns:
141
- Empty list (actual check runs in execute_policy())
142
- """
143
- del statement, statement_idx, fetcher, config # Unused
144
- # This is a policy-level check - execution happens in execute_policy()
145
- return []
115
+ check_id: ClassVar[str] = "sid_uniqueness"
116
+ description: ClassVar[str] = (
117
+ "Validates that Statement IDs (Sids) are unique and follow AWS naming requirements (no spaces)"
118
+ )
119
+ default_severity: ClassVar[str] = "warning"
146
120
 
147
121
  async def execute_policy(
148
122
  self,