package web import ( "encoding/json" "io" "log" "path/filepath" "strings" "testing" "gitea.dooplex.hu/admin/felhom-hub/internal/store" ) // R-975: every secret the operator's edit form writes into config_json is stored sealed. The form is // posted with EVERY field buildConfigJSON reads; each leaf of the stored JSON must then be either on the // short non-secret list below or sealed. A new secret field added to the form without adding its path // to store.configTokenPaths fails here (its leaf is neither). func TestR975_EveryFormSecretIsSealed(t *testing.T) { st, err := store.New(filepath.Join(t.TempDir(), "t.db"), log.New(io.Discard, "", 0)) if err != nil { t.Fatal(err) } t.Cleanup(func() { st.Close() }) form := "cf_tunnel_token=tunnel-canary-r975&cf_api_token=cfapi-canary-r975&git_username=bot&git_token=git-canary-r975&debug_mode=on" js := buildConfigJSON(postForm("/configs/c1/edit", form)) if err := st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", RetrievalPassword: "p", APIKey: "k", ConfigJSON: js}); err != nil { t.Fatal(err) } raw, err := st.RawConfigJSONForTest("c1") if err != nil { t.Fatal(err) } for _, c := range []string{"tunnel-canary-r975", "cfapi-canary-r975", "git-canary-r975"} { if strings.Contains(raw, c) { t.Errorf("stored config_json holds the form's secret %q in plaintext", c) } } notSecret := map[string]bool{"git.username": true, "logging.level": true} var m map[string]any if err := json.Unmarshal([]byte(raw), &m); err != nil { t.Fatal(err) } leaves := 0 var walk func(p string, v any) walk = func(p string, v any) { switch x := v.(type) { case map[string]any: for k, vv := range x { walk(strings.TrimPrefix(p+"."+k, "."), vv) } case string: leaves++ if !notSecret[p] && !strings.HasPrefix(x, "enc:v1:") { t.Errorf("config_json leaf %s from the form is neither on the non-secret list nor sealed", p) } } } walk("", m) if leaves != 5 { t.Fatalf("the form wrote %d leaves, want 5 — a field was added or lost; classify it here", leaves) } // And the hub reads the plaintext back (storedCFToken feeds the Cloudflare zone check). cfg, err := st.GetCustomerConfig("c1") if err != nil || cfg == nil { t.Fatal(err) } if got := storedCFToken(cfg.ConfigJSON); got != "cfapi-canary-r975" { t.Fatalf("storedCFToken after the round trip = %q", got) } }