203 lines
7.7 KiB
Go
203 lines
7.7 KiB
Go
package engine
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestSecretPathCollectionKeepsParsedContributions(t *testing.T) {
|
|
for _, pointer := range []string{"", "/", "/records/0", "/records/a~1b~0/01"} {
|
|
t.Run(pointer, func(t *testing.T) {
|
|
paths := make(map[string]Path)
|
|
for repeat := 0; repeat < 3; repeat++ {
|
|
addSecretPath(paths, pointer)
|
|
path, found := paths[pointer]
|
|
if !found || !path.valid() || path.String() != pointer {
|
|
t.Fatalf("collected path = %#v, found %v; want %q", path, found, pointer)
|
|
}
|
|
for _, segment := range path.segments {
|
|
if segment.kind != pathSegmentParsed {
|
|
t.Fatalf("collected path %q retained a typed segment: %#v", pointer, segment)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSecretClassificationParsesTypedHistoryAfterNumericShapeChanges(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
earlier string
|
|
final string
|
|
historical Path
|
|
}{
|
|
{"mapping to sequence", "items: {'0': old}", "items: [new]", Keys("items", "0")},
|
|
{"sequence to mapping", "items: [old]", "items: {'0': new}", Keys("items").Index(0)},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
earlier := parseCompositionDocument(t, "earlier", test.earlier)
|
|
final := parseCompositionDocument(t, "final", test.final)
|
|
pointer := test.historical.String()
|
|
metadata := Metadata{history: map[string][]Change{pointer: {{Path: test.historical}}}}
|
|
seen := false
|
|
classified := classifySecretPaths(nil, func(path Path) bool {
|
|
if path.String() != pointer {
|
|
return false
|
|
}
|
|
seen = true
|
|
for _, segment := range path.segments {
|
|
if segment.kind != pathSegmentParsed {
|
|
t.Fatalf("predicate received typed history after final projection: %#v", path)
|
|
}
|
|
}
|
|
return true
|
|
}, metadata, final, []historyApplication{{value: earlier}}, earlier)
|
|
if !seen || !classified[pointer] {
|
|
t.Fatalf("historical/final numeric path was not classified: %#v", classified)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSecretPredicateReceivesDetachedHistoricalPath(t *testing.T) {
|
|
path := Keys("records").Key("1")
|
|
metadata := Metadata{history: map[string][]Change{
|
|
path.String(): {{Path: path}},
|
|
}}
|
|
seen := false
|
|
classified := classifySecretPaths(nil, func(candidate Path) bool {
|
|
if candidate.String() != path.String() {
|
|
return false
|
|
}
|
|
seen = true
|
|
candidate.segments[0].key = "changed"
|
|
return true
|
|
}, metadata, nil, nil)
|
|
|
|
if !seen || !classified[path.String()] || classified["/changed/1"] {
|
|
t.Fatalf("classification after predicate mutation = %#v, predicate called: %v", classified, seen)
|
|
}
|
|
}
|
|
|
|
func TestSourceVersionEvidenceSensitivityUsesOriginalMountedPaths(t *testing.T) {
|
|
const canary = "PRIVATE-VERSION-CANARY"
|
|
type credential struct {
|
|
Value string `yaml:"credential" configkit:"secret"`
|
|
Public string `yaml:"public"`
|
|
}
|
|
type account struct {
|
|
Token string `yaml:"token"`
|
|
}
|
|
type tenant struct {
|
|
Groups map[string][]credential `yaml:"groups"`
|
|
Account account `yaml:"account" configkit:"secret"`
|
|
Settings map[string]string `yaml:"settings"`
|
|
Labels map[string]string `yaml:"labels"`
|
|
}
|
|
type configuration struct {
|
|
Tenant tenant `yaml:"tenant"`
|
|
}
|
|
schema, issue := buildSchema(reflect.TypeFor[configuration](), testSecretBinding())
|
|
if issue != nil {
|
|
t.Fatal(issue)
|
|
}
|
|
|
|
destination := Keys("tenant")
|
|
sequencePath := Keys("groups").Key("blue").Index(2).Key("credential")
|
|
accountPath := Keys("account", "token")
|
|
predicatePath := Keys("settings", "private")
|
|
nullPath := Keys("settings", "nullable")
|
|
absentPath := Keys("settings", "missing")
|
|
publicPath := Keys("labels", "public")
|
|
evidence := []*sourceVersionEvidence{
|
|
{
|
|
path: sequencePath.clone(), resolvedPath: sequencePath.clone(), destination: destination.clone(),
|
|
version: Version{Present: true, Text: canary},
|
|
ancestors: []sourceVersionAncestor{{}, {path: Keys("groups")}, {path: Keys("groups").Key("blue")}, {path: Keys("groups").Key("blue").Index(2)}, {path: sequencePath.clone()}},
|
|
},
|
|
{
|
|
path: accountPath.clone(), resolvedPath: accountPath.clone(), destination: destination.clone(),
|
|
version: Version{Present: true, Text: canary},
|
|
ancestors: []sourceVersionAncestor{{}, {path: Keys("account")}, {path: accountPath.clone()}},
|
|
},
|
|
{path: predicatePath.clone(), resolvedPath: predicatePath.clone(), destination: destination.clone(), version: Version{Present: true, Text: canary}},
|
|
{path: nullPath.clone(), resolvedPath: nullPath.clone(), destination: destination.clone(), version: Version{Present: true, Null: true}},
|
|
{path: absentPath.clone(), resolvedPath: absentPath.clone(), destination: destination.clone()},
|
|
{path: publicPath.clone(), resolvedPath: publicPath.clone(), destination: destination.clone(), version: Version{Present: true, Text: canary}},
|
|
}
|
|
|
|
final := parseCompositionDocument(t, "final", "tenant:\n groups:\n blue:\n - public: shifted-neighbor\n labels:\n public: visible\n")
|
|
var sawOriginalSequencePath, sawAncestorPredicate bool
|
|
classified := classifySecretPathsWithEvidence(schema, func(path Path) bool {
|
|
if path.String() == appendDestination(destination, predicatePath).String() {
|
|
return true
|
|
}
|
|
if path.String() == appendDestination(destination, nullPath).String() {
|
|
return true
|
|
}
|
|
if path.String() == appendDestination(destination, absentPath).String() {
|
|
return true
|
|
}
|
|
if path.String() == appendDestination(destination, Keys("account")).String() {
|
|
sawAncestorPredicate = true
|
|
return true
|
|
}
|
|
if path.String() == appendDestination(destination, sequencePath).String() {
|
|
sawOriginalSequencePath = len(path.segments) == 5 && path.segments[3].kind == pathSegmentIndex && path.segments[3].index == 2
|
|
}
|
|
return false
|
|
}, Metadata{}, final, []historyApplication{{path: destination, value: final}}, evidence)
|
|
|
|
for _, path := range []Path{
|
|
appendDestination(destination, sequencePath),
|
|
appendDestination(destination, Keys("account")),
|
|
appendDestination(destination, predicatePath),
|
|
appendDestination(destination, nullPath),
|
|
appendDestination(destination, absentPath),
|
|
} {
|
|
if !classified[path.String()] {
|
|
t.Errorf("evidence path %s was not classified: %#v", path, classified)
|
|
}
|
|
}
|
|
shiftedNeighbor := appendDestination(destination, Keys("groups").Key("blue").Index(0).Key("public"))
|
|
if classified[shiftedNeighbor.String()] {
|
|
t.Fatalf("shifted neighboring payload was classified from declaration evidence: %#v", classified)
|
|
}
|
|
if classified[appendDestination(destination, publicPath).String()] || !sawOriginalSequencePath || !sawAncestorPredicate {
|
|
t.Fatalf("unclassified or semantic evidence candidates = %#v; original path seen %v, ancestor predicate seen %v", classified, sawOriginalSequencePath, sawAncestorPredicate)
|
|
}
|
|
}
|
|
|
|
func TestSourceVersionEvidenceClassificationKeepsCollidingShapes(t *testing.T) {
|
|
t.Parallel()
|
|
type header struct {
|
|
Version string `yaml:"version" configkit:"secret"`
|
|
}
|
|
type configuration struct {
|
|
Meta map[string]header `yaml:"meta"`
|
|
}
|
|
schema, issue := buildSchema(reflect.TypeFor[configuration](), testSecretBinding())
|
|
if issue != nil {
|
|
t.Fatal(issue)
|
|
}
|
|
keyPath := Keys("meta", "0", "version")
|
|
indexPath := Keys("meta").Index(0).Key("version")
|
|
for _, reverse := range []bool{false, true} {
|
|
evidence := []*sourceVersionEvidence{{resolvedPath: keyPath}, {resolvedPath: indexPath}}
|
|
if reverse {
|
|
evidence[0], evidence[1] = evidence[1], evidence[0]
|
|
}
|
|
var sawKey, sawIndex bool
|
|
classified := classifySecretPathsWithEvidence(schema, func(path Path) bool {
|
|
sawKey = sawKey || reflect.DeepEqual(path, keyPath)
|
|
sawIndex = sawIndex || reflect.DeepEqual(path, indexPath)
|
|
return false
|
|
}, Metadata{}, nil, nil, evidence)
|
|
if !classified[keyPath.String()] || !sawKey || !sawIndex {
|
|
t.Errorf("reverse=%v: classified=%v, key seen=%v, index seen=%v", reverse, classified, sawKey, sawIndex)
|
|
}
|
|
}
|
|
}
|