Publish detached sanitized source evidence

This commit is contained in:
2026-10-15 16:12:21 +00:00
parent 1e47ee21cd
commit 43c994a32a
11 changed files with 414 additions and 9 deletions

View File

@@ -63,8 +63,9 @@ func WriteDiagnostics(output io.Writer, diagnostics []Diagnostic) error {
}
type diagnosticAccumulator struct {
diagnostics []Diagnostic
sources []SourceInfo
diagnostics []Diagnostic
sources []SourceInfo
versionState *sourceVersionState
}
func (a *diagnosticAccumulator) add(diagnostics ...Diagnostic) {
@@ -74,9 +75,13 @@ func (a *diagnosticAccumulator) add(diagnostics ...Diagnostic) {
}
func (a *diagnosticAccumulator) loadError() *LoadError {
sources := a.sources
if a.versionState != nil {
sources = sourceInfosWithVersionEvidence(sources, a.versionState.evidence, nil, true)
}
return &LoadError{
diagnostics: canonicalDiagnostics(a.diagnostics),
sources: cloneSourceInfos(a.sources),
sources: cloneSourceInfos(sources),
}
}

View File

@@ -173,6 +173,7 @@ func executeLoadRequest[T any](request loadRequest[T], secrets SecretBinding, lo
versionState := &sourceVersionState{
localRule: request.definition.Version, effectiveRule: request.protectionVersion(), mode: request.sourceVersionMode,
}
accumulator.versionState = versionState
input, diagnostics := assembleBaseInputTrackedWithVersion(request.main.source, mainOptions, request.main.destination, accounting, nil, nil, versionState)
accumulator.sources = input.sources
accumulator.add(versionState.diagnostics...)

View File

@@ -37,7 +37,7 @@ func assembleBaseInputTrackedForRole[T any](source Source, options Options[T], d
if parsed.source != nil {
parsed.source.Destination = destination.clone()
}
input := assembledInput{parsed: parsed, document: parsed.document, versionState: versionState}
input := assembledInput{parsed: parsed, document: parsed.document, sources: sourceInfos(parsed.source), versionState: versionState}
if len(diagnostics) != 0 {
input.sources = sourceInfos(parsed.source)
// Mapping value issues already identify application content. Other early

View File

@@ -58,7 +58,11 @@ func resolveLoadInput[T any](schema *schemaDescriptor, definition Definition[T],
final := decoded.final
var resolutionSnapshots []resolutionSnapshot
if definition.Resolve != nil {
entry := newResolutionInput(metadata, decoded.final, input.sources, input.contexts)
sources := input.sources
if input.versionState != nil {
sources = sourceInfosWithVersionEvidence(sources, input.versionState.evidence, nil, true)
}
entry := newResolutionInput(metadata, decoded.final, sources, input.contexts)
resolved := runResolution(&configuration, definition.Resolve, schema, decoded, input.document, entry)
diagnostics = append(diagnostics, resolved.diagnostics...)
if hasErrors(resolved.diagnostics) {
@@ -103,6 +107,7 @@ func publishLoadResult[T any](schema *schemaDescriptor, definition Definition[T]
final, applications, resolutionSnapshots := resolved.final, resolved.applications, resolved.snapshots
finalizeMetadataPaths(&metadata, final, applications, decoded.baseline, decoded.final)
secrets := classifySecretPathsWithEvidence(schema, definition.Secret, metadata, final, applications, resolved.versionEvidence, decoded.baseline, decoded.final)
sources := sourceInfosWithVersionEvidence(accumulator.sources, resolved.versionEvidence, secrets, false)
retainedTrees := make([]*document.Node, 0, len(applications)+2)
retainedTrees = append(retainedTrees, decoded.final, final)
for _, application := range applications {
@@ -110,7 +115,7 @@ func publishLoadResult[T any](schema *schemaDescriptor, definition Definition[T]
}
inspection := &Inspection{
metadata: metadata,
sources: cloneSourceInfos(accumulator.sources),
sources: sources,
contexts: cloneResourceContexts(resolved.contexts),
diagnostics: canonicalDiagnostics(accumulator.diagnostics),
baseline: decoded.baseline,

View File

@@ -191,9 +191,19 @@ type SourceInfo struct {
Kind SourceKind
UnderlayIndex int
ImportedFrom *Location
VersionCheck *SourceVersionCheck
namespace *namespaceToken
}
// SourceVersionCheck is a detached, public-safe record of a source-local
// version declaration observed during an enabled load.
type SourceVersionCheck struct {
Path Path
Version Version
Location *Location
Redacted bool
}
// Diagnostic is a safe structured problem report. Message and source labels must
// never contain configuration values or other secrets.
type Diagnostic struct {
@@ -262,9 +272,20 @@ func cloneLocations(locations []Location) []Location {
func cloneSourceInfo(source SourceInfo) SourceInfo {
source.ImportedFrom = cloneLocation(source.ImportedFrom)
source.Destination = source.Destination.clone()
source.VersionCheck = cloneSourceVersionCheck(source.VersionCheck)
return source
}
func cloneSourceVersionCheck(value *SourceVersionCheck) *SourceVersionCheck {
if value == nil {
return nil
}
cloned := *value
cloned.Path = value.Path.clone()
cloned.Location = cloneLocation(value.Location)
return &cloned
}
func cloneSourceInfos(sources []SourceInfo) []SourceInfo {
if sources == nil {
return nil

View File

@@ -51,6 +51,46 @@ type sourceVersionState struct {
diagnostics []Diagnostic
}
func sourceInfosWithVersionEvidence(sources []SourceInfo, evidence []*sourceVersionEvidence, secrets map[string]bool, conservative bool) []SourceInfo {
result := cloneSourceInfos(sources)
if len(evidence) == 0 {
return result
}
sensitivity := newDiffSensitivity(secrets, nil)
for _, item := range evidence {
if item == nil || item.sourceID == "" {
continue
}
for index := range result {
if result[index].ID == item.sourceID {
result[index].VersionCheck = sourceVersionCheckForEvidence(item, sensitivity, conservative)
break
}
}
}
return result
}
func sourceVersionCheckForEvidence(evidence *sourceVersionEvidence, sensitivity *diffSensitivity, conservative bool) *SourceVersionCheck {
path := evidence.resolvedPath
if !path.valid() {
path = evidence.path
}
version := evidence.version
redacted := evidence.redacted
if conservative {
redacted = redacted || version.Text != ""
} else if sensitivity.isSensitiveAtOrAbove(appendDestination(evidence.destination, path).String()) {
redacted = true
}
if redacted {
version.Text = ""
}
return &SourceVersionCheck{
Path: path.clone(), Version: version, Location: cloneLocation(evidence.location), Redacted: redacted,
}
}
func (state *sourceVersionState) process(root *document.Node, role sourceVersionRole, destination Path, sourceID string) (sourceVersionProcessingResult, []Diagnostic) {
if state == nil || state.mode == sourceVersionModeDefault {
return sourceVersionProcessingResult{}, nil