package delivery // The own secrets a node's shadow answer finds the vault holds another value for than the node runs (novox/hq issue 1126, // cause C4): the vault's `move-in` never replaces a value it holds, so a value the controller made anew later (a rotation // at start, a re-issue) runs while the vault keeps the first one it was handed. The node opens both seals with its own // key and its answer's shown bodies mask the value as MaskedAnotherValue; this reads which own secrets those are, for the // node agent (which hands the vault what runs) and for the vault's `own_drift` (which lists them mesh-wide), so both read // the answer one way. import ( "sort" "strings" "git.novox.be/novox/mesh-sdk/go/catalogue" ) // MaskedAnotherValue is the word a shown body masks a value with when the node opened both sides with its key and they // are two values (MaskedPair, maskWord). const MaskedAnotherValue = "" // OwnValueDiffers is one own secret of a module whose value composed (the vault's) is another than the one that runs. type OwnValueDiffers struct { Module string `json:"module"` Name string `json:"name"` } // ownNeedPrefix starts the id of the file a module's own secret is placed in (catalogue.NeedID). var ownNeedPrefix = catalogue.NeedID("") // OwnNeedName is the name of module's own secret whose file a resource id names, and whether it names one. On a node a // resource's id carries its module's name (`only-office.needs-jwt`, as a live shadow answer shows it); the bare form a // declaration composes (`needs-jwt`, catalogue.NeedID) is read too. An id of another module's is not this module's. func OwnNeedName(module, id string) (string, bool) { if module != "" { id = strings.TrimPrefix(id, module+".") } name, own := strings.CutPrefix(id, ownNeedPrefix) return name, own && name != "" && !strings.Contains(name, ".") } // OwnValuesDiffer is every own secret a's shown bodies say holds another value composed than running: a body of a // module's own secret's file (OwnNeedName: `.needs-` on a node) masked MaskedAnotherValue on either side. Sorted by module and name, each // once. A body the answer did not show (beyond MaxBodies) is not read. func OwnValuesDiffer(a CandidateAnswer) []OwnValueDiffers { seen := map[OwnValueDiffers]bool{} var out []OwnValueDiffers for _, d := range a.Differs { for _, b := range d.Bodies { name, own := OwnNeedName(d.Module, b.ID) if !own || !anotherValue(b) { continue } o := OwnValueDiffers{Module: d.Module, Name: name} if !seen[o] { seen[o] = true out = append(out, o) } } } sort.Slice(out, func(i, j int) bool { if out[i].Module != out[j].Module { return out[i].Module < out[j].Module } return out[i].Name < out[j].Name }) return out } // ValuesDiffer is how many shown bodies of a hold a value masked MaskedAnotherValue, own secrets and every other // resource alike: the count a node's answer carries as CandidateAnswer.ValuesDiffer. func ValuesDiffer(a CandidateAnswer) int { n := 0 for _, d := range a.Differs { for _, b := range d.Bodies { if anotherValue(b) { n++ } } } return n } // WithValuesDiffer is answers with each one's ValuesDiffer counted. func WithValuesDiffer(answers map[string]CandidateAnswer) map[string]CandidateAnswer { out := make(map[string]CandidateAnswer, len(answers)) for g, a := range answers { a.ValuesDiffer = ValuesDiffer(a) out[g] = a } return out } func anotherValue(b ResourceBodies) bool { return strings.Contains(b.Composed, MaskedAnotherValue) || strings.Contains(b.Running, MaskedAnotherValue) }