Files
mesh-catalog/modules/rofi/cmd/rofi-tools/secret_test.go
T
jschoubben d26b2d1500
mesh/merge-gate fail: builds rofi → g14, shanks; no bus step; a manifest the change touches fails the module check: rofi claims node-launcher and says it s…
mesh/repo-check fail: its merge-check.sh failed: files asking for a setting without saying whether it is trusted: 21
mesh/delivery superseded: a newer head of the same pull request
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 12:17:45 +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)
}
}
}