iam-policy-validator 1.1.0__py3-none-any.whl → 1.1.1__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: iam-policy-validator
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: Validate AWS IAM policies for correctness and security using AWS Service Reference API
5
5
  Project-URL: Homepage, https://github.com/boogy/iam-policy-validator
6
6
  Project-URL: Documentation, https://github.com/boogy/iam-policy-validator/tree/main/docs
@@ -197,7 +197,7 @@ jobs:
197
197
  - name: Set up Python
198
198
  uses: actions/setup-python@v5
199
199
  with:
200
- python-version: '3.13'
200
+ python-version: '3.12'
201
201
 
202
202
  - name: Install uv
203
203
  uses: astral-sh/setup-uv@v3
@@ -1,38 +1,44 @@
1
1
  iam_validator/__init__.py,sha256=APnMR3Fu4fHhxfsHBvUM2dJIwazgvLKQbfOsSgFPidg,693
2
2
  iam_validator/__main__.py,sha256=to_nz3n_IerJpVVZZ6WSFlFR5s_06J0csfPOTfQZG8g,197
3
- iam_validator/__version__.py,sha256=YOIURWR5ocuvaQTQgwFi1XjHm_ifJDzicMOQSJZqmZc,206
4
- iam_validator/checks/__init__.py,sha256=q-_rIYGZJMjsiHK-R_3CbSUCBVGN5e137LPNDnMRZmw,841
3
+ iam_validator/__version__.py,sha256=xEe2pX5CjvpoW3wJ6rXXULgJzJ3B6BM7RqL5dElKRA4,206
4
+ iam_validator/checks/__init__.py,sha256=eKTPgiZ1i3zvyP6OdKgLx9s3u69onITMYifmJPJwZgM,968
5
5
  iam_validator/checks/action_condition_enforcement.py,sha256=3M1Wj89Af6H-ywBTruZbJPzhCBBQVanVb5hwv-fkiDE,29721
6
- iam_validator/checks/action_validation.py,sha256=KbUw1SV-2nN-HtLlj3zrE6sdd0z8iAF0ubqz35Vwb7c,6921
6
+ iam_validator/checks/action_resource_constraint.py,sha256=p-gP7S9QYR6M7vffrnJY6LOlMUTn0kpEbrxQ8pTY5rs,6031
7
+ iam_validator/checks/action_validation.py,sha256=IpxtTsk58f2zEZ-xzAoyHw4QK8BCRV43OffP-8ydf9E,2578
7
8
  iam_validator/checks/condition_key_validation.py,sha256=bc4LQ8IRKyt0RquaQvQvVjmeJnuOUAFRL8xdduLPa_U,2661
8
9
  iam_validator/checks/policy_size.py,sha256=4cvZiWRJXGuvYo8PRcdD1Py_ZL8Xw0lOJfXTs6EX-_I,5753
9
10
  iam_validator/checks/resource_validation.py,sha256=AEIoiR6AKYLuVaA8ne3QE5qy6NCMDe98_2JAiwE9-JU,4261
10
- iam_validator/checks/security_best_practices.py,sha256=OCAtbsO9HEK97DVPPnm-hJDQtf-ATlnwa1LLshweZDk,32045
11
+ iam_validator/checks/security_best_practices.py,sha256=-gqxtcx_cUV1ZnyZ8Flydwan1vxb-RmnanWoIlU6YyY,21711
11
12
  iam_validator/checks/sid_uniqueness.py,sha256=7S8RtVJgYPTKgr7gSEmxgT0oIGkSoXN6iu0ALHbcSfw,5015
12
- iam_validator/commands/__init__.py,sha256=zuhECuz-1Us5hBNAJtdMae8LaYn1eNeoYPBmQPMwI94,357
13
+ iam_validator/checks/utils/__init__.py,sha256=j0X4ibUB6RGx2a-kNoJnlVZwHfoEvzZsIeTmJIAoFzA,45
14
+ iam_validator/checks/utils/policy_level_checks.py,sha256=2V60C0zhKfsFPjQ-NMlD3EemtwA9S6-4no8nETgXdQE,5274
15
+ iam_validator/checks/utils/sensitive_action_matcher.py,sha256=_kQRGRJDQ0TLHymZbucXmKOfZJ_OUsf5p0blLfP54EY,8883
16
+ iam_validator/checks/utils/wildcard_expansion.py,sha256=L6AWrLRacqXqk9k-5ZmXv5HyoAyz98cg5AlTvzH2tTI,3158
17
+ iam_validator/commands/__init__.py,sha256=lF0fSUukLSxTAvhjg-0P79YMseYwihIr_tmQYbfNgcY,425
13
18
  iam_validator/commands/analyze.py,sha256=TWlDaZ8gVOdNv6__KQQfzeLVW36qLiL5IzlhGYfvq_g,16501
