Skip to content

Commit 0198f5e

Browse files
authored
Mask values in assignment expressions (#17)
1 parent 0c7da96 commit 0198f5e

File tree

2 files changed

+97
-2
lines changed

2 files changed

+97
-2
lines changed

main.go

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,19 @@ type match struct {
2424
postfix string
2525
}
2626

27+
type keyValueMatch struct {
28+
leadingWhitespace string
29+
property string
30+
trailingWhitespaceBefore string
31+
trailingWhitespaceAfter string
32+
oldValue string
33+
}
34+
2735
type expression struct {
2836
planStatusRegex *regexp.Regexp
2937
reTfPlanLine *regexp.Regexp
3038
reTfPlanCurrentResource *regexp.Regexp
39+
reMapKeyPair *regexp.Regexp
3140
resourceIndex int
3241
assign string
3342
operator string
@@ -58,6 +67,9 @@ var versionedExpressions = map[string]expression{
5867
reTfPlanCurrentResource: regexp.MustCompile(
5968
"^([~/+-]+) (.*?) +(.*)$",
6069
),
70+
reMapKeyPair: regexp.MustCompile(
71+
"(?i)^(\\s+(?:[~+-] )?)\"(.*)\"(\\s+)=(\\s+)\"(.*)\"$",
72+
),
6173
resourceIndex: 2,
6274
assign: ":",
6375
operator: "=>",
@@ -72,6 +84,9 @@ var versionedExpressions = map[string]expression{
7284
reTfPlanCurrentResource: regexp.MustCompile(
7385
"^([~/+-]+) (.*?) +(.*) (.*) (.*)$",
7486
),
87+
reMapKeyPair: regexp.MustCompile(
88+
"(?i)^(\\s+(?:[~+-] )?)\"(.*)\"(\\s+)=(\\s+)\"(.*)\"$",
89+
),
7590
resourceIndex: 3,
7691
assign: "=",
7792
operator: "->",
@@ -90,14 +105,14 @@ func main() {
90105
var tfmaskResourceRegex = getEnv("TFMASK_RESOURCES_REGEX",
91106
"(?i)^(random_id|random_string).*$")
92107

93-
// Default to tf 0.11, but users can override
108+
// Default to tf 0.12, but users can override
94109
var tfenv = getEnv("TFENV", "0.12")
95110

96111
reTfValues := regexp.MustCompile(tfmaskValuesRegex)
97112
reTfResource := regexp.MustCompile(tfmaskResourceRegex)
98113
scanner := bufio.NewScanner(os.Stdin)
99114
versionedExpressions := versionedExpressions[tfenv]
100-
// initialise currentResource once before scanning
115+
// initialize currentResource once before scanning
101116
currentResource := ""
102117
for scanner.Scan() {
103118
line := scanner.Text()
@@ -125,6 +140,7 @@ func getCurrentResource(expression expression, currentResource, line string) str
125140
match := reTfApplyCurrentResource.FindStringSubmatch(line)
126141
currentResource = match[1]
127142
}
143+
128144
return currentResource
129145
}
130146

@@ -138,6 +154,9 @@ func processLine(expression expression, reTfResource,
138154
line = planLine(expression.reTfPlanLine, reTfResource, reTfValues,
139155
currentResource, tfmaskChar, expression.assign,
140156
expression.operator, line)
157+
} else if expression.reMapKeyPair.MatchString(line) {
158+
line = assignmentLine(expression.reMapKeyPair, reTfValues,
159+
tfmaskChar, line)
141160
}
142161
return line
143162
}
@@ -170,6 +189,17 @@ func matchFromLine(reTfPlanLine *regexp.Regexp, line string) match {
170189
}
171190
}
172191

192+
func matchFromAssignment(reMapKeyPair *regexp.Regexp, line string) keyValueMatch {
193+
subMatch := reMapKeyPair.FindStringSubmatch(line)
194+
return keyValueMatch{
195+
leadingWhitespace: subMatch[1],
196+
property: subMatch[2],
197+
trailingWhitespaceBefore: subMatch[3],
198+
trailingWhitespaceAfter: subMatch[4],
199+
oldValue: subMatch[5],
200+
}
201+
}
202+
173203
func planLine(reTfPlanLine, reTfResource, reTfValues *regexp.Regexp,
174204
currentResource, tfmaskChar, assign, operator, line string) string {
175205
match := matchFromLine(reTfPlanLine, line)
@@ -188,6 +218,20 @@ func planLine(reTfPlanLine, reTfResource, reTfValues *regexp.Regexp,
188218
return line
189219
}
190220

221+
func assignmentLine(reMapKeyPair, reTfValues *regexp.Regexp, tfmaskChar, line string) string {
222+
match := matchFromAssignment(reMapKeyPair, line)
223+
if reTfValues.MatchString(match.property) {
224+
maskedValue := maskValue(match.oldValue, tfmaskChar)
225+
line = fmt.Sprintf("%v\"%v\"%v=%v\"%v\"",
226+
match.leadingWhitespace,
227+
match.property,
228+
match.trailingWhitespaceBefore,
229+
match.trailingWhitespaceAfter,
230+
maskedValue)
231+
}
232+
return line
233+
}
234+
191235
func maskValue(value, tfmaskChar string) string {
192236
exclusions := []string{"sensitive", "computed", "<computed",
193237
"known after apply"}

main_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,54 @@ func TestMaskValue(t *testing.T) {
167167
}
168168
}
169169
}
170+
171+
var assignmentTests = []struct {
172+
line string
173+
expectedResult string
174+
minorVersion string
175+
}{
176+
// tf 0.12 ------------------------------------
177+
{
178+
" + \"foo_secret\" = \"123456\"",
179+
" + \"foo_secret\" = \"******\"",
180+
"0.12",
181+
},
182+
{
183+
" - \"foo_secret\" = \"123456\"",
184+
" - \"foo_secret\" = \"******\"",
185+
"0.12",
186+
},
187+
{
188+
" ~ \"foo_secret\" = \"123456\"",
189+
" ~ \"foo_secret\" = \"******\"",
190+
"0.12",
191+
},
192+
{
193+
" ~ \"foo\" = \"123456\"",
194+
" ~ \"foo\" = \"123456\"",
195+
"0.12",
196+
},
197+
{
198+
" \"foo_secret\" = \"123456\"",
199+
" \"foo_secret\" = \"******\"",
200+
"0.12",
201+
},
202+
}
203+
204+
func TestAssignmentLine(t *testing.T) {
205+
// Character used to mask sensitive output
206+
var tfmaskChar = "*"
207+
// Pattern representing sensitive output
208+
var tfmaskValuesRegex = "(?i)^.*(oauth|secret|token|password|key|result|id).*$"
209+
reTfValues := regexp.MustCompile(tfmaskValuesRegex)
210+
211+
for _, assignmentTest := range assignmentTests {
212+
result := assignmentLine(
213+
versionedExpressions[assignmentTest.minorVersion].reMapKeyPair,
214+
reTfValues, tfmaskChar,
215+
assignmentTest.line)
216+
if result != assignmentTest.expectedResult {
217+
t.Errorf("Got %s, want %s", result, assignmentTest.expectedResult)
218+
}
219+
}
220+
}

0 commit comments

Comments
 (0)