68 lines
2.3 KiB
Go
68 lines
2.3 KiB
Go
package engine
|
|
|
|
import (
|
|
"bytes"
|
|
"slices"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
|
yamladapter "gitea.maximumdirect.net/eric/ConfigKit/internal/yaml"
|
|
)
|
|
|
|
// Snapshot projection is the shared redaction boundary for effective YAML
|
|
// and explanation values. It borrows frozen trees and returns a detached tree.
|
|
func valueSnapshot(root *document.Node, path Path, secrets map[string]bool) ValueSnapshot {
|
|
return valueSnapshotWithLookup(root, path, secrets, nil)
|
|
}
|
|
|
|
func valueSnapshotWithLookup(root *document.Node, path Path, secrets map[string]bool, lookup *documentLookup) ValueSnapshot {
|
|
node, present := documentAtPathWithLookup(root, path, lookup)
|
|
if !present {
|
|
return ValueSnapshot{}
|
|
}
|
|
safe, redacted := safeSnapshotDocument(node, secrets, path)
|
|
var output bytes.Buffer
|
|
if err := yamladapter.Write(&output, safe); err != nil {
|
|
return ValueSnapshot{Present: true, Redacted: true, YAML: "'[REDACTED]'\n"}
|
|
}
|
|
return ValueSnapshot{Present: true, Redacted: redacted, YAML: output.String()}
|
|
}
|
|
|
|
func safeSnapshotDocument(node *document.Node, secrets map[string]bool, path Path) (*document.Node, bool) {
|
|
if node == nil {
|
|
return nil, false
|
|
}
|
|
if pathIsSecret(secrets, path) {
|
|
return &document.Node{Kind: document.String, Text: "[REDACTED]"}, true
|
|
}
|
|
result := *node
|
|
redacted := false
|
|
switch node.Kind {
|
|
case document.Mapping:
|
|
result.Mapping = make([]document.Entry, len(node.Mapping))
|
|
copy(result.Mapping, node.Mapping)
|
|
sortDocumentMapping(result.Mapping)
|
|
for index := range result.Mapping {
|
|
entry := &result.Mapping[index]
|
|
entry.Value, redacted = safeSnapshotChild(entry.Value, secrets, path.Key(entry.Key), redacted)
|
|
}
|
|
case document.Sequence:
|
|
result.Sequence = make([]*document.Node, len(node.Sequence))
|
|
for index, child := range node.Sequence {
|
|
result.Sequence[index], redacted = safeSnapshotChild(child, secrets, path.Index(index), redacted)
|
|
}
|
|
}
|
|
return &result, redacted
|
|
}
|
|
|
|
func safeSnapshotChild(node *document.Node, secrets map[string]bool, path Path, prior bool) (*document.Node, bool) {
|
|
safe, redacted := safeSnapshotDocument(node, secrets, path)
|
|
return safe, prior || redacted
|
|
}
|
|
|
|
func sortDocumentMapping(entries []document.Entry) {
|
|
slices.SortFunc(entries, func(left, right document.Entry) int {
|
|
return strings.Compare(left.Key, right.Key)
|
|
})
|
|
}
|