settings: no layer replaces a value the vault fills, at any depth (issue 610)
mesh/merge-gate head bf6beec465c2 merged into trunk 9f3aa91e812b — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head bf6beec465c2 merged into trunk 9f3aa91e812b — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/merge-gate head bf6beec465c2 merged into trunk 9f3aa91e812b — pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every …
mesh/repo-check head bf6beec465c2 merged into trunk 9f3aa91e812b — pass: its merge-check.sh passed
mesh/delivery delivered
A mergeable file's protected keys were checked at the top level only, so a layer could still set searxng's server.secret_key under the server key its file takes. The merge now refuses any layer value that would replace a ${secret:} placeholder in the file's own content, at any depth, whatever the module's protected list says.
This commit is contained in:
@@ -234,3 +234,29 @@ func TestAServedValueMayBeTheOperators(t *testing.T) {
|
||||
t.Errorf("a setting a served fact asks for is not stray: %v", unused)
|
||||
}
|
||||
}
|
||||
|
||||
// No layer replaces a value the vault fills, at any depth (novox/hq issue 610): searxng's server.secret_key
|
||||
// sits under `server`, a key its file takes, and a layer reaching it was let through by a top-level-only check.
|
||||
func TestNoLayerReplacesAVaultSecretAtAnyDepth(t *testing.T) {
|
||||
file := map[string]any{"id": "settings", "type": "file", "merge": MergeJSON,
|
||||
"content": `{"server": {"secret_key": "${secret:secret}", "port": 8080}, "ui": {"theme": "simple"}, "token": "${secret:token}"}`}
|
||||
layer := func(values map[string]any) []Layer { return []Layer{{From: "anchor", Values: values}} }
|
||||
|
||||
merged, err := ApplySettings(file, layer(map[string]any{"server": map[string]any{"port": float64(9000)}, "ui": map[string]any{"theme": "dark"}}))
|
||||
if err != nil {
|
||||
t.Fatalf("a key beside the secret was refused: %v", err)
|
||||
}
|
||||
if c := merged["content"].(string); !strings.Contains(c, `"secret_key": "${secret:secret}"`) || !strings.Contains(c, `"port": 9000`) {
|
||||
t.Fatalf("the secret or the setting did not land as meant:\n%s", c)
|
||||
}
|
||||
for name, values := range map[string]map[string]any{
|
||||
"the nested secret": {"server": map[string]any{"secret_key": "chosen"}},
|
||||
"the whole map holding it": {"server": "elsewhere"},
|
||||
"a top-level secret": {"token": "chosen"},
|
||||
"the secret, emptied": {"server": map[string]any{"secret_key": ""}},
|
||||
} {
|
||||
if _, err := ApplySettings(file, layer(values)); err == nil || !strings.Contains(err.Error(), "vault") {
|
||||
t.Errorf("%s: %v, want a refusal naming the vault", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user