Skip to content

Commit 24ca184

Browse files
authored
Merge pull request #407 from majewsky/fix-redact-secrets-with-stringdata
diff: fix secret redaction for secrets with stringData
2 parents 59f7768 + 6dc2b07 commit 24ca184

File tree

2 files changed

+141
-0
lines changed

2 files changed

+141
-0
lines changed

diff/diff.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,12 +163,29 @@ func redactSecrets(old, new *manifest.MappingResult) {
163163
if err := yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(old.Content)).Decode(&oldSecret); err != nil {
164164
old.Content = fmt.Sprintf("Error parsing old secret: %s", err)
165165
}
166+
//if we have a Secret containing `stringData`, apply the same
167+
//transformation that the apiserver would do with it (this protects
168+
//stringData keys from being overwritten down below)
169+
if len(oldSecret.StringData) > 0 && oldSecret.Data == nil {
170+
oldSecret.Data = make(map[string][]byte, len(oldSecret.StringData))
171+
}
172+
for k, v := range oldSecret.StringData {
173+
oldSecret.Data[k] = []byte(v)
174+
}
166175
}
167176
if new != nil {
168177
if err := yaml.NewYAMLToJSONDecoder(bytes.NewBufferString(new.Content)).Decode(&newSecret); err != nil {
169178
new.Content = fmt.Sprintf("Error parsing new secret: %s", err)
170179
}
180+
//same as above
181+
if len(newSecret.StringData) > 0 && newSecret.Data == nil {
182+
newSecret.Data = make(map[string][]byte, len(newSecret.StringData))
183+
}
184+
for k, v := range newSecret.StringData {
185+
newSecret.Data[k] = []byte(v)
186+
}
171187
}
188+
172189
if old != nil {
173190
oldSecret.StringData = make(map[string]string, len(oldSecret.Data))
174191
for k, v := range oldSecret.Data {
@@ -189,6 +206,7 @@ func redactSecrets(old, new *manifest.MappingResult) {
189206
}
190207
}
191208
}
209+
192210
// remove Data field now that we are using StringData for serialization
193211
var buf bytes.Buffer
194212
if old != nil {

diff/diff_test.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ func assertDiff(t *testing.T, before, after string, context int, stripTrailingCR
169169
}
170170

171171
func TestManifests(t *testing.T) {
172+
ansi.DisableColors(true)
173+
172174
specBeta := map[string]*manifest.MappingResult{
173175
"default, nginx, Deployment (apps)": {
174176

@@ -461,3 +463,124 @@ Plan: 0 to add, 1 to change, 0 to destroy.
461463
require.Equal(t, "Resource name: nginx\n", buf1.String())
462464
})
463465
}
466+
467+
func TestManifestsWithRedactedSecrets(t *testing.T) {
468+
ansi.DisableColors(true)
469+
470+
specSecretWithByteData := map[string]*manifest.MappingResult{
471+
"default, foobar, Secret (v1)": {
472+
Name: "default, foobar, Secret (v1)",
473+
Kind: "Secret",
474+
Content: `
475+
apiVersion: v1
476+
kind: Secret
477+
metadata:
478+
name: foobar
479+
type: Opaque
480+
data:
481+
key1: dmFsdWUx
482+
key2: dmFsdWUy
483+
key3: dmFsdWUz
484+
`,
485+
}}
486+
487+
specSecretWithByteDataChanged := map[string]*manifest.MappingResult{
488+
"default, foobar, Secret (v1)": {
489+
Name: "default, foobar, Secret (v1)",
490+
Kind: "Secret",
491+
Content: `
492+
apiVersion: v1
493+
kind: Secret
494+
metadata:
495+
name: foobar
496+
type: Opaque
497+
data:
498+
key1: dmFsdWUxY2hhbmdlZA==
499+
key2: dmFsdWUy
500+
key4: dmFsdWU0
501+
`,
502+
}}
503+
504+
specSecretWithStringData := map[string]*manifest.MappingResult{
505+
"default, foobar, Secret (v1)": {
506+
Name: "default, foobar, Secret (v1)",
507+
Kind: "Secret",
508+
Content: `
509+
apiVersion: v1
510+
kind: Secret
511+
metadata:
512+
name: foobar
513+
type: Opaque
514+
stringData:
515+
key1: value1
516+
key2: value2
517+
key3: value3
518+
`,
519+
}}
520+
521+
specSecretWithStringDataChanged := map[string]*manifest.MappingResult{
522+
"default, foobar, Secret (v1)": {
523+
Name: "default, foobar, Secret (v1)",
524+
Kind: "Secret",
525+
Content: `
526+
apiVersion: v1
527+
kind: Secret
528+
metadata:
529+
name: foobar
530+
type: Opaque
531+
stringData:
532+
key1: value1changed
533+
key2: value2
534+
key4: value4
535+
`,
536+
}}
537+
538+
t.Run("OnChangeSecretWithByteData", func(t *testing.T) {
539+
var buf1 bytes.Buffer
540+
diffOptions := Options{"diff", 10, false, false, []string{}, 0.5} //NOTE: ShowSecrets = false
541+
542+
if changesSeen := Manifests(specSecretWithByteData, specSecretWithByteDataChanged, &diffOptions, &buf1); !changesSeen {
543+
t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`")
544+
}
545+
546+
//TODO: Why is there no empty line between the header and the start of the diff, like in the other diffs?
547+
require.Equal(t, `default, foobar, Secret (v1) has changed:
548+
apiVersion: v1
549+
kind: Secret
550+
metadata:
551+
name: foobar
552+
data:
553+
- key1: '-------- # (6 bytes)'
554+
+ key1: '++++++++ # (13 bytes)'
555+
key2: 'REDACTED # (6 bytes)'
556+
- key3: '-------- # (6 bytes)'
557+
+ key4: '++++++++ # (6 bytes)'
558+
type: Opaque
559+
560+
`, buf1.String())
561+
})
562+
563+
t.Run("OnChangeSecretWithStringData", func(t *testing.T) {
564+
var buf1 bytes.Buffer
565+
diffOptions := Options{"diff", 10, false, false, []string{}, 0.5} //NOTE: ShowSecrets = false
566+
567+
if changesSeen := Manifests(specSecretWithStringData, specSecretWithStringDataChanged, &diffOptions, &buf1); !changesSeen {
568+
t.Error("Unexpected return value from Manifests: Expected the return value to be `true` to indicate that it has seen any change(s), but was `false`")
569+
}
570+
571+
require.Equal(t, `default, foobar, Secret (v1) has changed:
572+
apiVersion: v1
573+
kind: Secret
574+
metadata:
575+
name: foobar
576+
data:
577+
- key1: '-------- # (6 bytes)'
578+
+ key1: '++++++++ # (13 bytes)'
579+
key2: 'REDACTED # (6 bytes)'
580+
- key3: '-------- # (6 bytes)'
581+
+ key4: '++++++++ # (6 bytes)'
582+
type: Opaque
583+
584+
`, buf1.String())
585+
})
586+
}

0 commit comments

Comments
 (0)