Preserve secret sensitivity across sibling inheritance

This commit is contained in:
2026-09-29 22:56:10 +00:00
parent f916e7d1f9
commit 501d59f21b
3 changed files with 182 additions and 0 deletions

View File

@@ -0,0 +1,136 @@
package engine
import (
"bytes"
"reflect"
"strings"
"testing"
)
func TestInheritanceSensitivityFollowsCopiesWithoutMaskingNeighbors(t *testing.T) {
const canary = "INHERITED-CANARY-CREDENTIAL"
input := parseCompositionDocument(t, "source-1", "parent: {token: "+canary+", plain: visible}\nchild: {extends: parent}\nnext: {extends: child}\nother: {extends: parent}\n")
for _, classified := range []Path{Keys("parent", "token"), Keys("next", "token")} {
t.Run(classified.String(), func(t *testing.T) {
inspection, _ := inheritanceInspectionForTest(t, input)
inspection.secrets = classifySecretPaths(nil, func(path Path) bool {
return path.String() == classified.String()
}, inspection.metadata, inspection.final, inspection.applications, inspection.input)
for _, path := range []Path{Keys("parent", "token"), Keys("child", "token"), Keys("next", "token"), Keys("other", "token")} {
explanation, found := inspection.Explain(path)
if !found || !explanation.Effective.Redacted || strings.Contains(explanation.Effective.YAML, canary) {
t.Fatalf("sensitive copy %s = %#v, %v", path, explanation, found)
}
for _, change := range explanation.History {
if strings.Contains(change.Before.YAML, canary) || strings.Contains(change.After.YAML, canary) {
t.Fatalf("historical canary at %s: %#v", path, change)
}
}
}
plain, found := inspection.Explain(Keys("other", "plain"))
if !found || plain.Effective.Redacted || plain.Effective.YAML != "visible\n" {
t.Fatalf("unrelated field = %#v, %v", plain, found)
}
var output bytes.Buffer
if err := inspection.WriteEffective(&output); err != nil || strings.Contains(output.String(), canary) || !strings.Contains(output.String(), "visible") {
t.Fatalf("effective rendering = %q, %v", output.String(), err)
}
})
}
}
func TestInheritanceSensitivityPreservesDisplacedHistoryAndControl(t *testing.T) {
const canary = "DISPLACED-CANARY-CREDENTIAL"
inspection, _ := inheritanceInspectionForTest(t, parseCompositionDocument(t, "source-1", "parent: {branch: {token: "+canary+"}}\nchild: {extends: parent, branch: public}\n"))
inspection.secrets = classifySecretPaths(nil, func(path Path) bool {
return path.String() == Keys("parent", "branch", "token").String() || path.String() == Keys("child", inheritanceKey).String()
}, inspection.metadata, inspection.final, inspection.applications, inspection.input)
path := Keys("child", "branch", "token")
explanation, found := inspection.Explain(path)
if !found || explanation.Effective.Present {
t.Fatalf("displaced sensitive path = %#v, %v", explanation, found)
}
for _, change := range explanation.History {
if strings.Contains(change.Before.YAML, canary) || strings.Contains(change.After.YAML, canary) ||
(change.After.Present && !change.After.Redacted) {
t.Fatalf("displaced secret leaked: %#v", explanation.History)
}
}
var rendered bytes.Buffer
if err := inspection.WriteExplanation(&rendered, path); err != nil || strings.Contains(rendered.String(), canary) {
t.Fatalf("displaced explanation rendering = %q, %v", rendered.String(), err)
}
control, found := inspection.Explain(Keys("child", inheritanceKey))
if !found || control.Effective.Present || len(control.History) == 0 || !control.History[0].After.Redacted {
t.Fatalf("sensitive control history = %#v, %v", control, found)
}
info, found := inspection.Metadata().Lookup(Keys("child", "branch"))
if !found || len(info.Origin.Inheritance) != 0 {
t.Fatalf("independent replacement provenance = %#v, %v", info, found)
}
}
func TestInheritanceSensitivityIncludesSchemaTaggedSource(t *testing.T) {
type entry struct {
Token string `yaml:"token"`
}
type configuration struct {
Parent struct {
Token string `yaml:"token" configkit:"secret"`
} `yaml:"parent"`
Child entry `yaml:"child"`
}
schema, issue := buildSchema(reflect.TypeFor[configuration](), testSecretBinding())
if issue != nil {
t.Fatal(issue)
}
inspection, _ := inheritanceInspectionForTest(t, parseCompositionDocument(t, "source-1", "parent: {token: TAGGED-CANARY}\nchild: {extends: parent}\n"))
inspection.secrets = classifySecretPaths(schema, nil, inspection.metadata, inspection.final, inspection.applications, inspection.input)
child, found := inspection.Explain(Keys("child", "token"))
if !found || !child.Effective.Redacted || strings.Contains(child.Effective.YAML, "TAGGED-CANARY") {
t.Fatalf("tagged source copy = %#v, %v", child, found)
}
}
func TestInheritedSecretReferencesResolveOnlyAfterExpansion(t *testing.T) {
type entry struct {
Credential Secret `yaml:"credential"`
}
type configuration struct {
Entries map[string]entry `yaml:"entries"`
}
schema, issue := buildSchema(reflect.TypeFor[configuration](), testSecretBinding())
if issue != nil {
t.Fatal(issue)
}
composed, diagnostics := composeDocuments(parseCompositionDocument(t, "source-1", "entries:\n parent: {credential: INHERITED_SECRET_NAME}\n child: {extends: parent}\n"))
if len(diagnostics) != 0 {
t.Fatal(diagnostics)
}
expanded, diagnostics := expandInheritanceWithOrigins(composed.root, composed.origins, defaultProjectionLimits())
if len(diagnostics) != 0 {
t.Fatal(diagnostics)
}
lookupCount := 0
decoded, issues := decodeConfiguration(schema, reflect.Zero(reflect.TypeFor[configuration]()), expanded.root)
if len(issues) != 0 || lookupCount != 0 {
t.Fatalf("decoding and lookup order = %#v, %d", issues, lookupCount)
}
resolved := resolveSecretReferences(decoded.value, schema, expanded.root, func(name string) (string, bool) {
lookupCount++
if name != "INHERITED_SECRET_NAME" {
t.Fatalf("inherited reference name = %q", name)
}
return "RESOLVED-CREDENTIAL-CANARY", true
})
if len(resolved.diagnostics) != 0 || lookupCount != 1 ||
decoded.value.Interface().(configuration).Entries["child"].Credential.Value() != "RESOLVED-CREDENTIAL-CANARY" {
t.Fatalf("inherited reference resolution = %#v, %d", resolved.diagnostics, lookupCount)
}
inspection, _ := inheritanceInspectionFromComposition(t, composed)
inspection.secrets = classifySecretPaths(schema, nil, inspection.metadata, inspection.final, inspection.applications, inspection.input)
var rendered bytes.Buffer
if err := inspection.WriteEffective(&rendered); err != nil || strings.Contains(rendered.String(), "RESOLVED-CREDENTIAL-CANARY") {
t.Fatalf("reference rendering = %q, %v", rendered.String(), err)
}
}

