Review found a chain through the command verb: set the operator's key to one the caller holds, rotate secrets so they are sealed to it too, read the sealed copies, open them. Whoever may call a verb includes agents (hq ADR 0266), so command now runs an allow list of reading forms, and operator, identity, token, broker, api, licence and every secret command but rotate are refused through any verb.
80 lines
2.9 KiB
Go
80 lines
2.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/novox/mesh-controller/internal/broker"
|
|
"github.com/novox/mesh-controller/internal/link"
|
|
)
|
|
|
|
// consoleWaits is how long the console waits for an answer (mesh-tools node-tools/internal/bus
|
|
// RequestTimeout) — the shortest wait of a caller the mesh ships.
|
|
const consoleWaits = 30 * time.Second
|
|
|
|
// **A call's one answer is never later than its caller or the bus allow** (novox/hq issue 265): a
|
|
// holder answers within AnswerWithin, which must be inside both the console's wait and the window the
|
|
// bus gives an answer. Before, the console waited 30s, the bus 60s, and a push ran as long as it ran.
|
|
func TestAVerbAnswersInsideEveryWaitOnIt(t *testing.T) {
|
|
if link.AnswerWithin >= consoleWaits/2 {
|
|
t.Errorf("a call answers within %s: not well inside the console's %s", link.AnswerWithin, consoleWaits)
|
|
}
|
|
if link.AnswerWithin >= broker.ResponseTTL {
|
|
t.Errorf("a call answers within %s, after the bus stops permitting an answer at %s", link.AnswerWithin, broker.ResponseTTL)
|
|
}
|
|
}
|
|
|
|
// A push — named or through command — answers before it runs: it sends the machine holding the bus
|
|
// first, and the broker reloading its user list forgets the answer it was about to permit.
|
|
func TestAPushAnswersBeforeItSends(t *testing.T) {
|
|
for _, c := range []struct {
|
|
verb string
|
|
args map[string]any
|
|
want bool
|
|
}{
|
|
{"push", map[string]any{"node": "anchor", "why": "w"}, true},
|
|
{"push", map[string]any{"why": "w"}, true},
|
|
{"command", map[string]any{"command": "builds"}, false},
|
|
{"status", map[string]any{}, false},
|
|
{"assign", map[string]any{"node": "anchor", "module": "m"}, false},
|
|
} {
|
|
argv, err := argvFor(c.verb, c.args)
|
|
if err != nil {
|
|
t.Fatalf("%s %v: %v", c.verb, c.args, err)
|
|
}
|
|
if got := answersFirst(argv); got != c.want {
|
|
t.Errorf("%s %v answers first: %v, want %v", c.verb, c.args, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// `calls` is served, takes a call's id and nothing else, and says plainly when it holds no such call.
|
|
func TestCallsIsServedAndSaysWhatItKeeps(t *testing.T) {
|
|
handlers, behind, err := seatToolHandlers()
|
|
if err != nil || len(behind) != 0 {
|
|
t.Fatalf("%v %v", behind, err)
|
|
}
|
|
calls, ok := handlers["calls"]
|
|
if !ok {
|
|
t.Fatal("calls is not served")
|
|
}
|
|
if _, err := calls(context.Background(), json.RawMessage(`{"node":"anchor"}`)); err == nil ||
|
|
!strings.Contains(err.Error(), `"node"`) {
|
|
t.Errorf("calls took an argument it does not declare: %v", err)
|
|
}
|
|
if _, err := calls(context.Background(), json.RawMessage(`{"call":"call-0-0"}`)); err == nil ||
|
|
!strings.Contains(err.Error(), "not across a restart") {
|
|
t.Errorf("an unknown call was not said plainly: %v", err)
|
|
}
|
|
got, err := calls(context.Background(), json.RawMessage(`{}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, listed := got.(map[string]any)["calls"]; !listed {
|
|
t.Errorf("calls answered %v", got)
|
|
}
|
|
}
|