142 lines
3.7 KiB
Go
142 lines
3.7 KiB
Go
package configkit
|
|
|
|
import "gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
|
|
|
type secretDependency struct {
|
|
target Path
|
|
dependency Path
|
|
}
|
|
|
|
func classifySecretPaths(schema *schemaDescriptor, predicate func(Path) bool, metadata Metadata, final *document.Node, applications []historyApplication, projections ...*document.Node) map[string]bool {
|
|
paths := make(map[string]Path)
|
|
finalPaths := make(map[string]*document.Node)
|
|
indexDocument(final, Path{}, finalPaths)
|
|
for pointer := range finalPaths {
|
|
addSecretPath(paths, pointer)
|
|
}
|
|
for _, application := range applications {
|
|
applicationPaths := make(map[string]*document.Node)
|
|
indexDocument(application.value, application.path, applicationPaths)
|
|
for pointer := range applicationPaths {
|
|
addSecretPath(paths, pointer)
|
|
}
|
|
}
|
|
var dependencies []secretDependency
|
|
for pointer, history := range metadata.history {
|
|
addSecretPath(paths, pointer)
|
|
for _, change := range history {
|
|
paths[change.Path.String()] = change.Path.clone()
|
|
for _, dependency := range change.Dependencies {
|
|
paths[dependency.String()] = dependency.clone()
|
|
dependencies = append(dependencies, secretDependency{
|
|
target: change.Path.clone(), dependency: dependency.clone(),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
secret := make(map[string]bool)
|
|
for _, tree := range append(projections, final) {
|
|
projected := make(map[string]*document.Node)
|
|
indexDocument(tree, Path{}, projected)
|
|
for pointer, node := range projected {
|
|
addSecretPath(paths, pointer)
|
|
if node.Sensitive {
|
|
secret[pointer] = true
|
|
}
|
|
}
|
|
}
|
|
for pointer, path := range paths {
|
|
if root, ok := schemaSecretRoot(schema, path); ok {
|
|
secret[root.String()] = true
|
|
}
|
|
if predicate != nil && predicate(path.clone()) {
|
|
secret[pointer] = true
|
|
}
|
|
}
|
|
for {
|
|
changed := false
|
|
for _, dependency := range dependencies {
|
|
if secretIntersectsPath(paths, secret, dependency.dependency) && !secret[dependency.target.String()] {
|
|
secret[dependency.target.String()] = true
|
|
changed = true
|
|
}
|
|
}
|
|
if !changed {
|
|
return secret
|
|
}
|
|
}
|
|
}
|
|
|
|
func addSecretPath(paths map[string]Path, pointer string) {
|
|
if path, err := ParsePath(pointer); err == nil {
|
|
paths[pointer] = path
|
|
}
|
|
}
|
|
|
|
func secretIntersectsPath(paths map[string]Path, secret map[string]bool, candidate Path) bool {
|
|
for pointer := range secret {
|
|
classified, ok := paths[pointer]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if pathContains(classified, candidate) || pathContains(candidate, classified) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func schemaSecretRoot(schema *schemaDescriptor, path Path) (Path, bool) {
|
|
if schema == nil || schema.root == nil || !path.valid() {
|
|
return Path{}, false
|
|
}
|
|
current := schema.root
|
|
walked := Path{}
|
|
for _, segment := range path.segments {
|
|
walked.segments = append(walked.segments, segment)
|
|
for current.kind == descriptorPointer {
|
|
current = current.element
|
|
}
|
|
switch current.kind {
|
|
case descriptorStruct:
|
|
if segment.kind == pathSegmentIndex {
|
|
return Path{}, false
|
|
}
|
|
position, ok := current.fieldByName[segment.key]
|
|
var field *fieldDescriptor
|
|
if ok {
|
|
field = ¤t.fields[position]
|
|
} else {
|
|
field = current.inlineMap
|
|
}
|
|
if field == nil {
|
|
return Path{}, false
|
|
}
|
|
if field.secret {
|
|
return walked.clone(), true
|
|
}
|
|
if field.inlineMap {
|
|
current = field.descriptor.element
|
|
} else {
|
|
current = field.descriptor
|
|
}
|
|
case descriptorMap:
|
|
if segment.kind == pathSegmentIndex {
|
|
return Path{}, false
|
|
}
|
|
current = current.element
|
|
case descriptorSlice, descriptorArray:
|
|
if _, ok := schemaIndex(segment); !ok {
|
|
return Path{}, false
|
|
}
|
|
current = current.element
|
|
case descriptorDynamic:
|
|
return Path{}, false
|
|
default:
|
|
return Path{}, false
|
|
}
|
|
}
|
|
return Path{}, false
|
|
}
|