mesh/merge-gate head fe2040c65ea2 merged into trunk 53e51864969f — pass: builds baserow, gitea, grafana, home-assistant, icecast, influxdb, letta, mailu, n…
mesh/repo-check head fe2040c65ea2 merged into trunk 53e51864969f — pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the ch…
mesh/delivery-group group fix/342-a-secret-is-never-a-setting ready: every member ready, and composed together they pass
mesh/delivery superseded: a newer head of the same pull request
A setting outranked the vault: baserow, grafana, letta and unifi merged a layer's
password over the ${secret:...} in their runtime config, and the gitea,
home-assistant, icecast, influxdb, mailu and nextcloud clients read the settings
file before the vault's file. A secret in a setting cannot be rotated and is read
back by the settings verb.
The four files now keep their password protected, so the controller refuses a
setting of it. The six clients ignore a secret in the settings file and say so
once, by key and never with the value; icecast reads its admin password from the
vault's file. checks/secrets refuses a mergeable file whose secret a layer could
replace; searxng's nested server.secret_key is listed as known until the
controller protects a nested key.
79 lines
2.8 KiB
Go
79 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func manifest(t *testing.T, root, module, resources string) {
|
|
t.Helper()
|
|
dir := filepath.Join(root, "modules", module)
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
body := `{"module":"` + module + `","resources":[` + resources + `]}`
|
|
if err := os.WriteFile(filepath.Join(dir, "module.json"), []byte(body), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestASecretASettingCouldReplaceIsRefused(t *testing.T) {
|
|
root := t.TempDir()
|
|
// Refused: the key holds the vault's secret and nothing keeps a layer off it.
|
|
manifest(t, root, "a", `{"id":"cfg","type":"file","merge":"json","content":"{\"password\": \"${secret:admin}\"}"}`)
|
|
// Refused: nested below a key the file takes.
|
|
manifest(t, root, "b", `{"id":"cfg","type":"file","merge":"json","takes-settings":["server"],`+
|
|
`"content":"{\"server\": {\"secret_key\": \"${secret:s}\", \"port\": 1}}"}`)
|
|
// Kept: protected.
|
|
manifest(t, root, "c", `{"id":"cfg","type":"file","merge":"json","protected":["password"],`+
|
|
`"content":"{\"password\": \"${secret:admin}\", \"user\": \"admin\"}"}`)
|
|
// Kept: the file names its keys, and not this one.
|
|
manifest(t, root, "d", `{"id":"cfg","type":"file","merge":"json","takes-settings":["ui"],`+
|
|
`"content":"{\"token\": \"${secret:t}\", \"ui\": {}}"}`)
|
|
// Kept: a file that does not merge takes no setting.
|
|
manifest(t, root, "e", `{"id":"cfg","type":"file","content":"{\"token\": \"${secret:t}\"}"}`)
|
|
// Refused: protecting another key does not protect this one; a list element counts too.
|
|
manifest(t, root, "f", `{"id":"cfg","type":"file","merge":"json","protected":["user"],`+
|
|
`"content":"{\"users\": [{\"password\": \"${secret:p}\"}]}"}`)
|
|
found, err := Check(root)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := []string{"a cfg password", "b cfg server.secret_key", "f cfg users[0].password"}
|
|
if len(found) != len(want) {
|
|
t.Fatalf("found %v, want %v", found, want)
|
|
}
|
|
for i, f := range found {
|
|
if f.String() != want[i] {
|
|
t.Fatalf("found %v, want %v", found, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTheKnownListOnlyShrinks(t *testing.T) {
|
|
found := []Finding{{"b", "cfg", "server.secret_key"}, {"a", "cfg", "password"}}
|
|
refused, stale := Judge(found, map[string]bool{"b cfg server.secret_key": true, "gone cfg token": true})
|
|
if len(refused) != 1 || refused[0].String() != "a cfg password" {
|
|
t.Fatalf("refused %v", refused)
|
|
}
|
|
if len(stale) != 1 || stale[0] != "gone cfg token" {
|
|
t.Fatalf("stale %v", stale)
|
|
}
|
|
}
|
|
|
|
func TestTheCatalogueHoldsNoSecretASettingCouldReplace(t *testing.T) {
|
|
found, err := Check("../..")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
known, err := Known("known")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
refused, stale := Judge(found, known)
|
|
if len(refused) > 0 || len(stale) > 0 {
|
|
t.Fatalf("refused %v, stale %v", refused, stale)
|
|
}
|
|
}
|