Skip to content

Commit e1e55da

Browse files
committed
fix: turn it into one rule for all write-only attributes
1 parent 5420040 commit e1e55da

6 files changed

+361
-147
lines changed

docs/rules/aws_secretsmanager_secret_version_secret_string.md renamed to docs/rules/aws_write_only_attributes.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
# aws_secretsmanager_secret_version_secret_string
1+
# aws_write_only_attributes
22

3-
Disallows using the `secret_string` attribute, in favor of the `secret_string_wo` attribute.
3+
Disallows using the normal attribute, in favor of an available write-only attribute. This is only valid for Terraform version 1.11.0 and later.
44

55
## Example
66

7+
In this example `aws_secretsmanager_secret_version` is used, but you can replace that with any resource with write-only attributes:
8+
79
```hcl
810
resource "aws_secretsmanager_secret_version" "test" {
911
secret_string = var.secret
@@ -14,7 +16,7 @@ resource "aws_secretsmanager_secret_version" "test" {
1416
$ tflint
1517
1 issue(s) found:
1618
17-
Warning: "secret_string" is a non-ephemeral attribute, which means this secret is stored in state. Please use "secret_string_wo". (aws_secretsmanager_secret_version_secret_string)
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)
1820
1921
on test.tf line 3:
2022
3: secret_string = var.secret
@@ -27,7 +29,7 @@ Saving secrets to state or plan files is a bad practice. It can cause serious se
2729

2830
## How To Fix
2931

30-
Replace the `secret_string` with `secret_string_wo` and `secret_string_wo_version`, either via using an ephemeral resource as source or using an ephemeral variable:
32+
Replace the `secret_string` with `secret_string_wo`, preferable via using an ephemeral resource as source or using an ephemeral variable:
3133

3234
```hcl
3335
ephemeral "random_password" "test" {

rules/aws_secretsmanager_secret_version_secret_string.go

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

rules/aws_secretsmanager_secret_version_secret_string_test.go

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

rules/aws_write_only_attributes.go

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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+
// AwsWriteOnlyAttributesRule checks if a write-only attribute is available for sensitive input attributes
13+
// It emits a warning if the normal attribute is used, as that is a non-ephemeral attribute and the secret value is stored in state
14+
type AwsWriteOnlyAttributesRule struct {
15+
tflint.DefaultRule
16+
17+
writeOnlyAttributes map[string]writeOnlyAttribute
18+
}
19+
20+
type writeOnlyAttribute struct {
21+
original string
22+
alternative string
23+
}
24+
25+
// NewAwsWriteOnlyAttributesRule returns new rule with default attributes
26+
func NewAwsWriteOnlyAttributesRule() *AwsWriteOnlyAttributesRule {
27+
writeOnlyAttributes := map[string]writeOnlyAttribute{
28+
"aws_secretsmanager_secret_version": {
29+
original: "secret_string",
30+
alternative: "secret_string_wo",
31+
},
32+
"aws_rds_cluster": {
33+
original: "master_password",
34+
alternative: "master_password_wo",
35+
},
36+
"aws_redshift_cluster": {
37+
original: "master_password",
38+
alternative: "master_password_wo",
39+
},
40+
"aws_docdb_cluster": {
41+
original: "master_password",
42+
alternative: "master_password_wo",
43+
},
44+
"aws_redshiftserverless_namespace": {
45+
original: "admin_password",
46+
alternative: "admin_password_wo",
47+
},
48+
"aws_ssm_parameter": {
49+
original: "value",
50+
alternative: "value_wo",
51+
},
52+
}
53+
return &AwsWriteOnlyAttributesRule{
54+
writeOnlyAttributes: writeOnlyAttributes,
55+
}
56+
}
57+
58+
// Name returns the rule name
59+
func (r *AwsWriteOnlyAttributesRule) Name() string {
60+
return "aws_write_only_attributes"
61+
}
62+
63+
// Enabled returns whether the rule is enabled by default
64+
func (r *AwsWriteOnlyAttributesRule) Enabled() bool {
65+
return true
66+
}
67+
68+
// Severity returns the rule severity
69+
func (r *AwsWriteOnlyAttributesRule) Severity() tflint.Severity {
70+
return tflint.WARNING
71+
}
72+
73+
// Link returns the rule reference link
74+
func (r *AwsWriteOnlyAttributesRule) Link() string {
75+
return project.ReferenceLink(r.Name())
76+
}
77+
78+
// Check checks whether the secret string attribute exists
79+
func (r *AwsWriteOnlyAttributesRule) Check(runner tflint.Runner) error {
80+
for resourceType, attributes := range r.writeOnlyAttributes {
81+
resources, err := runner.GetResourceContent(resourceType, &hclext.BodySchema{
82+
Attributes: []hclext.AttributeSchema{
83+
{Name: attributes.original},
84+
},
85+
}, nil)
86+
if err != nil {
87+
return err
88+
}
89+
90+
for _, resource := range resources.Blocks {
91+
attribute, exists := resource.Body.Attributes[attributes.original]
92+
if !exists {
93+
continue
94+
}
95+
96+
err := runner.EvaluateExpr(attribute.Expr, func(val cty.Value) error {
97+
if !val.IsNull() {
98+
if err := runner.EmitIssueWithFix(
99+
r,
100+
fmt.Sprintf("\"%s\" is a non-ephemeral attribute, which means this secret is stored in state. Please use write-only attribute \"%s\".", attributes.original, attributes.alternative),
101+
attribute.Expr.Range(),
102+
func(f tflint.Fixer) error {
103+
return f.ReplaceText(attribute.NameRange, attributes.alternative)
104+
},
105+
); err != nil {
106+
return fmt.Errorf("failed to call EmitIssueWithFix(): %w", err)
107+
}
108+
}
109+
return nil
110+
}, nil)
111+
if err != nil {
112+
return err
113+
}
114+
}
115+
}
116+
117+
return nil
118+
}

0 commit comments

Comments
 (0)