14
19
  iam_validator/commands/base.py,sha256=5baCCMwxz7pdQ6XMpWfXFNz7i1l5dB8Qv9dKKR04Gzs,1074
20
+ iam_validator/commands/cache.py,sha256=1E-irKF3e2CFUEG9s1z64hIKLVSYFQ-L92ld6L3za5g,14368
15
21
  iam_validator/commands/post_to_pr.py,sha256=hl_K-XlELYN-ArjMdgQqysvIE-26yf9XdrMl4ToDwG0,2148
16
22
  iam_validator/commands/validate.py,sha256=R295cOTly8n7zL1jfvbh9RuCgiM5edBqbf6YMn_4G9A,14013
17
23
  iam_validator/core/__init__.py,sha256=1FvJPMrbzJfS9YbRUJCshJLd5gzWwR9Fd_slS0Aq9c8,416
18
24
  iam_validator/core/access_analyzer.py,sha256=poeT1i74jXpKr1B3UmvqiTvCTbq82zffWgZHwiFUwoo,24337
19
- iam_validator/core/access_analyzer_report.py,sha256=iTIFKul6zQZd2qBg8V6zaDNPMKF8D_XDSX6pJwXFVYY,24791
20
- iam_validator/core/aws_fetcher.py,sha256=fUCIMItIWmrbgsoVCz_9Oe5k3SjuXBlNBVwQ60IWwns,25492
25
+ iam_validator/core/access_analyzer_report.py,sha256=IrQVszlhFfQ6WykYLpig7TU3hf8dnQTegPDsOvHjR5Q,24873
26
+ iam_validator/core/aws_fetcher.py,sha256=P7fYX1Q1TICuTOlGaqH97U3m8B0bqGE9jP7cxfmny8k,27418
21
27
  iam_validator/core/aws_global_conditions.py,sha256=ADVcMEWhgvDZWdBmRUQN3HB7a9OycbTLecXFAy3LPbo,5837
22
- iam_validator/core/check_registry.py,sha256=wXg4Yw5LJ-rAVLiPUIJOtw8Y49Q1PY00Zbu37LzyjHY,15477
23
- iam_validator/core/cli.py,sha256=5UHsHS8o7Fkag4d6MNaaqjCFSGu8evCIbtpa81591lE,3831
24
- iam_validator/core/config_loader.py,sha256=6Px_JEzk8WU6g8KXaSLme3x8qZXT9oppxUVXYyDkboY,16425
25
- iam_validator/core/defaults.py,sha256=JzuDYNQGERDtX9S8E5grr4KZmooSephJW8KRplT34L8,10956
28
+ iam_validator/core/check_registry.py,sha256=wxqaF2t_3lWgT6x7_PnnZ8XGjHKUxUk72UlmdYBLFyo,15679
29
+ iam_validator/core/cli.py,sha256=PkXiZjlgrQ21QustBbspefYsdbxst4gxoClyG2_HQR8,3843
30
+ iam_validator/core/config_loader.py,sha256=Pq2rd6LJtEZET0ZeW4hEZS2ZRLC5gNRsKbtLyIsT21I,16516
31
+ iam_validator/core/defaults.py,sha256=sPQJUMyjv4yalGCuyQhlY42rDc_-BZLq6qS0GgoP4mc,11893
26
32
  iam_validator/core/models.py,sha256=rWIZnD-I81Sg4asgOhnB10FWJC5mxQ2JO9bdS0sHb4Q,10772
27
- iam_validator/core/policy_checks.py,sha256=xK5CntsEKVDgN27uIdQ92jCL97t7eBqOk0SChWU9cgw,23872
33
+ iam_validator/core/policy_checks.py,sha256=vIzRkqf5k1BB0elry5a4E2SRBlp6Vz3jPqrav29k3PM,24842
28
34
  iam_validator/core/policy_loader.py,sha256=TR7SpzlRG3TwH4HBGEFUuhNOmxIR8Cud2SQ-AmHWBpM,14040
