Skip to content

Commit 13da514

Browse files
committed
fix: disabled by default, DB Instance added, wo attribute -> wo argument rewrite
1 parent 3ff4e41 commit 13da514

File tree

5 files changed

+173
-145
lines changed

5 files changed

+173
-145
lines changed

docs/rules/aws_write_only_attributes.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
# aws_write_only_attributes
1+
# aws_write_only_arguments
22

33
Recommends using available [write-only arguments](https://developer.hashicorp.com/terraform/language/resources/ephemeral/write-only) instead of the original sensitive attribute. This is only valid for Terraform v1.11+.
44

55
## Example
66

7-
This example uses `aws_secretsmanager_secret_version`, but the rule applies to all resources with write-only attributes:
7+
This example uses `aws_secretsmanager_secret_version`, but the rule applies to all resources with write-only arguments:
88

99
```hcl
1010
resource "aws_secretsmanager_secret_version" "test" {
@@ -16,7 +16,7 @@ resource "aws_secretsmanager_secret_version" "test" {
1616
$ tflint
1717
1 issue(s) found:
1818
19-
Warning: [Fixable] "secret_string" is a non-ephemeral attribute, which means this secret is stored in state. Please use "secret_string_wo". (aws_write_only_attributes)
19+
Warning: [Fixable] "secret_string" is a non-ephemeral attribute, which means this secret is stored in state. Please use "secret_string_wo". (aws_write_only_arguments)
2020
2121
on test.tf line 3:
2222
3: secret_string = var.secret
@@ -48,7 +48,7 @@ resource "aws_secretsmanager_secret_version" "test" {
4848
```hcl
4949
variable "test" {
5050
type = string
51-
ephemeral = true # Optional, non-ephemeral values can also be used for write-only attributes
51+
ephemeral = true # Optional, non-ephemeral values can also be used for write-only arguments
5252
description = "Input variable for a secret"
5353
}
5454

rules/aws_write_only_argurments.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package rules
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/terraform-linters/tflint-plugin-sdk/hclext"
7+
"github.com/terraform-linters/tflint-plugin-sdk/tflint"
8+
"github.com/terraform-linters/tflint-ruleset-aws/project"
9+
"github.com/zclconf/go-cty/cty"
10+
)
11+
12+
// AwsWriteOnlyArgumentsRule checks if a write-only argument is available for sensitive input attributes
13+
type AwsWriteOnlyArgumentsRule struct {
14+
tflint.DefaultRule
15+
16+
writeOnlyArguments map[string]writeOnlyArgument
17+
}
18+
19+
type writeOnlyArgument struct {
20+
originalAttribute string
21+
writeOnlyAlternative string
22+
}
23+
24+
// NewAwsWriteOnlyArgumentsRule returns new rule with default attributes
25+
func NewAwsWriteOnlyArgumentsRule() *AwsWriteOnlyArgumentsRule {
26+
writeOnlyArguments := map[string]writeOnlyArgument{
27+
"aws_secretsmanager_secret_version": {
28+
originalAttribute: "secret_string",
29+
writeOnlyAlternative: "secret_string_wo",
30+
},
31+
"aws_rds_cluster": {
32+
originalAttribute: "master_password",
33+
writeOnlyAlternative: "master_password_wo",
34+
},
35+
"aws_db_instance": {
36+
originalAttribute: "password",
37+
writeOnlyAlternative: "password_wo",
38+
},
39+
"aws_redshift_cluster": {
40+
originalAttribute: "master_password",
41+
writeOnlyAlternative: "master_password_wo",
42+
},
43+
"aws_docdb_cluster": {
44+
originalAttribute: "master_password",
45+
writeOnlyAlternative: "master_password_wo",
46+
},
47+
"aws_redshiftserverless_namespace": {
48+
originalAttribute: "admin_password",
49+
writeOnlyAlternative: "admin_password_wo",
50+
},
51+
"aws_ssm_parameter": {
52+
originalAttribute: "value",
53+
writeOnlyAlternative: "value_wo",
54+
},
55+
}
56+
return &AwsWriteOnlyArgumentsRule{
57+
writeOnlyArguments: writeOnlyArguments,
58+
}
59+
}
60+
61+
// Name returns the rule name
62+
func (r *AwsWriteOnlyArgumentsRule) Name() string {
63+
return "aws_write_only_arguments"
64+
}
65+
66+
// Enabled returns whether the rule is enabled by default
67+
func (r *AwsWriteOnlyArgumentsRule) Enabled() bool {
68+
return false
69+
}
70+
71+
// Severity returns the rule severity
72+
func (r *AwsWriteOnlyArgumentsRule) Severity() tflint.Severity {
73+
return tflint.WARNING
74+
}
75+
76+
// Link returns the rule reference link
77+
func (r *AwsWriteOnlyArgumentsRule) Link() string {
78+
return project.ReferenceLink(r.Name())
79+
}
80+
81+
// Check checks whether the sensitive attribute exists
82+
func (r *AwsWriteOnlyArgumentsRule) Check(runner tflint.Runner) error {
83+
for resourceType, attributes := range r.writeOnlyArguments {
84+
resources, err := runner.GetResourceContent(resourceType, &hclext.BodySchema{
85+
Attributes: []hclext.AttributeSchema{
86+
{Name: attributes.originalAttribute},
87+
},
88+
}, nil)
89+
if err != nil {
90+
return err
91+
}
92+
93+
for _, resource := range resources.Blocks {
94+
attribute, exists := resource.Body.Attributes[attributes.originalAttribute]
95+
if !exists {
96+
continue
97+
}
98+
99+
err := runner.EvaluateExpr(attribute.Expr, func(val cty.Value) error {
100+
if !val.IsNull() {
101+
if err := runner.EmitIssueWithFix(
102+
r,
103+
fmt.Sprintf("\"%s\" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only argument \"%s\".", attributes.originalAttribute, attributes.writeOnlyAlternative),
104+
attribute.Expr.Range(),
105+
func(f tflint.Fixer) error {
106+
return f.ReplaceText(attribute.NameRange, attributes.writeOnlyAlternative)
107+
},
108+
); err != nil {
109+
return fmt.Errorf("failed to call EmitIssueWithFix(): %w", err)
110+
}
111+
}
112+
return nil
113+
}, nil)
114+
if err != nil {
115+
return err
116+
}
117+
}
118+
}
119+
120+
return nil
121+
}
Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ resource "aws_secretsmanager_secret_version" "test" {
2323
`,
2424
Expected: helper.Issues{
2525
{
26-
Rule: NewAwsWriteOnlyAttributesRule(),
27-
Message: `"secret_string" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only attribute "secret_string_wo".`,
26+
Rule: NewAwsWriteOnlyArgumentsRule(),
27+
Message: `"secret_string" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only argument "secret_string_wo".`,
2828
Range: hcl.Range{
2929
Filename: "resource.tf",
3030
Start: hcl.Pos{Line: 3, Column: 19},
@@ -56,8 +56,8 @@ resource "aws_rds_cluster" "test" {
5656
`,
5757
Expected: helper.Issues{
5858
{
59-
Rule: NewAwsWriteOnlyAttributesRule(),
60-
Message: `"master_password" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only attribute "master_password_wo". Alternatively, you can use "manage_master_user_password" to manage the secret in an different way.`,
59+
Rule: NewAwsWriteOnlyArgumentsRule(),
60+
Message: `"master_password" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only argument "master_password_wo".`,
6161
Range: hcl.Range{
6262
Filename: "resource.tf",
6363
Start: hcl.Pos{Line: 3, Column: 21},
@@ -77,6 +77,40 @@ resource "aws_rds_cluster" "test" {
7777
resource "aws_rds_cluster" "test" {
7878
master_password_wo = "test"
7979
}
80+
`,
81+
Expected: helper.Issues{},
82+
},
83+
84+
{
85+
Name: "basic aws_db_instance",
86+
Content: `
87+
resource "aws_db_instance" "test" {
88+
password = "test"
89+
}
90+
`,
91+
Expected: helper.Issues{
92+
{
93+
Rule: NewAwsWriteOnlyArgumentsRule(),
94+
Message: `"password" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only argument "password_wo".`,
95+
Range: hcl.Range{
96+
Filename: "resource.tf",
97+
Start: hcl.Pos{Line: 3, Column: 14},
98+
End: hcl.Pos{Line: 3, Column: 20},
99+
},
100+
},
101+
},
102+
Fixed: `
103+
resource "aws_db_instance" "test" {
104+
password_wo = "test"
105+
}
106+
`,
107+
},
108+
{
109+
Name: "everything is fine aws_db_instance",
110+
Content: `
111+
resource "aws_db_instance" "test" {
112+
password_wo = "test"
113+
}
80114
`,
81115
Expected: helper.Issues{},
82116
},
@@ -89,8 +123,8 @@ resource "aws_redshift_cluster" "test" {
89123
`,
90124
Expected: helper.Issues{
91125
{
92-
Rule: NewAwsWriteOnlyAttributesRule(),
93-
Message: `"master_password" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only attribute "master_password_wo". Alternatively, you can use "manage_master_password" to manage the secret in an different way.`,
126+
Rule: NewAwsWriteOnlyArgumentsRule(),
127+
Message: `"master_password" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only argument "master_password_wo".`,
94128
Range: hcl.Range{
95129
Filename: "resource.tf",
96130
Start: hcl.Pos{Line: 3, Column: 21},
@@ -122,8 +156,8 @@ resource "aws_docdb_cluster" "test" {
122156
`,
123157
Expected: helper.Issues{
124158
{
125-
Rule: NewAwsWriteOnlyAttributesRule(),
126-
Message: `"master_password" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only attribute "master_password_wo".`,
159+
Rule: NewAwsWriteOnlyArgumentsRule(),
160+
Message: `"master_password" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only argument "master_password_wo".`,
127161
Range: hcl.Range{
128162
Filename: "resource.tf",
129163
Start: hcl.Pos{Line: 3, Column: 21},
@@ -156,8 +190,8 @@ resource "aws_redshiftserverless_namespace" "test" {
156190
`,
157191
Expected: helper.Issues{
158192
{
159-
Rule: NewAwsWriteOnlyAttributesRule(),
160-
Message: `"admin_password" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only attribute "admin_password_wo". Alternatively, you can use "manage_admin_password" to manage the secret in an different way.`,
193+
Rule: NewAwsWriteOnlyArgumentsRule(),
194+
Message: `"admin_password" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only argument "admin_password_wo".`,
161195
Range: hcl.Range{
162196
Filename: "resource.tf",
163197
Start: hcl.Pos{Line: 3, Column: 20},
@@ -189,8 +223,8 @@ resource "aws_ssm_parameter" "test" {
189223
`,
190224
Expected: helper.Issues{
191225
{
192-
Rule: NewAwsWriteOnlyAttributesRule(),
193-
Message: `"value" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only attribute "value_wo".`,
226+
Rule: NewAwsWriteOnlyArgumentsRule(),
227+
Message: `"value" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only argument "value_wo".`,
194228
Range: hcl.Range{
195229
Filename: "resource.tf",
196230
Start: hcl.Pos{Line: 3, Column: 11},
@@ -216,7 +250,7 @@ resource "aws_ssm_parameter" "test" {
216250
},
217251
}
218252

219-
rule := NewAwsWriteOnlyAttributesRule()
253+
rule := NewAwsWriteOnlyArgumentsRule()
220254

221255
for _, tc := range cases {
222256
filename := "resource.tf"

rules/aws_write_only_attributes.go

Lines changed: 0 additions & 127 deletions
This file was deleted.

0 commit comments

Comments
 (0)