Skip to content

Commit eb7f901

Browse files
authored
Allow unbracketed IAM policy statements (#183)
1 parent 66c25cd commit eb7f901

File tree

2 files changed

+79
-42
lines changed

2 files changed

+79
-42
lines changed

rules/aws_iam_policy_sid_invalid_characters.go

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ import (
1010
"github.com/terraform-linters/tflint-ruleset-aws/project"
1111
)
1212

13-
type AwsIAMPolicySidInvalidCharactersStatementStruct struct {
13+
type AwsIAMPolicySidInvalidCharactersPolicyStatement struct {
1414
Sid string `json:"Sid"`
1515
}
16-
type AwsIAMPolicySidInvalidCharactersPolicyStruct struct {
17-
Statement []AwsIAMPolicySidInvalidCharactersStatementStruct `json:"Statement"`
16+
type AwsIAMPolicySidInvalidCharactersPolicy struct {
17+
Statement []AwsIAMPolicySidInvalidCharactersPolicyStatement `json:"Statement"`
18+
}
19+
type AwsIAMPolicySidInvalidCharactersPolicyWithSingleStatement struct {
20+
Statement AwsIAMPolicySidInvalidCharactersPolicyStatement `json:"Statement"`
1821
}
1922

2023
// AwsIAMPolicySidInvalidCharactersRule checks for invalid characters in SID
@@ -60,11 +63,20 @@ func (r *AwsIAMPolicySidInvalidCharactersRule) Check(runner tflint.Runner) error
6063
err := runner.EvaluateExpr(attribute.Expr, &val, nil)
6164

6265
return runner.EnsureNoError(err, func() error {
63-
unMarshaledPolicy := AwsIAMPolicySidInvalidCharactersPolicyStruct{}
64-
if jsonErr := json.Unmarshal([]byte(val), &unMarshaledPolicy); jsonErr != nil {
65-
return jsonErr
66+
var statements []AwsIAMPolicySidInvalidCharactersPolicyStatement
67+
68+
policy := AwsIAMPolicySidInvalidCharactersPolicy{}
69+
if err := json.Unmarshal([]byte(val), &policy); err != nil {
70+
// If the Statement clause includes only one value, you can omit the brackets, so try unmarshal to the struct accordingly.
71+
// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_grammar.html
72+
policy := AwsIAMPolicySidInvalidCharactersPolicyWithSingleStatement{}
73+
if err := json.Unmarshal([]byte(val), &policy); err != nil {
74+
return err
75+
}
76+
statements = []AwsIAMPolicySidInvalidCharactersPolicyStatement{policy.Statement}
77+
} else {
78+
statements = policy.Statement
6679
}
67-
statements := unMarshaledPolicy.Statement
6880

6981
for _, statement := range statements {
7082
if statement.Sid == "" {

rules/aws_iam_policy_sid_invalid_characters_test.go

Lines changed: 60 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ resource "aws_iam_policy" "policy" {
2121
role = "test_role"
2222
policy = <<-EOF
2323
{
24-
"Version": "2012-10-17",
25-
"Statement": [
26-
{
27-
"Sid": "This contains invalid-characters.",
28-
"Action": [
29-
"ec2:Describe*"
30-
],
31-
"Effect": "Allow",
32-
"Resource": "arn:aws:s3:::<bucketname>/*"
33-
}
34-
]
24+
"Version": "2012-10-17",
25+
"Statement": [
26+
{
27+
"Sid": "This contains invalid-characters.",
28+
"Action": [
29+
"ec2:Describe*"
30+
],
31+
"Effect": "Allow",
32+
"Resource": "arn:aws:s3:::<bucketname>/*"
33+
}
34+
]
3535
}
3636
EOF
3737
}
@@ -56,25 +56,25 @@ resource "aws_iam_policy" "policy2" {
5656
role = "test_role"
5757
policy = <<-EOF
5858
{
59-
"Version": "2012-10-17",
60-
"Statement": [
61-
{
62-
"Sid": "ThisIsAValidSid",
63-
"Action": [
64-
"ec2:Describe*"
65-
],
66-
"Effect": "Allow",
67-
"Resource": "arn:aws:s3:::<bucketname>/*"
68-
},
69-
{
70-
"Sid": "This contains invalid-characters.",
71-
"Action": [
72-
"ec2:Describe*"
73-
],
74-
"Effect": "Allow",
75-
"Resource": "arn:aws:s3:::<bucketname>/*"
76-
}
77-
]
59+
"Version": "2012-10-17",
60+
"Statement": [
61+
{
62+
"Sid": "ThisIsAValidSid",
63+
"Action": [
64+
"ec2:Describe*"
65+
],
66+
"Effect": "Allow",
67+
"Resource": "arn:aws:s3:::<bucketname>/*"
68+
},
69+
{
70+
"Sid": "This contains invalid-characters.",
71+
"Action": [
72+
"ec2:Describe*"
73+
],
74+
"Effect": "Allow",
75+
"Resource": "arn:aws:s3:::<bucketname>/*"
76+
}
77+
]
7878
}
7979
EOF
8080
}
@@ -112,6 +112,29 @@ resource "aws_iam_policy" "policy" {
112112
}
113113
EOF
114114
}
115+
`,
116+
Expected: helper.Issues{},
117+
},
118+
{
119+
Name: "Single Statement",
120+
Content: `
121+
resource "aws_iam_policy" "policy" {
122+
name = "test_policy"
123+
role = "test_role"
124+
policy = <<-EOF
125+
{
126+
"Version": "2012-10-17",
127+
"Statement": {
128+
"Sid": "ThisIsAValidSid",
129+
"Action": [
130+
"ec2:Describe*"
131+
],
132+
"Effect": "Allow",
133+
"Resource": "arn:aws:s3:::<bucketname>/*"
134+
}
135+
}
136+
EOF
137+
}
115138
`,
116139
Expected: helper.Issues{},
117140
},
@@ -120,12 +143,14 @@ EOF
120143
rule := NewAwsIAMPolicySidInvalidCharactersRule()
121144

122145
for _, tc := range cases {
123-
runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content})
146+
t.Run(tc.Name, func(t *testing.T) {
147+
runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content})
124148

125-
if err := rule.Check(runner); err != nil {
126-
t.Fatalf("Unexpected error occurred: %s", err)
127-
}
149+
if err := rule.Check(runner); err != nil {
150+
t.Fatalf("Unexpected error occurred: %s", err)
151+
}
128152

129-
helper.AssertIssues(t, tc.Expected, runner.Issues)
153+
helper.AssertIssues(t, tc.Expected, runner.Issues)
154+
})
130155
}
131156
}

0 commit comments

Comments
 (0)