Preserve header sensitivity across colliding path shapes

This commit is contained in:
2026-10-15 22:17:49 +00:00
parent 25cb3c47e7
commit e5a6df7af7
5 changed files with 134 additions and 13 deletions

View File

@@ -75,6 +75,19 @@ func classifySecretPathsWithEvidence(schema *schemaDescriptor, predicate func(Pa
classifyProjection(tree)
}
classifyProjection(final)
classifyPath := func(path Path) {
if root, ok := schemaSecretRoot(schema, path); ok {
secret[root.String()] = true
}
if predicate != nil && predicate(path.clone()) {
secret[path.String()] = true
}
}
for _, path := range paths {
classifyPath(path)
}
// Classify each original evidence shape before folding results into pointer
// sensitivity. Numeric mapping keys and sequence indices can share a pointer.
for _, item := range evidence {
if item == nil {
continue
@@ -83,25 +96,18 @@ func classifySecretPathsWithEvidence(schema *schemaDescriptor, predicate func(Pa
if !candidate.valid() {
candidate = item.path
}
addSecretEvidencePath(paths, item.destination, candidate)
classifyPath(addSecretEvidencePath(paths, item.destination, candidate))
for _, ancestor := range item.ancestors {
addSecretEvidencePath(paths, item.destination, ancestor.path)
}
}
for pointer, path := range paths {
if root, ok := schemaSecretRoot(schema, path); ok {
secret[root.String()] = true
}
if predicate != nil && predicate(path.clone()) {
secret[pointer] = true
classifyPath(addSecretEvidencePath(paths, item.destination, ancestor.path))
}
}
return closeSecretDependencies(paths, secret, dependencies, inherited)
}
func addSecretEvidencePath(paths map[string]Path, destination, local Path) {
func addSecretEvidencePath(paths map[string]Path, destination, local Path) Path {
path := appendDestination(destination, local)
paths[path.String()] = path.clone()
addSecretPath(paths, path.String())
return path
}
func addSecretPath(paths map[string]Path, pointer string) {

View File

@@ -169,3 +169,34 @@ func TestSourceVersionEvidenceSensitivityUsesOriginalMountedPaths(t *testing.T)
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)
}
}
}