29
35
  iam_validator/core/pr_commenter.py,sha256=TOhVXKTFcRHQ9EVuShXQcKXn9aNjB1mU6FnR2jvltmw,10581
30
- iam_validator/core/report.py,sha256=aRM1mYlDjkPmQrUZtcq2akbviLPVTSa8q_mH7XyuuK4,32897
36
+ iam_validator/core/report.py,sha256=wPkLvsIej-AaW5FlqntvUuHuEMvyi2iBI6NQF496gCM,33064
31
37
  iam_validator/core/formatters/__init__.py,sha256=fnCKAEBXItnOf2m4rhVs7zwMaTxbG6ESh3CF8V5j5ec,868
32
38
  iam_validator/core/formatters/base.py,sha256=SShDeDiy5mYQnS6BpA8xYg91N-KX1EObkOtlrVHqx1Q,4451
33
39
  iam_validator/core/formatters/console.py,sha256=lX4Yp4bTW61fxe0fCiHuO6bCZtC_6cjCwqDNQ55nT_8,1937
34
40
  iam_validator/core/formatters/csv.py,sha256=2FaN6Y_0TPMFOb3A3tNtj0-9bkEc5P-6eZ7eLROIqFE,5899
35
- iam_validator/core/formatters/enhanced.py,sha256=6hRiATRpH6Osqf_y6C58VN48L47AXYP3Dm9R90VMGs8,16432
41
+ iam_validator/core/formatters/enhanced.py,sha256=k_DwzhGTARUKMv4bjkaCrpI6ypT10z9LcSk8gKlyDIM,16547
36
42
  iam_validator/core/formatters/html.py,sha256=j4sQi-wXiD9kCHldW5JCzbJe0frhiP5uQI9KlH3Sj_g,22994
37
43
  iam_validator/core/formatters/json.py,sha256=A7gZ8P32GEdbDvrSn6v56yQ4fOP_kyMaoFVXG2bgnew,939
38
44
  iam_validator/core/formatters/markdown.py,sha256=aPAY6FpZBHsVBDag3FAsB_X9CZzznFjX9dQr0ysDrTE,2251
@@ -40,8 +46,8 @@ iam_validator/core/formatters/sarif.py,sha256=tqp8g7RmUh0HRk-kKDaucx4sa-5I9ikgkS
40
46
  iam_validator/integrations/__init__.py,sha256=7Hlor_X9j0NZaEjFuSvoXAAuSKQ-zgY19Rk-Dz3JpKo,616
41
47
  iam_validator/integrations/github_integration.py,sha256=bKs94vNT4PmcmUPUeuY2WJFhCYpUY2SWiBP1vj-andA,25673
42
48
  iam_validator/integrations/ms_teams.py,sha256=t2PlWuTDb6GGH-eDU1jnOKd8D1w4FCB68bahGA7MJcE,14475
43
- iam_policy_validator-1.1.0.dist-info/METADATA,sha256=a_cegBs1dAS4y_ByXskcvgXY3CNsqC-7frERfdQR27k,25144
44
- iam_policy_validator-1.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
45
- iam_policy_validator-1.1.0.dist-info/entry_points.txt,sha256=8HtWd8O7mvPiPdZR5YbzY8or_qcqLM4-pKaFdhtFT8M,62
46
- iam_policy_validator-1.1.0.dist-info/licenses/LICENSE,sha256=AMnbFTBDcK4_MITe2wiQBkj0vg-jjBBhsc43ydC7tt4,1098
47
- iam_policy_validator-1.1.0.dist-info/RECORD,,
49
+ iam_policy_validator-1.1.1.dist-info/METADATA,sha256=tliAMa89Y5CSxbEy0PCV_IXr-FSn07I91AKl44QpoJQ,25144
50
+ iam_policy_validator-1.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
51
+ iam_policy_validator-1.1.1.dist-info/entry_points.txt,sha256=8HtWd8O7mvPiPdZR5YbzY8or_qcqLM4-pKaFdhtFT8M,62
52
+ iam_policy_validator-1.1.1.dist-info/licenses/LICENSE,sha256=AMnbFTBDcK4_MITe2wiQBkj0vg-jjBBhsc43ydC7tt4,1098
53
+ iam_policy_validator-1.1.1.dist-info/RECORD,,
@@ -3,5 +3,5 @@
3
3
  This file is the single source of truth for the package version.