View File

@@ -34,6 +34,35 @@ func classifySecretPaths(schema *schemaDescriptor, predicate func(Path) bool, me
}
}
}
var inherited []secretDependency
for _, operation := range metadata.inheritance {
if operation.control != nil {
control := operation.control.path
paths[control.String()] = control.clone()
for _, contributor := range operation.controlOrigin.contributors {
paths[contributor.source.String()] = contributor.source.clone()
inherited = append(inherited, secretDependency{target: control.clone(), dependency: contributor.source.clone()})
}
continue
}
if operation.transfer == nil {
continue
}
for pointer := range operation.parentOrigins {
source, err := ParsePath(pointer)
if err != nil {
continue
}
relative, ok := relativeHistoryPath(operation.transfer.from, source)
if !ok {
continue
}
destination := appendInheritancePath(operation.transfer.to, relative)
paths[pointer] = source
paths[destination.String()] = destination.clone()
inherited = append(inherited, secretDependency{target: destination, dependency: source})
}
}
secret := make(map[string]bool)
for _, tree := range append(projections, final) {
@@ -62,6 +91,16 @@ func classifySecretPaths(schema *schemaDescriptor, predicate func(Path) bool, me
changed = true
}
}
for _, link := range inherited {
if pathIsSecret(secret, link.dependency) && !secret[link.target.String()] {
secret[link.target.String()] = true
changed = true
}
if pathIsSecret(secret, link.target) && !secret[link.dependency.String()] {
secret[link.dependency.String()] = true
changed = true
}
}
if !changed {
return secret
}