Skip to content

Commit 435729d

Browse files
authored
Update iam rules (#4026)
* Update iam rules * Update W3037 to use regex for *,?
1 parent 8ea6c9c commit 435729d

File tree

4 files changed

+39
-28
lines changed

4 files changed

+39
-28
lines changed

src/cfnlint/rules/resources/iam/Permissions.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
from typing import Any
99

10+
import regex as re
11+
1012
from cfnlint.data import AdditionalSpecs
1113
from cfnlint.helpers import ensure_list, load_resource
1214
from cfnlint.jsonschema import ValidationError, ValidationResult, Validator
@@ -61,17 +63,12 @@ def validate(
6163
enums = list(self.service_map[service].get("Actions", []).keys())
6264
if permission == "*":
6365
pass
64-
elif permission.endswith("*"):
65-
wilcarded_permission = permission.split("*")[0]
66-
if not any(wilcarded_permission in action for action in enums):
67-
yield ValidationError(
68-
f"{permission!r} is not one of {enums!r}",
69-
rule=self,
70-
)
7166

72-
elif permission.startswith("*"):
73-
wilcarded_permission = permission.split("*")[1]
74-
if not any(wilcarded_permission in action for action in enums):
67+
if any(x in permission for x in ["*", "?"]):
68+
permission_regex = (
69+
f"^{permission.replace('*', '.*').replace('?', '.')}$"
70+
)
71+
if not any(re.match(permission_regex, action) for action in enums):
7572
yield ValidationError(
7673
f"{permission!r} is not one of {enums!r}",
7774
rule=self,

src/cfnlint/rules/resources/iam/StatementResources.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -101,24 +101,25 @@ def validate(
101101

102102
using_fn_arns = False
103103
all_resources: set[str] = set()
104-
for resources, _ in get_value_from_path(
105-
validator, instance, path=deque(["Resource"])
106-
):
107-
resources = ensure_list(resources)
108-
109-
for resource in resources:
110-
if not isinstance(resource, str):
111-
k, v = is_function(resource)
112-
if k is None:
104+
for key in ["Resource", "NotResource"]:
105+
for resources, _ in get_value_from_path(
106+
validator, instance, path=deque([key])
107+
):
108+
resources = ensure_list(resources)
109+
110+
for resource in resources:
111+
if not isinstance(resource, str):
112+
k, v = is_function(resource)
113+
if k is None:
114+
continue
115+
if k == "Ref" and validator.is_type(v, "string"):
116+
if v in validator.context.parameters:
117+
return
118+
using_fn_arns = True
113119
continue
114-
if k == "Ref" and validator.is_type(v, "string"):
115-
if v in validator.context.parameters:
116-
return
117-
using_fn_arns = True
118-
continue
119-
if resource == "*":
120-
return
121-
all_resources.add(resource)
120+
if resource == "*":
121+
return
122+
all_resources.add(resource)
122123

123124
all_resource_arns = [_Arn(a) for a in all_resources]
124125
for actions, _ in get_value_from_path(
@@ -130,7 +131,7 @@ def validate(
130131

131132
if not validator.is_type(action, "string"):
132133
continue
133-
if "*" in action:
134+
if any(x in action for x in ["*", "?"]):
134135
continue
135136
if ":" not in action:
136137
continue

test/unit/rules/resources/iam/test_iam_permissions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,14 @@ def rule():
3131
("Invalid service", "foo:Bar", 1),
3232
("Empty string", "", 1),
3333
("A function", {"Ref": "MyParameter"}, 0),
34+
("asterisk in the middle", "iam:*Tags", 0),
35+
("multiple asterisks good", "iam:*Group*", 0),
36+
("multiple asterisks bad", "iam:*ec2*", 1),
37+
("question mark is bad", "iam:Tag?", 1),
38+
("question mark is good", "iam:TagRol?", 0),
3439
],
3540
)
3641
def test_permissions(name, instance, err_count, rule, validator):
3742
errors = list(rule.validate(validator, {}, instance, {}))
43+
3844
assert len(errors) == err_count, f"Test {name!r} got {errors!r}"

test/unit/rules/resources/iam/test_statement_resources.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,13 @@ def template():
243243
),
244244
],
245245
),
246+
(
247+
{
248+
"Action": ["cloudformation:Create?tack"],
249+
"Resource": ["arn:aws:logs:us-east-1:123456789012:stack/dne/*"],
250+
},
251+
[],
252+
),
246253
(
247254
{
248255
"Action": ["cloudformation:CreateStack"],

0 commit comments

Comments
 (0)