Files
configkit/internal/engine/secret_walk_test.go

138 lines
5.0 KiB
Go

package engine
import (
"fmt"
"strings"
"testing"
)
func TestSecretResolutionAndMutationAuditCoverTypedContainers(t *testing.T) {
t.Parallel()
type record struct {
Token Secret `yaml:"token"`
}
type inline struct {
Token Secret `yaml:"inline_token"`
}
type config struct {
Inline *inline `yaml:",inline"`
Pointer *Secret `yaml:"pointer"`
Indirect **Secret `yaml:"indirect"`
Values map[string]Secret `yaml:"values"`
Pointers map[string]*Secret `yaml:"pointers"`
Records map[string]record `yaml:"records"`
PointerRecords map[string]*record `yaml:"pointer_records"`
Slice []record `yaml:"slice"`
Array [1]Secret `yaml:"array"`
Absent *Secret `yaml:"absent"`
NilMap map[string]Secret `yaml:"nil_map"`
NilSlice []Secret `yaml:"nil_slice"`
}
const input = `inline_token: WALK_SECRET
pointer: WALK_SECRET
indirect: WALK_SECRET
values: {"a/b~c": WALK_SECRET}
pointers: {entry: WALK_SECRET, absent: null}
records: {entry: {token: WALK_SECRET}}
pointer_records: {entry: {token: WALK_SECRET}, absent: null}
slice: [{token: WALK_SECRET}]
array: [WALK_SECRET]
`
assertResolved := func(t *testing.T, value *config) {
t.Helper()
if value.Inline == nil || value.Pointer == nil || value.Indirect == nil || *value.Indirect == nil ||
value.Pointers["entry"] == nil || value.PointerRecords["entry"] == nil || len(value.Slice) != 1 {
t.Fatal("typed secret containers were not decoded")
}
for _, reference := range []Secret{
value.Inline.Token, *value.Pointer, **value.Indirect, value.Values["a/b~c"],
*value.Pointers["entry"], value.Records["entry"].Token, value.PointerRecords["entry"].Token,
value.Slice[0].Token, value.Array[0],
} {
if !reference.IsResolved() || reference.EnvName() != "WALK_SECRET" || reference.Value() != "WALK-CREDENTIAL-CANARY" {
t.Fatal("a typed secret did not retain its resolved state")
}
}
if value.Absent != nil || value.NilMap != nil || value.NilSlice != nil ||
value.Pointers["absent"] != nil || value.PointerRecords["absent"] != nil {
t.Fatal("nil secret containers changed")
}
}
for _, test := range []struct {
name string
path string
mutate func(*config)
}{
{name: "read-only validation"},
{name: "inline pointer", path: "/inline_token", mutate: func(value *config) {
value.Inline.Token = SecretEnv(value.Inline.Token.EnvName())
}},
{name: "pointer", path: "/pointer", mutate: func(value *config) {
*value.Pointer = SecretEnv(value.Pointer.EnvName())
}},
{name: "pointer chain", path: "/indirect", mutate: func(value *config) {
**value.Indirect = SecretEnv((**value.Indirect).EnvName())
}},
{name: "map value", path: "/values/a~1b~0c", mutate: func(value *config) {
value.Values["a/b~c"] = SecretEnv(value.Values["a/b~c"].EnvName())
}},
{name: "map pointer", path: "/pointers/entry", mutate: func(value *config) {
*value.Pointers["entry"] = SecretEnv(value.Pointers["entry"].EnvName())
}},
{name: "map record", path: "/records/entry/token", mutate: func(value *config) {
entry := value.Records["entry"]
entry.Token = SecretEnv(entry.Token.EnvName())
value.Records["entry"] = entry
}},
{name: "map record pointer", path: "/pointer_records/entry/token", mutate: func(value *config) {
entry := value.PointerRecords["entry"]
entry.Token = SecretEnv(entry.Token.EnvName())
}},
{name: "slice record", path: "/slice/0/token", mutate: func(value *config) {
value.Slice[0].Token = SecretEnv(value.Slice[0].Token.EnvName())
}},
{name: "array", path: "/array/0", mutate: func(value *config) {
value.Array[0] = SecretEnv(value.Array[0].EnvName())
}},
} {
t.Run(test.name, func(t *testing.T) {
lookups := 0
validated := false
result, err := testLoadWithSecretLookup(Bytes("secret-containers.yml", []byte(input)), func(name string) (string, bool) {
lookups++
if name != "WALK_SECRET" {
t.Fatalf("unexpected secret name %q", name)
}
return "WALK-CREDENTIAL-CANARY", true
}, Options[config]{Definition: Definition[config]{Validate: func(value *config, _ *Validation) {
validated = true
assertResolved(t, value)
if test.mutate != nil {
test.mutate(value)
}
}}})
if !validated || lookups != 1 {
t.Fatalf("validation ran = %v, lookups = %d", validated, lookups)
}
if test.mutate == nil {
if err != nil {
t.Fatal(err)
}
assertResolved(t, &result.Config)
return
}
if result != nil {
t.Fatal("validator mutation published a result")
}
loadError := requireSecretLoadCode(t, err, CodeValidateMutation)
diagnostics := loadError.Diagnostics()
if len(diagnostics) != 1 || diagnostics[0].Path.String() != test.path {
t.Fatalf("mutation diagnostics = %#v, want one at %s", diagnostics, test.path)
}
if strings.Contains(fmt.Sprintf("%v %#v", err, err), "WALK-CREDENTIAL-CANARY") {
t.Fatal("validator mutation error exposed a credential")
}
})
}
}