Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit ce36ba7

Browse files
authoredMar 19, 2025··
Update IAM Resource policy pattern (#4040)
* Update IAM Resource policy * Add rule E3514 to validate resource policy resource ARNs * Update W3037 to limit actions for resource policies
1 parent dded1dc commit ce36ba7

File tree

8 files changed

+429
-32
lines changed

8 files changed

+429
-32
lines changed
 

‎src/cfnlint/data/schemas/other/iam/policy.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
]
1717
},
1818
"AwsArn": {
19-
"pattern": "(^arn:(aws|aws-cn|aws-us-gov):[^:]+:[^:]*(:(?:\\d{12}|\\*|aws)?:.+|)|\\*)$"
19+
"pattern": "^(arn:aws[A-Za-z\\-]*?:[^:]+:[^:]*(:(?:\\d{12}|\\*|aws)?:.+|)|\\*)$"
2020
},
2121
"AwsPrincipalArn": {
2222
"anyOf": [

‎src/cfnlint/data/schemas/other/iam/policy_resource.json

+16-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22
"$id": "resource",
33
"additionalProperties": false,
44
"definitions": {
5+
"Resource": {
6+
"cfnLint": [
7+
"AWS::IAM::Policy/Properties/PolicyDocument/Statement/Resource"
8+
],
9+
"items": {
10+
"cfnLint": [
11+
"AWS::IAM::Policy/Properties/PolicyDocument/Statement/Resource"
12+
],
13+
"type": "string"
14+
},
15+
"type": [
16+
"string",
17+
"array"
18+
]
19+
},
520
"Statement": {
621
"additionalProperties": false,
722
"allOf": [
@@ -52,7 +67,7 @@
5267
"$ref": "policy#/definitions/Principal"
5368
},
5469
"Resource": {
55-
"$ref": "policy#/definitions/Resource"
70+
"$ref": "#/definitions/Resource"
5671
},
5772
"Sid": {
5873
"$ref": "policy#/definitions/Statement/properties/Sid"

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

+28-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ def __init__(self):
2929
super().__init__(
3030
["AWS::IAM::Policy/Properties/PolicyDocument/Statement/Action"]
3131
)
32-
self.service_map = load_resource(AdditionalSpecs, "Policies.json")
32+
self._service_map = load_resource(AdditionalSpecs, "Policies.json")
33+
self._resource_action_limitations = {
34+
"AWS::S3::BucketPolicy": ["s3"],
35+
"AWS::SQS::QueuePolicy": ["sqs"],
36+
"AWS::SNS::TopicPolicy": ["sns"],
37+
}
3338

3439
def validate(
3540
self, validator: Validator, _, instance: Any, schema: dict[str, Any]
@@ -59,8 +64,27 @@ def validate(
5964
service = service.lower()
6065
permission = permission.lower()
6166

62-
if service in self.service_map:
63-
enums = list(self.service_map[service].get("Actions", []).keys())
67+
if len(validator.context.path.cfn_path) >= 2:
68+
if (
69+
validator.context.path.cfn_path[1]
70+
in self._resource_action_limitations
71+
):
72+
if (
73+
service
74+
not in self._resource_action_limitations[
75+
validator.context.path.cfn_path[1]
76+
]
77+
):
78+
yield ValidationError(
79+
(
80+
f"{service!r} is not one of "
81+
f"{self._resource_action_limitations[validator.context.path.cfn_path[1]]!r}"
82+
),
83+
rule=self,
84+
)
85+
86+
if service in self._service_map:
87+
enums = list(self._service_map[service].get("Actions", []).keys())
6488
if permission == "*":
6589
pass
6690

@@ -80,6 +104,6 @@ def validate(
80104
)
81105
else:
82106
yield ValidationError(
83-
f"{service!r} is not one of {list(self.service_map.keys())!r}",
107+
f"{service!r} is not one of {list(self._service_map.keys())!r}",
84108
rule=self,
85109
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
SPDX-License-Identifier: MIT-0
4+
"""
5+
6+
from __future__ import annotations
7+
8+
from typing import Any
9+
10+
import regex as re
11+
12+
from cfnlint.jsonschema import ValidationError, ValidationResult, Validator
13+
from cfnlint.rules.jsonschema.CfnLintKeyword import CfnLintKeyword
14+
15+
16+
class ResourcePolicyResourceArn(CfnLintKeyword):
17+
18+
id = "E3514"
19+
shortdesc = "Validate IAM resource policy resource ARNs"
20+
description = "Validates an IAM resource policy has a compliant resource ARN"
21+
source_url = (
22+
"https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_identifiers.html"
23+
)
24+
tags = ["parameters", "iam"]
25+
26+
def __init__(self) -> None:
27+
super().__init__(
28+
[
29+
"AWS::IAM::Policy/Properties/PolicyDocument/Statement/Resource",
30+
]
31+
)
32+
33+
# pylint: disable=unused-argument
34+
def validate(
35+
self, validator: Validator, aZ: Any, arn: Any, schema: dict[str, Any]
36+
) -> ValidationResult:
37+
if not validator.is_type(arn, "string"):
38+
return
39+
40+
if not len(validator.context.path.cfn_path) >= 2:
41+
return
42+
43+
patrn: str = (
44+
"^(arn:aws[A-Za-z\\-]*?:[^:]+:[^:]*(:(?:\\d{12}|\\*|aws)?:.+|)|\\*)$"
45+
)
46+
if validator.context.path.cfn_path[1] == "AWS::S3::BucketPolicy":
47+
patrn = "^arn:aws[A-Za-z\\-]*?:[^:]+:[^:]*(:(?:\\d{12}|\\*|aws)?:.+|)$"
48+
49+
if not re.match(patrn, arn):
50+
yield ValidationError(
51+
f"{arn!r} does not match {patrn!r}",
52+
rule=self,
53+
)

‎test/fixtures/results/quickstart/nist_application.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
},
9090
{
9191
"Filename": "test/fixtures/templates/quickstart/nist_application.yaml",
92-
"Id": "e7287f1c-73b2-cb51-ec2b-8ab42c30ca27",
92+
"Id": "c855ed21-761f-b3a3-1fac-92578c95872d",
9393
"Level": "Warning",
9494
"Location": {
9595
"End": {
@@ -106,7 +106,7 @@
106106
"LineNumber": 198
107107
}
108108
},
109-
"Message": "{'Ref': 'pSecurityAlarmTopic'} does not match '(^arn:(aws|aws-cn|aws-us-gov):[^:]+:[^:]*(:(?:\\\\d{12}|\\\\*|aws)?:.+|)|\\\\*)$' when 'Ref' is resolved",
109+
"Message": "{'Ref': 'pSecurityAlarmTopic'} does not match '^(arn:aws[A-Za-z\\\\-]*?:[^:]+:[^:]*(:(?:\\\\d{12}|\\\\*|aws)?:.+|)|\\\\*)$' when 'Ref' is resolved",
110110
"ParentId": null,
111111
"Rule": {
112112
"Description": "Resolve the Ref and then validate the values against the schema",

‎test/fixtures/results/quickstart/non_strict/nist_application.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
},
9090
{
9191
"Filename": "test/fixtures/templates/quickstart/nist_application.yaml",
92-
"Id": "e7287f1c-73b2-cb51-ec2b-8ab42c30ca27",
92+
"Id": "c855ed21-761f-b3a3-1fac-92578c95872d",
9393
"Level": "Warning",
9494
"Location": {
9595
"End": {
@@ -106,7 +106,7 @@
106106
"LineNumber": 198
107107
}
108108
},
109-
"Message": "{'Ref': 'pSecurityAlarmTopic'} does not match '(^arn:(aws|aws-cn|aws-us-gov):[^:]+:[^:]*(:(?:\\\\d{12}|\\\\*|aws)?:.+|)|\\\\*)$' when 'Ref' is resolved",
109+
"Message": "{'Ref': 'pSecurityAlarmTopic'} does not match '^(arn:aws[A-Za-z\\\\-]*?:[^:]+:[^:]*(:(?:\\\\d{12}|\\\\*|aws)?:.+|)|\\\\*)$' when 'Ref' is resolved",
110110
"ParentId": null,
111111
"Rule": {
112112
"Description": "Resolve the Ref and then validate the values against the schema",
There was a problem loading the remainder of the diff.

0 commit comments

Comments
 (0)
Please sign in to comment.