Expose safe inheritance origins in explanations
This commit is contained in:
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
ck "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/audita/internal/core/config"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type rejectingTransport struct{ t *testing.T }
|
||||
@@ -112,6 +113,76 @@ func TestOfflineProfileAssemblyAndSafeViews(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExplanationYAMLIncludesSafeInheritanceEvidence(t *testing.T) {
|
||||
t.Setenv("SYNTHETIC_KEY", "synthetic-inherited-secret-never-print")
|
||||
text := `version: 2
|
||||
backends:
|
||||
shared:
|
||||
endpoint: https://shared.invalid/v1
|
||||
concurrency: 3
|
||||
queue_capacity: 0
|
||||
credential: SYNTHETIC_KEY
|
||||
offline:
|
||||
extends: shared
|
||||
concurrency: 1
|
||||
llm:
|
||||
proposal_profile: base
|
||||
`
|
||||
loaded, err := config.Load(ck.Bytes("config.yml", []byte(text)), "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
explanation, err := ExplanationYAML(loaded)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if strings.Contains(string(explanation), "synthetic-inherited-secret-never-print") {
|
||||
t.Fatal("explanation exposed a resolved inherited credential")
|
||||
}
|
||||
var records []struct {
|
||||
Path string `yaml:"path"`
|
||||
Inheritance []struct {
|
||||
From string `yaml:"from"`
|
||||
To string `yaml:"to"`
|
||||
Reference string `yaml:"reference"`
|
||||
} `yaml:"inheritance"`
|
||||
}
|
||||
if err := yaml.Unmarshal(explanation, &records); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var endpoint, credential, concurrency *struct {
|
||||
Path string `yaml:"path"`
|
||||
Inheritance []struct {
|
||||
From string `yaml:"from"`
|
||||
To string `yaml:"to"`
|
||||
Reference string `yaml:"reference"`
|
||||
} `yaml:"inheritance"`
|
||||
}
|
||||
for i := range records {
|
||||
switch records[i].Path {
|
||||
case "/backends/offline/endpoint":
|
||||
endpoint = &records[i]
|
||||
case "/backends/offline/credential":
|
||||
credential = &records[i]
|
||||
case "/backends/offline/concurrency":
|
||||
concurrency = &records[i]
|
||||
}
|
||||
}
|
||||
if endpoint == nil || len(endpoint.Inheritance) != 1 {
|
||||
t.Fatalf("inherited endpoint evidence missing: %s", explanation)
|
||||
}
|
||||
step := endpoint.Inheritance[0]
|
||||
if step.From != "/backends/shared/endpoint" || step.To != "/backends/offline/endpoint" || step.Reference != "/backends/offline/extends" {
|
||||
t.Fatalf("inherited endpoint evidence: %+v", step)
|
||||
}
|
||||
if credential == nil || len(credential.Inheritance) != 1 {
|
||||
t.Fatalf("inherited credential evidence missing: %s", explanation)
|
||||
}
|
||||
if concurrency == nil || len(concurrency.Inheritance) != 0 {
|
||||
t.Fatalf("direct child override reported parent inheritance: %+v", concurrency)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProfileAssemblyRejectsUnsafeSelections(t *testing.T) {
|
||||
t.Setenv("SYNTHETIC_KEY", "synthetic-value-never-print")
|
||||
dir := profileLayout(t, profileBase, "")
|
||||
|
||||
@@ -61,11 +61,17 @@ type derivationView struct {
|
||||
Reason string `yaml:"reason"`
|
||||
Dependencies []string `yaml:"dependencies,omitempty"`
|
||||
}
|
||||
type inheritanceStepView struct {
|
||||
From string `yaml:"from"`
|
||||
To string `yaml:"to"`
|
||||
Reference string `yaml:"reference"`
|
||||
}
|
||||
type explanationView struct {
|
||||
Path string `yaml:"path"`
|
||||
Source string `yaml:"source"`
|
||||
Layer ck.Layer `yaml:"layer"`
|
||||
Derivations []derivationView `yaml:"derivations,omitempty"`
|
||||
Path string `yaml:"path"`
|
||||
Source string `yaml:"source"`
|
||||
Layer ck.Layer `yaml:"layer"`
|
||||
Derivations []derivationView `yaml:"derivations,omitempty"`
|
||||
Inheritance []inheritanceStepView `yaml:"inheritance,omitempty"`
|
||||
}
|
||||
|
||||
// ExplanationYAML reports only origins and recorded resolution operations.
|
||||
@@ -93,6 +99,11 @@ func ExplanationYAML(result *ck.Result[config.Config]) ([]byte, error) {
|
||||
continue
|
||||
}
|
||||
record := explanationView{Path: path.String(), Layer: info.Origin.Layer, Source: string(info.Origin.Layer)}
|
||||
for _, step := range info.Origin.Inheritance {
|
||||
record.Inheritance = append(record.Inheritance, inheritanceStepView{
|
||||
From: step.From.String(), To: step.To.String(), Reference: step.Reference.String(),
|
||||
})
|
||||
}
|
||||
if len(info.Origin.Locations) > 0 {
|
||||
if label := sources[info.Origin.Locations[0].SourceID]; label != "" {
|
||||
record.Source = label
|
||||
|
||||
Reference in New Issue
Block a user