Skip to content

Commit 8212ff6

Browse files
committed
Generate validation rules for tag keys and values
Extend the generator to traverse Smithy model structure types (Tags → Tag → TagKey/TagValue) to extract pattern and length constraints for map attributes. - Add shape traversal functions to follow list/map/structure types - Add generateMapRuleFile for map[string]string validation rules - Add map_rule.go.tmpl template for generated map rules - Handle PCRE negative lookaheads by stripping (Go regexp incompatible) - Skip malformed Unicode patterns with error message Generates 149 new tag validation rules plus additional map rules for environment_variables, attributes, labels, etc. Closes #1019
1 parent fced415 commit 8212ff6

File tree

165 files changed

+19331
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

165 files changed

+19331
-1
lines changed

docs/rules/README.md

Lines changed: 160 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// This file generated by `generator/`. DO NOT EDIT
2+
3+
package models
4+
5+
import (
6+
"fmt"
7+
"regexp"
8+
9+
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
10+
"github.com/terraform-linters/tflint-plugin-sdk/logger"
11+
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
12+
)
13+
14+
// AwsAcmCertificateInvalidTagsRule checks the pattern is valid
15+
type AwsAcmCertificateInvalidTagsRule struct {
16+
tflint.DefaultRule
17+
18+
resourceType string
19+
attributeName string
20+
keyMax int
21+
keyMin int
22+
keyPattern *regexp.Regexp
23+
valueMax int
24+
valuePattern *regexp.Regexp
25+
}
26+
27+
// NewAwsAcmCertificateInvalidTagsRule returns new rule with default attributes
28+
func NewAwsAcmCertificateInvalidTagsRule() *AwsAcmCertificateInvalidTagsRule {
29+
return &AwsAcmCertificateInvalidTagsRule{
30+
resourceType: "aws_acm_certificate",
31+
attributeName: "tags",
32+
keyMax: 128,
33+
keyMin: 1,
34+
keyPattern: regexp.MustCompile(`^[\p{L}\p{Z}\p{N}_.:\/=+\-@]*$`),
35+
valueMax: 256,
36+
valuePattern: regexp.MustCompile(`^[\p{L}\p{Z}\p{N}_.:\/=+\-@]*$`),
37+
}
38+
}
39+
40+
// Name returns the rule name
41+
func (r *AwsAcmCertificateInvalidTagsRule) Name() string {
42+
return "aws_acm_certificate_invalid_tags"
43+
}
44+
45+
// Enabled returns whether the rule is enabled by default
46+
func (r *AwsAcmCertificateInvalidTagsRule) Enabled() bool {
47+
return true
48+
}
49+
50+
// Severity returns the rule severity
51+
func (r *AwsAcmCertificateInvalidTagsRule) Severity() tflint.Severity {
52+
return tflint.ERROR
53+
}
54+
55+
// Link returns the rule reference link
56+
func (r *AwsAcmCertificateInvalidTagsRule) Link() string {
57+
return ""
58+
}
59+
60+
// Check checks the pattern is valid
61+
func (r *AwsAcmCertificateInvalidTagsRule) Check(runner tflint.Runner) error {
62+
logger.Trace("Check `%s` rule", r.Name())
63+
64+
resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{
65+
Attributes: []hclext.AttributeSchema{
66+
{Name: r.attributeName},
67+
},
68+
}, nil)
69+
if err != nil {
70+
return err
71+
}
72+
73+
for _, resource := range resources.Blocks {
74+
attribute, exists := resource.Body.Attributes[r.attributeName]
75+
if !exists {
76+
continue
77+
}
78+
79+
err := runner.EvaluateExpr(attribute.Expr, func(val map[string]string) error {
80+
for k, v := range val {
81+
if len(k) > r.keyMax {
82+
runner.EmitIssue(
83+
r,
84+
fmt.Sprintf("tag key %q must be 128 characters or less", truncateLongMessage(k)),
85+
attribute.Expr.Range(),
86+
)
87+
}
88+
if len(k) < r.keyMin {
89+
runner.EmitIssue(
90+
r,
91+
fmt.Sprintf("tag key %q must be 1 characters or higher", truncateLongMessage(k)),
92+
attribute.Expr.Range(),
93+
)
94+
}
95+
if !r.keyPattern.MatchString(k) {
96+
runner.EmitIssue(
97+
r,
98+
fmt.Sprintf(`tag key %q does not match valid pattern %s`, truncateLongMessage(k), `^[\p{L}\p{Z}\p{N}_.:\/=+\-@]*$`),
99+
attribute.Expr.Range(),
100+
)
101+
}
102+
if len(v) > r.valueMax {
103+
runner.EmitIssue(
104+
r,
105+
fmt.Sprintf("tag value for key %q must be 256 characters or less", truncateLongMessage(k)),
106+
attribute.Expr.Range(),
107+
)
108+
}
109+
if !r.valuePattern.MatchString(v) {
110+
runner.EmitIssue(
111+
r,
112+
fmt.Sprintf(`tag value %q for key %q does not match valid pattern %s`, truncateLongMessage(v), truncateLongMessage(k), `^[\p{L}\p{Z}\p{N}_.:\/=+\-@]*$`),
113+
attribute.Expr.Range(),
114+
)
115+
}
116+
}
117+
return nil
118+
}, nil)
119+
if err != nil {
120+
return err
121+
}
122+
}
123+
124+
return nil
125+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// This file generated by `generator/`. DO NOT EDIT
2+
3+
package models
4+
5+
import (
6+
"fmt"
7+
"regexp"
8+
9+
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
10+
"github.com/terraform-linters/tflint-plugin-sdk/logger"
11+
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
12+
)
13+
14+
// AwsALBInvalidTagsRule checks the pattern is valid
15+
type AwsALBInvalidTagsRule struct {
16+
tflint.DefaultRule
17+
18+
resourceType string
19+
attributeName string
20+
keyMax int
21+
keyMin int
22+
keyPattern *regexp.Regexp
23+
valueMax int
24+
valuePattern *regexp.Regexp
25+
}
26+
27+
// NewAwsALBInvalidTagsRule returns new rule with default attributes
28+
func NewAwsALBInvalidTagsRule() *AwsALBInvalidTagsRule {
29+
return &AwsALBInvalidTagsRule{
30+
resourceType: "aws_alb",
31+
attributeName: "tags",
32+
keyMax: 128,
33+
keyMin: 1,
34+
keyPattern: regexp.MustCompile(`^([\p{L}\p{Z}\p{N}_.:/=+\-@]*)$`),
35+
valueMax: 256,
36+
valuePattern: regexp.MustCompile(`^([\p{L}\p{Z}\p{N}_.:/=+\-@]*)$`),
37+
}
38+
}
39+
40+
// Name returns the rule name
41+
func (r *AwsALBInvalidTagsRule) Name() string {
42+
return "aws_alb_invalid_tags"
43+
}
44+
45+
// Enabled returns whether the rule is enabled by default
46+
func (r *AwsALBInvalidTagsRule) Enabled() bool {
47+
return true
48+
}
49+
50+
// Severity returns the rule severity
51+
func (r *AwsALBInvalidTagsRule) Severity() tflint.Severity {
52+
return tflint.ERROR
53+
}
54+
55+
// Link returns the rule reference link
56+
func (r *AwsALBInvalidTagsRule) Link() string {
57+
return ""
58+
}
59+
60+
// Check checks the pattern is valid
61+
func (r *AwsALBInvalidTagsRule) Check(runner tflint.Runner) error {
62+
logger.Trace("Check `%s` rule", r.Name())
63+
64+
resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{
65+
Attributes: []hclext.AttributeSchema{
66+
{Name: r.attributeName},
67+
},
68+
}, nil)
69+
if err != nil {
70+
return err
71+
}
72+
73+
for _, resource := range resources.Blocks {
74+
attribute, exists := resource.Body.Attributes[r.attributeName]
75+
if !exists {
76+
continue
77+
}
78+
79+
err := runner.EvaluateExpr(attribute.Expr, func(val map[string]string) error {
80+
for k, v := range val {
81+
if len(k) > r.keyMax {
82+
runner.EmitIssue(
83+
r,
84+
fmt.Sprintf("tag key %q must be 128 characters or less", truncateLongMessage(k)),
85+
attribute.Expr.Range(),
86+
)
87+
}
88+
if len(k) < r.keyMin {
89+
runner.EmitIssue(
90+
r,
91+
fmt.Sprintf("tag key %q must be 1 characters or higher", truncateLongMessage(k)),
92+
attribute.Expr.Range(),
93+
)
94+
}
95+
if !r.keyPattern.MatchString(k) {
96+
runner.EmitIssue(
97+
r,
98+
fmt.Sprintf(`tag key %q does not match valid pattern %s`, truncateLongMessage(k), `^([\p{L}\p{Z}\p{N}_.:/=+\-@]*)$`),
99+
attribute.Expr.Range(),
100+
)
101+
}
102+
if len(v) > r.valueMax {
103+
runner.EmitIssue(
104+
r,
105+
fmt.Sprintf("tag value for key %q must be 256 characters or less", truncateLongMessage(k)),
106+
attribute.Expr.Range(),
107+
)
108+
}
109+
if !r.valuePattern.MatchString(v) {
110+
runner.EmitIssue(
111+
r,
112+
fmt.Sprintf(`tag value %q for key %q does not match valid pattern %s`, truncateLongMessage(v), truncateLongMessage(k), `^([\p{L}\p{Z}\p{N}_.:/=+\-@]*)$`),
113+
attribute.Expr.Range(),
114+
)
115+
}
116+
}
117+
return nil
118+
}, nil)
119+
if err != nil {
120+
return err
121+
}
122+
}
123+
124+
return nil
125+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// This file generated by `generator/`. DO NOT EDIT
2+
3+
package models
4+
5+
import (
6+
"fmt"
7+
"regexp"
8+
9+
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
10+
"github.com/terraform-linters/tflint-plugin-sdk/logger"
11+
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
12+
)
13+
14+
// AwsALBTargetGroupInvalidTagsRule checks the pattern is valid
15+
type AwsALBTargetGroupInvalidTagsRule struct {
16+
tflint.DefaultRule
17+
18+
resourceType string
19+
attributeName string
20+
keyMax int
21+
keyMin int
22+
keyPattern *regexp.Regexp
23+
valueMax int
24+
valuePattern *regexp.Regexp
25+
}
26+
27+
// NewAwsALBTargetGroupInvalidTagsRule returns new rule with default attributes
28+
func NewAwsALBTargetGroupInvalidTagsRule() *AwsALBTargetGroupInvalidTagsRule {
29+
return &AwsALBTargetGroupInvalidTagsRule{
30+
resourceType: "aws_alb_target_group",
31+
attributeName: "tags",
32+
keyMax: 128,
33+
keyMin: 1,
34+
keyPattern: regexp.MustCompile(`^([\p{L}\p{Z}\p{N}_.:/=+\-@]*)$`),
35+
valueMax: 256,
36+
valuePattern: regexp.MustCompile(`^([\p{L}\p{Z}\p{N}_.:/=+\-@]*)$`),
37+
}
38+
}
39+
40+
// Name returns the rule name
41+
func (r *AwsALBTargetGroupInvalidTagsRule) Name() string {
42+
return "aws_alb_target_group_invalid_tags"
43+
}
44+
45+
// Enabled returns whether the rule is enabled by default
46+
func (r *AwsALBTargetGroupInvalidTagsRule) Enabled() bool {
47+
return true
48+
}
49+
50+
// Severity returns the rule severity
51+
func (r *AwsALBTargetGroupInvalidTagsRule) Severity() tflint.Severity {
52+
return tflint.ERROR
53+
}
54+
55+
// Link returns the rule reference link
56+
func (r *AwsALBTargetGroupInvalidTagsRule) Link() string {
57+
return ""
58+
}
59+
60+
// Check checks the pattern is valid
61+
func (r *AwsALBTargetGroupInvalidTagsRule) Check(runner tflint.Runner) error {
62+
logger.Trace("Check `%s` rule", r.Name())
63+
64+
resources, err := runner.GetResourceContent(r.resourceType, &hclext.BodySchema{
65+
Attributes: []hclext.AttributeSchema{
66+
{Name: r.attributeName},
67+
},
68+
}, nil)
69+
if err != nil {
70+
return err
71+
}
72+
73+
for _, resource := range resources.Blocks {
74+
attribute, exists := resource.Body.Attributes[r.attributeName]
75+
if !exists {
76+
continue
77+
}
78+
79+
err := runner.EvaluateExpr(attribute.Expr, func(val map[string]string) error {
80+
for k, v := range val {
81+
if len(k) > r.keyMax {
82+
runner.EmitIssue(
83+
r,
84+
fmt.Sprintf("tag key %q must be 128 characters or less", truncateLongMessage(k)),
85+
attribute.Expr.Range(),
86+
)
87+
}
88+
if len(k) < r.keyMin {
89+
runner.EmitIssue(
90+
r,
91+
fmt.Sprintf("tag key %q must be 1 characters or higher", truncateLongMessage(k)),
92+
attribute.Expr.Range(),
93+
)
94+
}
95+
if !r.keyPattern.MatchString(k) {
96+
runner.EmitIssue(
97+
r,
98+
fmt.Sprintf(`tag key %q does not match valid pattern %s`, truncateLongMessage(k), `^([\p{L}\p{Z}\p{N}_.:/=+\-@]*)$`),
99+
attribute.Expr.Range(),
100+
)
101+
}
102+
if len(v) > r.valueMax {
103+
runner.EmitIssue(
104+
r,
105+
fmt.Sprintf("tag value for key %q must be 256 characters or less", truncateLongMessage(k)),
106+
attribute.Expr.Range(),
107+
)
108+
}
109+
if !r.valuePattern.MatchString(v) {
110+
runner.EmitIssue(
111+
r,
112+
fmt.Sprintf(`tag value %q for key %q does not match valid pattern %s`, truncateLongMessage(v), truncateLongMessage(k), `^([\p{L}\p{Z}\p{N}_.:/=+\-@]*)$`),
113+
attribute.Expr.Range(),
114+
)
115+
}
116+
}
117+
return nil
118+
}, nil)
119+
if err != nil {
120+
return err
121+
}
122+
}
123+
124+
return nil
125+
}

0 commit comments

Comments
 (0)