package engine 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(), }) } } } var inherited []secretDependency for _, operation := range metadata.inheritance { if operation.control != nil { control := operation.control.path paths[control.String()] = control.clone() for _, contributor := range operation.controlOrigin.contributors { paths[contributor.source.String()] = contributor.source.clone() inherited = append(inherited, secretDependency{target: control.clone(), dependency: contributor.source.clone()}) } continue } if operation.transfer == nil { continue } for pointer := range operation.parentOrigins { source, err := ParsePath(pointer) if err != nil { continue } relative, ok := relativeHistoryPath(operation.transfer.from, source) if !ok { continue } destination := appendInheritancePath(operation.transfer.to, relative) paths[pointer] = source paths[destination.String()] = destination.clone() inherited = append(inherited, secretDependency{target: destination, dependency: source}) } } 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 } } for _, link := range inherited { if pathIsSecret(secret, link.dependency) && !secret[link.target.String()] { secret[link.target.String()] = true changed = true } if pathIsSecret(secret, link.target) && !secret[link.dependency.String()] { secret[link.dependency.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 } // Dynamic type replacement can change classification even when projected text // is equal. Validators must preserve this inspection metadata as well as values. func projectedSensitivity(root *document.Node) map[string]Path { result := make(map[string]Path) walkDocument(root, Path{}, func(path Path, node *document.Node) { if node.Sensitive { result[path.String()] = path.clone() } }) return result }