4
4
  """
5
5
 
6
- __version__ = "1.0.4"
6
+ __version__ = "1.1.1"
7
7
  __version_info__ = tuple(int(part) for part in __version__.split("."))
@@ -5,6 +5,7 @@ Built-in policy checks for IAM Policy Validator.
5
5
  from iam_validator.checks.action_condition_enforcement import (
6
6
  ActionConditionEnforcementCheck,
7
7
  )
8
+ from iam_validator.checks.action_resource_constraint import ActionResourceConstraintCheck
8
9
  from iam_validator.checks.action_validation import ActionValidationCheck
9
10
  from iam_validator.checks.condition_key_validation import ConditionKeyValidationCheck
10
11
  from iam_validator.checks.policy_size import PolicySizeCheck
@@ -14,6 +15,7 @@ from iam_validator.checks.sid_uniqueness import SidUniquenessCheck
14
15
 
15
16
  __all__ = [
16
17
  "ActionConditionEnforcementCheck",
18
+ "ActionResourceConstraintCheck",
17
19
  "ActionValidationCheck",
18
20
  "ConditionKeyValidationCheck",
19
21
  "PolicySizeCheck",
@@ -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
@@ -1,73 +1,22 @@
1
- """Action validation check - validates IAM actions against AWS service definitions."""
1
+ """Action validation check - validates IAM actions against AWS service definitions.
2
2
 
3
- import re
4
- from functools import lru_cache
5
- from re import Pattern
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
+ """
6
11
 
7
12
  from iam_validator.core.aws_fetcher import AWSServiceFetcher
8
13
  from iam_validator.core.check_registry import CheckConfig, PolicyCheck
9
14
  from iam_validator.core.models import Statement, ValidationIssue
10
15
 
11
16
 
12
- # Global cache for compiled wildcard patterns
13
- @lru_cache(maxsize=512)
14
- def _compile_wildcard_pattern(pattern: str) -> Pattern[str]:
15
- """Compile and cache wildcard patterns for O(1) reuse.
16
-
17
- Args:
18
- pattern: Wildcard pattern (e.g., "s3:Get*")
19
-
20
- Returns:
21
- Compiled regex pattern
22
-
23
- Performance:
24
- 20-30x speedup by avoiding repeated pattern compilation
25
- """
26
- regex_pattern = "^" + re.escape(pattern).replace(r"\*", ".*") + "$"
27
- return re.compile(regex_pattern, re.IGNORECASE)
28
-
29
-
30
17
  class ActionValidationCheck(PolicyCheck):
31
18
  """Validates that IAM actions exist in AWS services."""
32
19
 
33
- # Default allowlist for safe wildcard patterns (read-only operations)
34
- # Note: s3:Get* is intentionally excluded as reading S3 data can be sensitive
35
- # Using frozenset for O(1) lookup performance
36
- DEFAULT_ALLOWED_WILDCARDS = frozenset(
37
- {
38
- "s3:List*",
39
- "s3:Describe*",
40
- "ec2:Describe*",
41
- "iam:Get*",
42
- "iam:List*",
43
- "rds:Describe*",
44
- "lambda:Get*",
45
- "lambda:List*",
46
- "dynamodb:Describe*",
47
- "cloudwatch:Describe*",
48
- "cloudwatch:Get*",
49
- "cloudwatch:List*",
50
- "logs:Describe*",
51
- "logs:Get*",
52
- "logs:Filter*",
53
- "kms:Describe*",
54
- "kms:Get*",
55
- "kms:List*",
56
- "sns:Get*",
57
- "sns:List*",
58
- "sqs:Get*",
59
- "sqs:List*",
60
- "elasticloadbalancing:Describe*",
61
- "autoscaling:Describe*",
62
- "cloudformation:Describe*",
63
- "cloudformation:Get*",
64
- "cloudformation:List*",
65
- "route53:Get*",
66
- "route53:List*",
67
- "apigateway:GET",
68
- }
69
- )
70
-
71
20
  @property
72
21
  def check_id(self) -> str:
73
22
  return "action_validation"
@@ -80,41 +29,6 @@ class ActionValidationCheck(PolicyCheck):
80
29
  def default_severity(self) -> str:
81
30
  return "error"
82
31
 
83
- def _is_allowed_wildcard(
84
- self, action: str, allowed_wildcards: frozenset[str] | list[str] | set[str]
85
- ) -> bool:
86
- """Check if a wildcard action matches the allowlist.
87
-
88
- Args:
89
- action: The action to check (e.g., "s3:Get*")
90
- allowed_wildcards: Set or list of allowed wildcard patterns
91
-
92
- Returns:
93
- True if the action matches any pattern in the allowlist
94
-
95
- Note:
96
- Exact matches use O(1) set lookup for performance.
97
- Pattern matches (wildcards in allowlist) require O(n) iteration.
98
- """
99
- # Fast O(1) exact match using set membership
100
- if action in allowed_wildcards:
101
- return True
102
-
103
- # Pattern match - check if action matches any pattern in allowlist
104
- # This is needed when allowlist contains wildcards like "s3:*"
105
- # Uses cached compiled patterns for 20-30x speedup
106
- for pattern in allowed_wildcards:
107
- # Skip exact matches (already checked above)
108
- if "*" not in pattern:
109
- continue
110
-
111
- # Use cached compiled pattern
112
- compiled = _compile_wildcard_pattern(pattern)
113
- if compiled.match(action):
114
- return True
115
-
116
- return False
117
-
118
32
  async def execute(
119
33
  self,
120
34
  statement: Statement,
@@ -122,7 +36,11 @@ class ActionValidationCheck(PolicyCheck):
122
36
  fetcher: AWSServiceFetcher,
123
37
  config: CheckConfig,
124
38
  ) -> list[ValidationIssue]:
125
- """Execute action validation on a statement."""
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
+ """
126
44
  issues = []
