The review of 2026-10-09 (M4): - give refuses broker (the bus account issue mints) and any own secret the mesh may make itself; - the desk's prompt is asked by module, secret and machine, never with words of the caller's, and the bus denies the prompt's subjects to every principal but the controller, however wide its grant, so the prompt's 'the controller asks' is the bus's word (Permissions.PublishDeny, broker.ControllerOnly); - secret accept with a value is refused through a verb: a value comes from the terminal or the desk; - every value given for an own secret, at the terminal or the desk, raises the urgent condition secret-given on every channel, until the operator silences it.
229 lines
9.2 KiB
Go
229 lines
9.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/conditions"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
"github.com/novox/mesh-controller/internal/secrets"
|
|
)
|
|
|
|
const typed = "123456789:AAEhBP0av28P4XFQnIuR-o-7Xnz1kkUzW3g"
|
|
|
|
// aDesk is the desk path with a prompt the test answers as the operator would, and what it was asked kept.
|
|
func aDesk(t *testing.T, answer func(args map[string]any) (json.RawMessage, error)) (deskGive, *[]string, *[]link.HandAct, *[]map[string]any) {
|
|
t.Helper()
|
|
var accepted []string
|
|
var acts []link.HandAct
|
|
var asked []map[string]any
|
|
return deskGive{
|
|
declares: func(module, name string) error {
|
|
if module != "telegram" || name != "telegram-token" {
|
|
return errors.New(module + " does not declare " + name + " as an own secret")
|
|
}
|
|
return nil
|
|
},
|
|
ask: func(machine string, args map[string]any) (json.RawMessage, error) {
|
|
asked = append(asked, args)
|
|
return answer(args)
|
|
},
|
|
accept: func(value string) (bool, error) { accepted = append(accepted, value); return false, nil },
|
|
record: func(a link.HandAct) error { acts = append(acts, a); return nil },
|
|
}, &accepted, &acts, &asked
|
|
}
|
|
|
|
func sealedTo(t *testing.T, value string) func(args map[string]any) (json.RawMessage, error) {
|
|
return func(args map[string]any) (json.RawMessage, error) {
|
|
sealed, err := secrets.Seal(args["seal_to"].(string), []byte(value+"\n"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
raw, _ := json.Marshal(map[string]any{"sealed": sealed})
|
|
return raw, nil
|
|
}
|
|
}
|
|
|
|
// novox/hq ADR 0259 §10: the value typed at the desk is sealed as `secret accept` seals it, and is in no
|
|
// answer, no prompt argument and no act recorded.
|
|
func TestASecretGivenAtTheDeskIsSealedAndSaidNowhere(t *testing.T) {
|
|
d, accepted, acts, asked := aDesk(t, sealedTo(t, typed))
|
|
words, err := d.give("anchor", "telegram", "telegram-token", "laptop")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(*accepted) != 1 || (*accepted)[0] != typed {
|
|
t.Fatalf("the value sealed is not what was typed, its line ending taken off")
|
|
}
|
|
if len(*acts) != 1 || (*acts)[0].Verb != "secret accept" || (*acts)[0].Cause != "given-at-the-desk" ||
|
|
!strings.Contains((*acts)[0].Why, "at the desk on laptop") {
|
|
t.Errorf("the act: %+v", *acts)
|
|
}
|
|
raw, _ := json.Marshal(struct {
|
|
Words string
|
|
Acts []link.HandAct
|
|
Asked []map[string]any
|
|
}{words, *acts, *asked})
|
|
if strings.Contains(string(raw), typed) || strings.Contains(string(raw), "AAEhBP0") {
|
|
t.Fatal("the value appears in what was said, asked or recorded")
|
|
}
|
|
if !strings.Contains(words, "push anchor") || !strings.Contains(words, "given at the desk on laptop") {
|
|
t.Errorf("%q", words)
|
|
}
|
|
if p := (*asked)[0]; p["seal_to"] == "" || p["timeout_seconds"] != deskPromptWithin {
|
|
t.Errorf("the prompt was asked %v", p)
|
|
}
|
|
}
|
|
|
|
func TestNothingIsAskedForASecretTheMeshWouldRefuse(t *testing.T) {
|
|
for _, c := range [][2]string{{"telegram", "chat-id"}, {"nobody", "telegram-token"}} {
|
|
d, accepted, _, asked := aDesk(t, sealedTo(t, typed))
|
|
if _, err := d.give("anchor", c[0], c[1], "laptop"); err == nil || !strings.Contains(err.Error(), "nobody was asked") {
|
|
t.Errorf("%v: %v", c, err)
|
|
}
|
|
if len(*asked) != 0 || len(*accepted) != 0 {
|
|
t.Errorf("%v: the operator was asked anyway", c)
|
|
}
|
|
}
|
|
d, _, _, _ := aDesk(t, sealedTo(t, typed))
|
|
if _, err := d.give("anchor", "telegram", "telegram-token", ""); err == nil {
|
|
t.Error("no desk was refused nowhere")
|
|
}
|
|
}
|
|
|
|
func TestADismissedEmptyLateOrForeignAnswerTakesNothing(t *testing.T) {
|
|
for want, answer := range map[string]func(map[string]any) (json.RawMessage, error){
|
|
"not answered within 25 seconds": func(map[string]any) (json.RawMessage, error) {
|
|
return json.RawMessage(`{"cancelled":true,"timed_out":true}`), nil
|
|
},
|
|
"was dismissed": func(map[string]any) (json.RawMessage, error) { return json.RawMessage(`{"cancelled":true}`), nil },
|
|
"answered empty": sealedTo(t, " "),
|
|
"not sealed to this call": func(map[string]any) (json.RawMessage, error) {
|
|
other, _, _ := secrets.Keypair()
|
|
sealed, _ := secrets.Seal(other, []byte(typed))
|
|
raw, _ := json.Marshal(map[string]any{"sealed": sealed})
|
|
return raw, nil
|
|
},
|
|
"could not be asked": func(map[string]any) (json.RawMessage, error) { return nil, errors.New("no session answers") },
|
|
} {
|
|
d, accepted, acts, _ := aDesk(t, answer)
|
|
_, err := d.give("anchor", "telegram", "telegram-token", "laptop")
|
|
if err == nil || !strings.Contains(err.Error(), want) || strings.Contains(err.Error(), typed) {
|
|
t.Errorf("want %q, got %v", want, err)
|
|
}
|
|
if len(*accepted) != 0 || len(*acts) != 0 {
|
|
t.Errorf("%s: something was taken or recorded", want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestTheGiveVerbRunsTheDeskPathAndTheControllerMayAskTheDesk(t *testing.T) {
|
|
argv, err := argvFor("give", map[string]any{"node": "anchor", "module": "telegram", "secret": "telegram-token", "at": "laptop"})
|
|
if err != nil || strings.Join(argv, " ") != "secret accept anchor telegram telegram-token --at-desk laptop" {
|
|
t.Fatalf("%v %v", argv, err)
|
|
}
|
|
if _, err := argvFor("give", map[string]any{"node": "anchor", "module": "telegram", "secret": "telegram-token"}); err == nil {
|
|
t.Error("give without a desk was taken")
|
|
}
|
|
perms, err := broker.PermissionsFor(broker.Principal{Kind: broker.KindController})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
found := false
|
|
for _, p := range perms.Publish {
|
|
found = found || p == "mesh.seat.node-launcher.tool.secret.*"
|
|
}
|
|
if !found {
|
|
t.Error("the controller may not ask the desk's prompt")
|
|
}
|
|
}
|
|
|
|
// The review of 2026-10-09 (M4): the desk's prompt says who asks in words the caller does not choose — the
|
|
// controller, which the bus alone lets ask it — and what for, from names the controller checked; the prompt
|
|
// carries no free text of the caller's.
|
|
func TestThePromptIsAskedByNameNeverByWordsTheCallerChose(t *testing.T) {
|
|
d, _, _, asked := aDesk(t, sealedTo(t, typed))
|
|
if _, err := d.give("anchor", "telegram", "telegram-token", "laptop"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p := (*asked)[0]
|
|
if p["module"] != "telegram" || p["secret"] != "telegram-token" || p["node"] != "anchor" {
|
|
t.Errorf("the prompt was not asked by name: %v", p)
|
|
}
|
|
for _, free := range []string{"prompt", "message"} {
|
|
if _, there := p[free]; there {
|
|
t.Errorf("the prompt carries the caller's %s: %v", free, p)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Every value given for a module's own secret is announced as a condition, on every channel (the review of
|
|
// 2026-10-09, M4): a bot token changed by somebody else is a channel that now answers for them.
|
|
func TestAValueGivenAtTheDeskIsAnnounced(t *testing.T) {
|
|
d, _, _, _ := aDesk(t, sealedTo(t, typed))
|
|
var said []string
|
|
d.announce = func(node, module, name, how string) error {
|
|
said = append(said, node+" "+module+" "+name+" "+how)
|
|
return nil
|
|
}
|
|
if _, err := d.give("anchor", "telegram", "telegram-token", "laptop"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(said) != 1 || !strings.Contains(said[0], "anchor telegram telegram-token") || !strings.Contains(said[0], "laptop") {
|
|
t.Fatalf("announced %v", said)
|
|
}
|
|
o := secretGivenObservation("anchor", "telegram", "telegram-token", "at the desk on laptop", time.Date(2026, 10, 9, 12, 3, 0, 0, time.UTC))
|
|
if o.Severity != conditions.Urgent || !strings.Contains(o.Explanation, "telegram-token") ||
|
|
len(o.Actions) == 0 || o.Key() == "" {
|
|
t.Errorf("the announcement %+v", o)
|
|
}
|
|
if strings.Contains(o.Summary+o.Explanation+o.Said, typed) {
|
|
t.Error("the announcement carries the value")
|
|
}
|
|
}
|
|
|
|
// The bus lets the controller alone ask the desk's prompt (the review of 2026-10-09, M4): the runtime, which
|
|
// carries every agent's calls, and a person granted every tool are denied it, however wide their grant.
|
|
func TestOnlyTheControllerMayAskTheDesksPrompt(t *testing.T) {
|
|
for _, p := range []broker.Principal{
|
|
{Kind: broker.KindNodeTools, Node: "laptop"},
|
|
{Kind: broker.KindPerson, Module: "operator", Invokes: []string{"*"}},
|
|
{Kind: broker.KindModule, Node: "laptop", Module: "lab", Invokes: []string{"seat:node-launcher.secret"}},
|
|
} {
|
|
perms, err := broker.PermissionsFor(p)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, subject := range []string{"mesh.seat.node-launcher.tool.secret.laptop", "mesh.seat.node-launcher.tool.secret",
|
|
"mesh.mod.rofi.tool.node-launcher.secret", "mesh.mod.rofi.tool.node-launcher.secret.laptop"} {
|
|
if broker.MayPublish(perms, subject) {
|
|
t.Errorf("%s may publish %s", p.Username(), subject)
|
|
}
|
|
}
|
|
}
|
|
perms, _ := broker.PermissionsFor(broker.Principal{Kind: broker.KindController})
|
|
if !broker.MayPublish(perms, "mesh.seat.node-launcher.tool.secret.laptop") {
|
|
t.Error("the controller may not ask the desk's prompt")
|
|
}
|
|
}
|
|
|
|
// A value for a secret comes from the terminal or the desk, never through a verb (the review of 2026-10-09,
|
|
// M4): `secret accept` with a value, run for a verb, is refused before anything is read.
|
|
func TestASecretValueIsNeverAcceptedThroughAVerb(t *testing.T) {
|
|
t.Setenv(verbVar, "mesh-controller.command")
|
|
for _, args := range [][]string{
|
|
{"accept", "anchor", "telegram", "telegram-token", "--from", "/dev/null"},
|
|
{"accept", "anchor", "app", "db", "--from", "/dev/null", "--provider", "store"},
|
|
} {
|
|
err := secretCommand(context.Background(), args)
|
|
if err == nil || !strings.Contains(err.Error(), "never through a verb") {
|
|
t.Errorf("%v: %v", args, err)
|
|
}
|
|
}
|
|
}
|