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
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.
131 lines
4.7 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|