Files
mesh-catalog/modules/rofi/cmd/rofi-tools/secret_test.go
T
jschoubben f5cbfd850c
mesh/merge-gate pass: builds rofi → g14, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
rofi: write the hidden prompt from names and say the controller asks, never with the caller's words
The review of 2026-10-09 (M4): the prompt and its message were the caller's free text, so whoever could
ask the verb could put any words before the operator. The verb now takes the module, the secret and the
machine, checked as names, and writes the prompt here, saying the mesh's controller asks — which the bus
makes true, denying the verb to every other principal (mesh-controller #156). prompt and message are refused.
2026-10-09 17:08:17 +02:00

131 lines
4.7 KiB
Go

package main
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/crypto/nacl/box"
)
const typedValue = "123456789:AAEhBP0av28P4XFQnIuR-o-7Xnz1kkUzW3g"
func aKey(t *testing.T) (string, *[32]byte, *[32]byte) {
t.Helper()
pub, priv, err := box.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
return base64.StdEncoding.EncodeToString(pub[:]), pub, priv
}
// novox/hq ADR 0259 §10: what the operator types in the hidden prompt is answered sealed to the asker's
// key, and never in the clear — not in the answer, not in an error.
func TestWhatIsTypedIsAnsweredSealedToTheAskersKeyAndNeverInTheClear(t *testing.T) {
withSession(t)
bin := fakeBinaries(t, map[string]string{"rofi": `echo "$*" > "$LOG"; echo "` + typedValue + `"`})
t.Setenv("LOG", filepath.Join(bin, "log"))
key, pub, priv := aKey(t)
q, err := secretQuestion(map[string]any{"module": "telegram", "secret": "telegram-token", "node": "anchor", "seal_to": key})
if err != nil {
t.Fatal(err)
}
got, err := Secret(q)
if err != nil || got.Cancelled || got.Sealed == "" {
t.Fatalf("%+v %v", got, err)
}
raw, _ := json.Marshal(got)
if strings.Contains(string(raw), typedValue) || strings.Contains(string(raw), "AAEhBP0") {
t.Fatal("the answer carries what was typed in the clear")
}
blob, _ := base64.StdEncoding.DecodeString(got.Sealed)
opened, ok := box.OpenAnonymous(nil, blob, pub, priv)
if !ok || string(opened) != typedValue {
t.Fatalf("the answer does not open to what was typed, its line ending taken off")
}
asked, _ := os.ReadFile(filepath.Join(bin, "log"))
for _, want := range []string{"-dmenu", "-password", "-p telegram-token for telegram",
"-mesg The mesh's controller asks for telegram-token, the own secret of telegram on anchor"} {
if !strings.Contains(string(asked), want) {
t.Errorf("rofi was not asked %q: %s", want, asked)
}
}
if strings.Contains(string(asked), "-no-custom") {
t.Error("the prompt takes no typed answer")
}
}
func TestADismissedEmptyOrLatePromptIsACancelAndAFailureNeverSaysTheValue(t *testing.T) {
withSession(t)
key, _, _ := aKey(t)
q, _ := secretQuestion(map[string]any{"module": "telegram", "secret": "telegram-token", "node": "anchor", "seal_to": key, "timeout_seconds": 1})
for script, timedOut := range map[string]bool{`exit 1`: false, `echo ""`: false, `sleep 30`: true} {
fakeBinaries(t, map[string]string{"rofi": script})
got, err := Secret(q)
if err != nil || !got.Cancelled || got.TimedOut != timedOut || got.Sealed != "" {
t.Errorf("%s: %+v %v", script, got, err)
}
}
fakeBinaries(t, map[string]string{"rofi": `echo "` + typedValue + `"; echo "rofi broke" >&2; exit 3`})
if _, err := Secret(q); err == nil || strings.Contains(err.Error(), typedValue) || !strings.Contains(err.Error(), "rofi broke") {
t.Errorf("a failure said %v", err)
}
}
func TestASecretIsAskedOnlyWithAKeyToSealItTo(t *testing.T) {
key := base64.StdEncoding.EncodeToString(make([]byte, 32))
named := func(extra map[string]any) map[string]any {
a := map[string]any{"module": "telegram", "secret": "telegram-token", "node": "anchor"}
for k, v := range extra {
a[k] = v
}
return a
}
for _, args := range []map[string]any{
named(nil),
named(map[string]any{"seal_to": "not a key"}),
named(map[string]any{"seal_to": base64.StdEncoding.EncodeToString([]byte("short"))}),
named(map[string]any{"seal_to": key, "timeout_seconds": 60}),
{"seal_to": key},
} {
if _, err := secretQuestion(args); err == nil {
t.Errorf("%v was taken", args)
}
}
}
// The prompt is this holder's words from names, never the caller's (the review of 2026-10-09, M4): it says the
// mesh's controller asks — the bus lets nobody else ask it — and for which secret of which module on which
// machine; words of the caller's, and a name that is not one, are refused.
func TestThePromptIsWrittenHereFromNamesAndSaysTheControllerAsks(t *testing.T) {
key := base64.StdEncoding.EncodeToString(make([]byte, 32))
base := map[string]any{"module": "telegram", "secret": "telegram-token", "node": "anchor", "seal_to": key}
q, err := secretQuestion(base)
if err != nil {
t.Fatal(err)
}
if q.Prompt != "telegram-token for telegram" || !strings.HasPrefix(q.Message, "The mesh's controller asks for telegram-token") {
t.Errorf("prompt %q, message %q", q.Prompt, q.Message)
}
for key, value := range map[string]any{
"prompt": "Your bank's PIN",
"message": "Your bank asks",
"module": "telegram\nYour bank asks",
"secret": "a token",
"node": "<b>anchor</b>",
} {
args := map[string]any{}
for k, v := range base {
args[k] = v
}
args[key] = value
if _, err := secretQuestion(args); err == nil {
t.Errorf("%s=%q was taken", key, value)
}
}
}