Skip to content

Commit ccdd4aa

Browse files
authored
aws_iam_policy_sid (#155)
* aws_iam_policy_sid - add invalid sid rules fixesterraform-linters/tflint-ruleset-aws#149 * aws_iam_policy_sid - update error message and use structs to define json * aws_iam_policy_sid - update docs
1 parent b2d425a commit ccdd4aa

File tree

4 files changed

+247
-0
lines changed

4 files changed

+247
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# aws_iam_policy_sid_invalid_characters
2+
3+
AWS IAM policies have statement ids within each statement that must fit the regexp `^[a-zA-Z0-9]+$` otherwise the policy will fail.
4+
5+
## Example
6+
7+
```hcl
8+
resource "aws_iam_policy" "policy" {
9+
name = "test_policy"
10+
role = "test_role"
11+
policy = <<-EOF
12+
{
13+
"Version": "2012-10-17",
14+
"Statement": [
15+
{
16+
"Sid": "This contains invalid-characters.", //invalid Sid
17+
"Action": [
18+
"ec2:Describe*"
19+
],
20+
"Effect": "Allow",
21+
"Resource": "arn:aws:s3:::<bucketname>/*"
22+
}
23+
]
24+
}
25+
EOF
26+
}
27+
```
28+
29+
```
30+
$ tflint
31+
1 issue(s) found:
32+
33+
Error: The policy's sid ("This contains invalid-characters.") does not match "^[a-zA-Z0-9]+$". (aws_iam_policy_sid_invalid_characters)
34+
35+
on template.tf line 3:
36+
3: policy = <<-EOF
37+
{
38+
"Version": "2012-10-17",
39+
"Statement": [
40+
{
41+
"Sid": "This contains invalid-characters.", //invalid Sid
42+
"Action": [
43+
"ec2:Describe*"
44+
],
45+
"Effect": "Allow",
46+
"Resource": "arn:aws:s3:::<bucketname>/*"
47+
}
48+
]
49+
}
50+
EOF
51+
52+
```
53+
54+
## Why
55+
56+
AWS will fail to create or update the policy if the Sid is not valid
57+
58+
## How To Fix
59+
60+
Make sure you Sid's fit the regexp `^[a-zA-Z0-9]+$`
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package rules
2+
3+
import (
4+
"encoding/json"
5+
hcl "github.com/hashicorp/hcl/v2"
6+
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
7+
"github.com/terraform-linters/tflint-ruleset-aws/project"
8+
"regexp"
9+
"fmt"
10+
)
11+
type AwsIAMPolicySidInvalidCharactersStatementStruct struct {
12+
Sid string `json:"Sid"`
13+
}
14+
type AwsIAMPolicySidInvalidCharactersPolicyStruct struct {
15+
Statement []AwsIAMPolicySidInvalidCharactersStatementStruct `json:"Statement"`
16+
}
17+
18+
// AwsIAMPolicySidInvalidCharactersRule checks for invalid characters in SID
19+
type AwsIAMPolicySidInvalidCharactersRule struct {
20+
resourceType string
21+
attributeName string
22+
validCharacters *regexp.Regexp
23+
}
24+
25+
// NewAwsIAMPolicySidInvalidCharactersRule returns new rule with default attributes
26+
func NewAwsIAMPolicySidInvalidCharactersRule() *AwsIAMPolicySidInvalidCharactersRule {
27+
return &AwsIAMPolicySidInvalidCharactersRule{
28+
resourceType: "aws_iam_policy",
29+
attributeName: "policy",
30+
validCharacters: regexp.MustCompile(`^[a-zA-Z0-9]+$`),
31+
}
32+
}
33+
34+
// Name returns the rule name
35+
func (r *AwsIAMPolicySidInvalidCharactersRule) Name() string {
36+
return "aws_iam_policy_sid_invalid_characters"
37+
}
38+
39+
// Enabled returns whether the rule is enabled by default
40+
func (r *AwsIAMPolicySidInvalidCharactersRule) Enabled() bool {
41+
return true
42+
}
43+
44+
// Severity returns the rule severity
45+
func (r *AwsIAMPolicySidInvalidCharactersRule) Severity() string {
46+
return tflint.ERROR
47+
}
48+
49+
// Link returns the rule reference link
50+
func (r *AwsIAMPolicySidInvalidCharactersRule) Link() string {
51+
return project.ReferenceLink(r.Name())
52+
}
53+
54+
// Check checks the unmarshaled policy and loops through statements checking for invalid statement ids
55+
func (r *AwsIAMPolicySidInvalidCharactersRule) Check(runner tflint.Runner) error {
56+
return runner.WalkResourceAttributes(r.resourceType, r.attributeName, func(attribute *hcl.Attribute) error {
57+
var val string
58+
err := runner.EvaluateExpr(attribute.Expr, &val, nil)
59+
unMarshaledPolicy := AwsIAMPolicySidInvalidCharactersPolicyStruct{}
60+
if jsonErr := json.Unmarshal([]byte(val), &unMarshaledPolicy); jsonErr != nil {
61+
return jsonErr;
62+
}
63+
statements := unMarshaledPolicy.Statement
64+
65+
return runner.EnsureNoError(err, func() error {
66+
for _, statement := range statements {
67+
if r.validCharacters.MatchString(statement.Sid) == false {
68+
runner.EmitIssueOnExpr(
69+
r,
70+
fmt.Sprintf("The policy's sid (\"%s\") does not match \"%s\".", statement.Sid, r.validCharacters.String()),
71+
attribute.Expr,
72+
)
73+
}
74+
}
75+
return nil
76+
})
77+
return nil
78+
})
79+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package rules
2+
3+
import (
4+
"testing"
5+
6+
hcl "github.com/hashicorp/hcl/v2"
7+
"github.com/terraform-linters/tflint-plugin-sdk/helper"
8+
)
9+
10+
func Test_AwsIAMPolicySidInvalidCharacters(t *testing.T) {
11+
cases := []struct {
12+
Name string
13+
Content string
14+
Expected helper.Issues
15+
}{
16+
{
17+
Name: "single statement with invalid",
18+
Content: `
19+
resource "aws_iam_policy" "policy" {
20+
name = "test_policy"
21+
role = "test_role"
22+
policy = <<-EOF
23+
{
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+
]
35+
}
36+
EOF
37+
}
38+
`,
39+
Expected: helper.Issues{
40+
{
41+
Rule: NewAwsIAMPolicySidInvalidCharactersRule(),
42+
Message: `The policy's sid ("This contains invalid-characters.") does not match "^[a-zA-Z0-9]+$".`,
43+
Range: hcl.Range{
44+
Filename: "resource.tf",
45+
Start: hcl.Pos{Line: 5, Column: 11},
46+
End: hcl.Pos{Line: 19, Column: 4},
47+
},
48+
},
49+
},
50+
},
51+
{
52+
Name: "two statements second invalid",
53+
Content: `
54+
resource "aws_iam_policy" "policy2" {
55+
name = "test_policy"
56+
role = "test_role"
57+
policy = <<-EOF
58+
{
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+
]
78+
}
79+
EOF
80+
}
81+
`,
82+
Expected: helper.Issues{
83+
{
84+
Rule: NewAwsIAMPolicySidInvalidCharactersRule(),
85+
Message: `The policy's sid ("This contains invalid-characters.") does not match "^[a-zA-Z0-9]+$".`,
86+
Range: hcl.Range{
87+
Filename: "resource.tf",
88+
Start: hcl.Pos{Line: 5, Column: 11},
89+
End: hcl.Pos{Line: 27, Column: 4},
90+
},
91+
},
92+
},
93+
},
94+
}
95+
96+
rule := NewAwsIAMPolicySidInvalidCharactersRule()
97+
98+
for _, tc := range cases {
99+
runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content})
100+
101+
if err := rule.Check(runner); err != nil {
102+
t.Fatalf("Unexpected error occurred: %s", err)
103+
}
104+
105+
helper.AssertIssues(t, tc.Expected, runner.Issues)
106+
}
107+
}

rules/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ var Rules = append([]tflint.Rule{
3232
NewAwsElastiCacheReplicationGroupDefaultParameterGroupRule(),
3333
NewAwsElastiCacheReplicationGroupInvalidTypeRule(),
3434
NewAwsElastiCacheReplicationGroupPreviousTypeRule(),
35+
NewAwsIAMPolicySidInvalidCharactersRule(),
3536
}, models.Rules...)

0 commit comments

Comments
 (0)