Verify integer version provenance and protections

This commit is contained in:
2026-09-12 00:30:32 +00:00
parent 6eb40c5492
commit 8702121fa4
5 changed files with 121 additions and 8 deletions

View File

@@ -92,7 +92,7 @@ history, rendering, composition ownership, detachment, and redaction invariants.
## Stage 3 — Verify provenance and pipeline invariants ## Stage 3 — Verify provenance and pipeline invariants
Status: not started. Depends on: Stage 2. Status: complete (2026-09-12). Depends on: Stage 2.
Scope: `explain_test.go`, `provenance_test.go`, import/profile/override and secret Scope: `explain_test.go`, `provenance_test.go`, import/profile/override and secret
test owners as needed. Production changes only for demonstrated integration gaps. test owners as needed. Production changes only for demonstrated integration gaps.
@@ -116,6 +116,15 @@ needs repair, retain original input rather than suppressing its history.
Validation: `go test . -run 'Version|Import|Profile|Override|Provenance|Explain|Secret'`, Validation: `go test . -run 'Version|Import|Profile|Override|Provenance|Explain|Secret'`,
`go test -race ./...`, then the everyday checker. `go test -race ./...`, then the everyday checker.
Completion record: end-to-end inspection now proves a numeric retained input, a
string typed/effective value, a decoding reconciliation event, exact base origin
and location, historical lookup, equal-default handling, and detached repeated
results. Integer fixtures exercise fragment, profile, and CLI version protection.
Sensitive version history is redacted and performs no credential lookup. The
focused suite, race suite, and `./scripts/check.sh` pass with Go 1.26.7 on Linux.
Stage 4 should finish the maintained Notarius evidence, canonical documentation,
and release-readiness validation without publishing a release.
## Stage 4 — Complete consumer evidence and release readiness ## Stage 4 — Complete consumer evidence and release readiness
Status: not started. Depends on: Stage 3. Status: not started. Depends on: Stage 3.

View File

