Skip to content

Commit 6986646

Browse files
x-colorwata727
andauthored
Add aws_security_group_invalid_protocol rule (#356)
* Add aws_security_group_invalid_protocol rule * Fix allowed protocols * Update README.md.tmpl * Update README.md Co-authored-by: Kazuma Watanabe <[email protected]>
1 parent fdc5acc commit 6986646

File tree

6 files changed

+266
-0
lines changed

6 files changed

+266
-0
lines changed

docs/rules/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ These rules warn of possible errors that can occur at `terraform apply`. Rules m
5151
|aws_s3_bucket_invalid_acl|Disallow invalid ACL rule for S3 bucket|||
5252
|aws_s3_bucket_invalid_region|Disallow invalid region for S3 bucket|||
5353
|aws_spot_fleet_request_invalid_excess_capacity_termination_policy|Disallow invalid excess capacity termination policy|||
54+
|[aws_security_group_invalid_protocol](aws_security_group_invalid_protocol.md)|Disallow using invalid protocol|||
5455
|[aws_security_group_rule_invalid_protocol](aws_security_group_rule_invalid_protocol.md)|Disallow using invalid protocol|||
5556

5657
### Best Practices/Naming Conventions

docs/rules/README.md.tmpl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ These rules warn of possible errors that can occur at `terraform apply`. Rules m
5151
|aws_s3_bucket_invalid_acl|Disallow invalid ACL rule for S3 bucket||✔|
5252
|aws_s3_bucket_invalid_region|Disallow invalid region for S3 bucket||✔|
5353
|aws_spot_fleet_request_invalid_excess_capacity_termination_policy|Disallow invalid excess capacity termination policy||✔|
54+
|[aws_security_group_invalid_protocol](aws_security_group_invalid_protocol.md)|Disallow using invalid protocol||✔|
5455
|[aws_security_group_rule_invalid_protocol](aws_security_group_rule_invalid_protocol.md)|Disallow using invalid protocol||✔|
5556

5657
### Best Practices/Naming Conventions
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# aws_security_group_invalid_protocol
2+
3+
Disallow using invalid protocol.
4+
5+
## Example
6+
7+
```hcl
8+
resource "aws_security_group" "sample" {
9+
description = "test security group"
10+
11+
ingress {
12+
from_port = 443
13+
to_port = 443
14+
protocol = "https"
15+
}
16+
}
17+
```
18+
19+
```
20+
$ tflint
21+
1 issue(s) found:
22+
23+
Error: "https" is an invalid protocol. It allows "tcp", "udp" or "-1"(all). (aws_security_group_invalid_protocol)
24+
25+
on terraform.tf line 7:
26+
7: protocol = "https"
27+
```
28+
29+
## Why
30+
31+
Apply will fail. (Plan will succeed with the invalid value though)
32+
33+
## How To Fix
34+
35+
Select valid protocol according to the [document](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_SecurityGroupRule.html)
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package rules
2+
3+
import (
4+
"fmt"
5+
"strconv"
6+
"strings"
7+
8+
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
9+
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
10+
"github.com/terraform-linters/tflint-ruleset-aws/project"
11+
)
12+
13+
// AwsSecurityGroupInvalidProtocolRule checks whether "protocol" in "ingress" or "egress" has invalid value
14+
type AwsSecurityGroupInvalidProtocolRule struct {
15+
tflint.DefaultRule
16+
17+
resourceType string
18+
protocols map[string]struct{}
19+
}
20+
21+
// NewAwsSecurityGroupInvalidProtocolRule returns new rule with default attributes
22+
func NewAwsSecurityGroupInvalidProtocolRule() *AwsSecurityGroupInvalidProtocolRule {
23+
return &AwsSecurityGroupInvalidProtocolRule{
24+
resourceType: "aws_security_group",
25+
protocols: map[string]struct{}{
26+
"all": {},
27+
"tcp": {},
28+
"udp": {},
29+
"icmp": {},
30+
"icmpv6": {},
31+
},
32+
}
33+
}
34+
35+
// Name returns the rule name
36+
func (r *AwsSecurityGroupInvalidProtocolRule) Name() string {
37+
return "aws_security_group_invalid_protocol"
38+
}
39+
40+
// Enabled returns whether the rule is enabled by default
41+
func (r *AwsSecurityGroupInvalidProtocolRule) Enabled() bool {
42+
return true
43+
}
44+
45+
// Severity returns the rule severity
46+
func (r *AwsSecurityGroupInvalidProtocolRule) Severity() tflint.Severity {
47+
return tflint.ERROR
48+
}
49+
50+
// Link returns the rule reference link
51+
func (r *AwsSecurityGroupInvalidProtocolRule) Link() string {
52+
return project.ReferenceLink(r.Name())
53+
}
54+
55+
// Check checks whether "protocol" in "ingress" or "egress" has invalid value
56+
func (r *AwsSecurityGroupInvalidProtocolRule) Check(runner tflint.Runner) error {
57+
resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{
58+
Blocks: []hclext.BlockSchema{
59+
{
60+
Type: "ingress",
61+
Body: &hclext.BodySchema{
62+
Attributes: []hclext.AttributeSchema{
63+
{Name: "protocol"},
64+
},
65+
},
66+
},
67+
{
68+
Type: "egress",
69+
Body: &hclext.BodySchema{
70+
Attributes: []hclext.AttributeSchema{
71+
{Name: "protocol"},
72+
},
73+
},
74+
},
75+
},
76+
}, nil)
77+
if err != nil {
78+
return err
79+
}
80+
81+
for _, resource := range resources.Blocks {
82+
for _, rule := range resource.Body.Blocks {
83+
attribute, exists := rule.Body.Attributes["protocol"]
84+
if !exists {
85+
continue
86+
}
87+
88+
var protocol string
89+
err := runner.EvaluateExpr(attribute.Expr, &protocol, nil)
90+
91+
err = runner.EnsureNoError(err, func() error {
92+
if _, err := strconv.Atoi(protocol); err == nil {
93+
return nil
94+
}
95+
if _, ok := r.protocols[strings.ToLower(protocol)]; !ok {
96+
runner.EmitIssue(
97+
r,
98+
fmt.Sprintf("\"%s\" is an invalid protocol.", protocol),
99+
attribute.Expr.Range(),
100+
)
101+
}
102+
return nil
103+
})
104+
if err != nil {
105+
return err
106+
}
107+
}
108+
}
109+
110+
return nil
111+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
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_AwsSecurityGroupInvalidProtocol(t *testing.T) {
11+
cases := []struct {
12+
Name string
13+
Content string
14+
Expected helper.Issues
15+
}{
16+
{
17+
Name: "valid protocols",
18+
Content: `
19+
resource "aws_security_group" "this" {
20+
ingress {
21+
protocol = "tcp"
22+
}
23+
egress {
24+
protocol = "-1"
25+
}
26+
}
27+
`,
28+
Expected: helper.Issues{},
29+
},
30+
{
31+
Name: "valid protocol with upper case (for Terraform v0.11 and older)",
32+
Content: `
33+
resource "aws_security_group" "this" {
34+
ingress {
35+
protocol = "TCP"
36+
}
37+
}
38+
`,
39+
Expected: helper.Issues{},
40+
},
41+
{
42+
Name: "invalid ingress.protocol",
43+
Content: `
44+
resource "aws_security_group" "this" {
45+
ingress {
46+
protocol = "http"
47+
}
48+
}
49+
`,
50+
Expected: helper.Issues{
51+
{
52+
Rule: NewAwsSecurityGroupInvalidProtocolRule(),
53+
Message: "\"http\" is an invalid protocol.",
54+
Range: hcl.Range{
55+
Filename: "resource.tf",
56+
Start: hcl.Pos{Line: 4, Column: 20},
57+
End: hcl.Pos{Line: 4, Column: 26},
58+
},
59+
},
60+
},
61+
},
62+
{
63+
Name: "invalid egress.protocol",
64+
Content: `
65+
resource "aws_security_group" "this" {
66+
egress {
67+
protocol = "https"
68+
}
69+
}
70+
`,
71+
Expected: helper.Issues{
72+
{
73+
Rule: NewAwsSecurityGroupInvalidProtocolRule(),
74+
Message: "\"https\" is an invalid protocol.",
75+
Range: hcl.Range{
76+
Filename: "resource.tf",
77+
Start: hcl.Pos{Line: 4, Column: 20},
78+
End: hcl.Pos{Line: 4, Column: 27},
79+
},
80+
},
81+
},
82+
},
83+
{
84+
Name: "invalid protocol with upper case (for Terraform v0.11 and older)",
85+
Content: `
86+
resource "aws_security_group" "this" {
87+
ingress {
88+
protocol = "HTTP"
89+
}
90+
}
91+
`,
92+
Expected: helper.Issues{
93+
{
94+
Rule: NewAwsSecurityGroupInvalidProtocolRule(),
95+
Message: "\"HTTP\" is an invalid protocol.",
96+
Range: hcl.Range{
97+
Filename: "resource.tf",
98+
Start: hcl.Pos{Line: 4, Column: 20},
99+
End: hcl.Pos{Line: 4, Column: 26},
100+
},
101+
},
102+
},
103+
},
104+
}
105+
106+
rule := NewAwsSecurityGroupInvalidProtocolRule()
107+
108+
for _, tc := range cases {
109+
runner := helper.TestRunner(t, map[string]string{"resource.tf": tc.Content})
110+
111+
if err := rule.Check(runner); err != nil {
112+
t.Fatalf("Unexpected error occurred: %s", err)
113+
}
114+
115+
helper.AssertIssues(t, tc.Expected, runner.Issues)
116+
}
117+
}

rules/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var manualRules = []tflint.Rule{
3838
NewAwsIAMGroupPolicyTooLongRule(),
3939
NewAwsAcmCertificateLifecycleRule(),
4040
NewAwsElasticBeanstalkEnvironmentInvalidNameFormatRule(),
41+
NewAwsSecurityGroupInvalidProtocolRule(),
4142
NewAwsSecurityGroupRuleInvalidProtocolRule(),
4243
}
4344

0 commit comments

Comments
 (0)