127
45
 
128
46
  # Get actions from statement
@@ -130,28 +48,13 @@ class ActionValidationCheck(PolicyCheck):
130
48
  statement_sid = statement.sid
131
49
  line_number = statement.line_number
132
50
 
133
- # Get allowed wildcards from config, or use defaults
134
- allowed_wildcards_raw = config.config.get(
135
- "allowed_wildcards", self.DEFAULT_ALLOWED_WILDCARDS
136
- )
137
-
138
- # Convert list to frozenset for O(1) lookups (if from config)
139
- # DEFAULT_ALLOWED_WILDCARDS is already a frozenset
140
- if isinstance(allowed_wildcards_raw, list):
141
- allowed_wildcards = frozenset(allowed_wildcards_raw)
142
- else:
143
- allowed_wildcards = allowed_wildcards_raw
144
-
145
- # Check if wildcard warnings are disabled entirely
146
- disable_wildcard_warnings = config.config.get("disable_wildcard_warnings", False)
147
-
148
51
  for action in actions:
149
- # Wildcard-only actions are handled by security checks
150
- if action == "*":
52
+ # Skip wildcard actions - they're handled by security_best_practices_check
53
+ if action == "*" or "*" in action:
151
54
  continue
152
55
 
153
- # Validate the action
154
- is_valid, error_msg, is_wildcard = await fetcher.validate_action(action)
56
+ # Validate the action exists in AWS
57
+ is_valid, error_msg, _is_wildcard = await fetcher.validate_action(action)
155
58
 
156
59
  if not is_valid:
157
60
  issues.append(
@@ -165,28 +68,5 @@ class ActionValidationCheck(PolicyCheck):
165
68
  line_number=line_number,
166
69
  )
167
70
  )
168
- elif is_wildcard:
169
- # Check if this wildcard is in the allowlist
170
- if self._is_allowed_wildcard(action, allowed_wildcards):
171
- # Wildcard is allowed, skip warning
172
- continue
173
-
174
- # Wildcard actions are security concerns (unless disabled)
175
- # Note: This uses "info" severity by default because the security_best_practices
176
- # check provides more comprehensive wildcard analysis with proper security severities.
177
- # This is mainly informational - the action IS valid, just uses a wildcard.
178
- if not disable_wildcard_warnings:
179
- issues.append(
180
- ValidationIssue(
181
- severity="info", # Changed from "warning" - this is just informational
182
- statement_sid=statement_sid,
183
- statement_index=statement_idx,
184
- issue_type="wildcard_action",
185
- message=f"Action uses wildcard: {action}",
186
- action=action,
187
- suggestion="Consider using specific actions instead of wildcards for better security",
188
- line_number=line_number,
189
- )
190
- )
191
71
 
192
72
  return issues