@@ -366,6 +366,110 @@ func TestExplanationReconcilesDefaultsAfterInputReplacement(t *testing.T) {
} }
} }
func TestIntegerVersionInspectionRetainsInputAndReconcilesTypedValue(t *testing.T) {
type config struct {
Version string `yaml:"version"`
}
rule := &VersionRule{Path: Keys("version"), Check: func(version Version, _ *Validation) error {
if version.Text != "4" {
t.Errorf("version = %#v", version)
}
return nil
}}
options := Options[config]{Definition: Definition[config]{
Defaults: func() config { return config{Version: "4"} },
Version: rule,
}}
first, err := Load(Bytes("version.yml", []byte("version: 4\n")), options)
if err != nil {
t.Fatal(err)
}
second, err := Load(Bytes("version.yml", []byte("version: 4\n")), options)
if err != nil {
t.Fatal(err)
}
if first.Config.Version != "4" || second.Config.Version != "4" {
t.Fatalf("configurations = %#v / %#v", first.Config, second.Config)
}
inputVersion, inputOK := documentAtPath(first.applications[0].value, Keys("version"))
finalVersion, finalOK := documentAtPath(first.final, Keys("version"))
if !inputOK || inputVersion.Kind != document.Number || inputVersion.Text != "4" ||
!finalOK || finalVersion.Kind != document.String || finalVersion.Text != "4" {
t.Fatalf("input/final = %#v / %#v", inputVersion, finalVersion)
}
info, ok := first.Metadata().Lookup(Keys("version"))
if !ok || !info.Explicit || info.Null || info.Origin.Layer != LayerBase || len(info.Origin.Locations) != 1 ||
info.Origin.Locations[0].SourceID != "source-1" || info.Origin.Locations[0].Line != 1 {
t.Fatalf("version metadata = %#v, %v", info, ok)
}
explanation, ok := first.Explain(Keys("version"))
if !ok || explanation.Effective.YAML != "'4'\n" {
t.Fatalf("version explanation = %#v, %v", explanation, ok)
}
foundInput, foundDecoding := false, false
for _, change := range explanation.History {
if change.After.YAML == "4\n" && change.Origin.Layer == LayerBase {
foundInput = true
}
if change.Before.YAML == "4\n" && change.After.YAML == "'4'\n" && change.Origin.Operation == ChangeDecoding {
foundDecoding = true
}
if change.Origin.Layer == LayerResolution {
t.Fatalf("conversion recorded as resolution: %#v", change)
}
}
if !foundInput || !foundDecoding {
t.Fatalf("version history = %#v", explanation.History)
}
paths := first.Metadata().HistoricalPaths()
foundPath := false
for _, path := range paths {
if path.String() == "/version" {
foundPath = true
}
}
if !foundPath || len(first.Metadata().History(Keys("version"))) < 2 {
t.Fatalf("historical version paths/history = %#v / %#v", paths, first.Metadata().History(Keys("version")))
}
var effective strings.Builder
if err := first.WriteEffective(&effective); err != nil || effective.String() != "version: '4'\n" {
t.Fatalf("effective output = %q, %v", effective.String(), err)
}
first.Config.Version = "mutated"
effective.Reset()
if err := first.WriteEffective(&effective); err != nil || effective.String() != "version: '4'\n" || second.Config.Version != "4" {
t.Fatalf("detached results = %q / %#v, %v", effective.String(), second.Config, err)
}
}
func TestSensitiveIntegerVersionStaysRedactedWithoutSecretLookup(t *testing.T) {
type config struct {
Version string `yaml:"version"`
}
lookups := 0
result, err := loadWithSecretLookup(Bytes("version.yml", []byte("version: 987654321\n")), func(string) (string, bool) {
lookups++
return "CREDENTIAL_CANARY", true
}, Options[config]{Definition: Definition[config]{
Version: &VersionRule{Path: Keys("version"), Check: func(Version, *Validation) error { return nil }},
Secret: func(path Path) bool { return path.String() == "/version" },
}})
if err != nil || result.Config.Version != "987654321" || lookups != 0 {
t.Fatalf("result = %#v, lookups = %d, err = %v", result, lookups, err)
}
var effective strings.Builder
if err := result.WriteEffective(&effective); err != nil {
t.Fatal(err)
}
explanation, ok := result.Explain(Keys("version"))
formatted := effective.String() + fmt.Sprintf("%#v %#v", explanation, result.Metadata().History(Keys("version")))
if !ok || !explanation.Effective.Redacted || strings.Contains(formatted, "987654321") || strings.Contains(formatted, "CREDENTIAL_CANARY") {
t.Fatalf("sensitive inspection = %q / %#v", effective.String(), explanation)
}
}
func TestExplanationReplaysResolutionAdditionsAndOverlappingIntent(t *testing.T) { func TestExplanationReplaysResolutionAdditionsAndOverlappingIntent(t *testing.T) {
type cfg struct { type cfg struct {
M map[string]any `yaml:"m"` M map[string]any `yaml:"m"`

View File

@@ -113,7 +113,7 @@ func TestLoadRejectsImportConflictsAndFragmentVersions(t *testing.T) {
name string name string
fragment string fragment string
}{ }{
{name: "exact", fragment: "settings:\n version: v2\n"}, {name: "exact integer", fragment: "settings:\n version: 4\n"},
{name: "ancestor scalar", fragment: "settings: blocked\n"}, {name: "ancestor scalar", fragment: "settings: blocked\n"},
} { } {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {

View File

@@ -163,7 +163,7 @@ func TestOverrideValidationRejectsPathsOverlapValuesAndProtectedTargets(t *testi
Nested map[string]any `yaml:"nested"` Nested map[string]any `yaml:"nested"`
Unknown string `yaml:"unknown"` Unknown string `yaml:"unknown"`
} }
base := Bytes("override-errors.yml", []byte("version: v1\nscalar: value\nitems: [one]\nnested: {}\n")) base := Bytes("override-errors.yml", []byte("version: 4\nscalar: value\nitems: [one]\nnested: {}\n"))
version := &VersionRule{Path: Keys("version"), Check: func(Version, *Validation) error { return nil }} version := &VersionRule{Path: Keys("version"), Check: func(Version, *Validation) error { return nil }}
cycle := map[string]any{} cycle := map[string]any{}
cycle["self"] = cycle cycle["self"] = cycle
@@ -213,7 +213,7 @@ func TestOverrideCannotReplaceSchemaVersionAncestor(t *testing.T) {
type config struct { type config struct {
Schema schemaConfig `yaml:"schema"` Schema schemaConfig `yaml:"schema"`
} }
_, err := Load(Bytes("version.yml", []byte("schema: {version: v1}\n")), Options[config]{ _, err := Load(Bytes("version.yml", []byte("schema: {version: 4}\n")), Options[config]{
Definition: Definition[config]{Version: &VersionRule{Path: Keys("schema", "version"), Check: func(Version, *Validation) error { return nil }}}, Definition: Definition[config]{Version: &VersionRule{Path: Keys("schema", "version"), Check: func(Version, *Validation) error { return nil }}},
Overrides: []Override{{Path: Keys("schema"), Value: map[string]any{"other": "value"}, Flag: "--schema"}}, Overrides: []Override{{Path: Keys("schema"), Value: map[string]any{"other": "value"}, Flag: "--schema"}},
}) })

View File

@@ -123,7 +123,7 @@ func TestLoadProfileSelectionUsesOneFixedYAMLFile(t *testing.T) {
func TestLoadProfileRejectsDirectivesVersionsAndBaseAliases(t *testing.T) { func TestLoadProfileRejectsDirectivesVersionsAndBaseAliases(t *testing.T) {
versionRule := &VersionRule{Path: Keys("schema", "version"), Check: func(version Version, _ *Validation) error { versionRule := &VersionRule{Path: Keys("schema", "version"), Check: func(version Version, _ *Validation) error {
if !version.Present || version.Text != "v1" { if !version.Present || version.Text != "4" {
return errors.New("unexpected version") return errors.New("unexpected version")
} }
return nil return nil
@@ -135,12 +135,12 @@ func TestLoadProfileRejectsDirectivesVersionsAndBaseAliases(t *testing.T) {
}{ }{
{name: "root directive", profile: "configkit: {}\n", code: CodeProfileDirective}, {name: "root directive", profile: "configkit: {}\n", code: CodeProfileDirective},
{name: "nested directive", profile: "settings: {configkit: {}}\n", code: CodeProfileDirective}, {name: "nested directive", profile: "settings: {configkit: {}}\n", code: CodeProfileDirective},
{name: "equal version", profile: "schema: {version: v1}\n", code: CodeProfileVersion}, {name: "equal integer version", profile: "schema: {version: 4}\n", code: CodeProfileVersion},
{name: "ancestor scalar", profile: "schema: null\n", code: CodeProfileVersion}, {name: "ancestor scalar", profile: "schema: null\n", code: CodeProfileVersion},
} { } {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
directory := t.TempDir() directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", "schema: {version: v1}\n") mainPath := writeImportFile(t, directory, "main.yml", "schema: {version: 4}\n")
writeImportFile(t, directory, "profiles/testing.yml", test.profile) writeImportFile(t, directory, "profiles/testing.yml", test.profile)
requireProfileLoadCode(t, File(mainPath), Options[profileTestConfig]{Profile: "testing", Definition: Definition[profileTestConfig]{Version: versionRule}}, test.code) requireProfileLoadCode(t, File(mainPath), Options[profileTestConfig]{Profile: "testing", Definition: Definition[profileTestConfig]{Version: versionRule}}, test.code)
}) })
@@ -148,7 +148,7 @@ func TestLoadProfileRejectsDirectivesVersionsAndBaseAliases(t *testing.T) {
t.Run("mapping sibling is allowed", func(t *testing.T) { t.Run("mapping sibling is allowed", func(t *testing.T) {
directory := t.TempDir() directory := t.TempDir()
mainPath := writeImportFile(t, directory, "main.yml", "schema: {version: v1}\n") mainPath := writeImportFile(t, directory, "main.yml", "schema: {version: 4}\n")
writeImportFile(t, directory, "profiles/testing.yml", "schema: {other: allowed}\n") writeImportFile(t, directory, "profiles/testing.yml", "schema: {other: allowed}\n")
result, err := Load[profileTestConfig](File(mainPath), Options[profileTestConfig]{Profile: "testing", Definition: Definition[profileTestConfig]{Version: versionRule}}) result, err := Load[profileTestConfig](File(mainPath), Options[profileTestConfig]{Profile: "testing", Definition: Definition[profileTestConfig]{Version: versionRule}})
if err != nil || result.Config.Schema["other"] != "allowed" { if err != nil || result.Config.Schema["other"] != "allowed" {