mesh/merge-gate pass: builds mesh-host → ace, g14, novox, 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
mesh/delivery delivered
mesh/delivery-group group fix/358-judge-directories-read-only delivered: every member is delivered
Once a mesh directory's mode and owner go only through a walk that follows no link another account could make, a directory behind such a link is refused and its module gated: on the control-node that could stop the controller. The operator is to be told which directories that walk would refuse on every node before it lands. So the engine judges the declaration it keeps with the same walk, making and changing nothing: on a signed ask from the controller (mesh.node.<node>.ask.judge-directories, held to the hand-over's checks) and as `nox-mesh-host judge-directories` at the machine.
53 lines
2.6 KiB
Go
53 lines
2.6 KiB
Go
package link
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// The subject and the signing context the controller holds the same words for (mesh-controller internal/link).
|
|
func TestTheDirectoryJudgementAskKeepsItsSubjectAndContext(t *testing.T) {
|
|
if AskJudgeDirectoriesSubject("laptop") != "mesh.node.laptop.ask.judge-directories" {
|
|
t.Fatalf("the subject is %q", AskJudgeDirectoriesSubject("laptop"))
|
|
}
|
|
if JudgeDirectoriesContext != "novox-mesh judge-directories v1\n" || JudgeDirectoriesContext == HandOverContext ||
|
|
JudgeDirectoriesContext == SetuidSearchContext {
|
|
t.Fatalf("the signing context is %q", JudgeDirectoriesContext)
|
|
}
|
|
}
|
|
|
|
// A judgement ask is believed as a hand-over is: signed by the mesh under its own context, for this node,
|
|
// unexpired, once, and naming no path.
|
|
func TestADirectoryJudgementAskIsBelievedOnlySignedForThisNodeAndOnce(t *testing.T) {
|
|
m, key := aMember(t)
|
|
now := time.Date(2026, 10, 12, 14, 0, 0, 0, time.UTC)
|
|
var asked []string
|
|
handle := func(by string) (string, error) { asked = append(asked, by); return "3 directories declared", nil }
|
|
check := HandOverCheck{Signer: m.Signer, Node: "n1", Now: func() time.Time { return now }, Nonces: &Nonces{}}
|
|
good := HandOverAsk{Node: "n1", By: "jo", Expires: now.Add(HandOverGood), Nonce: aNonce}
|
|
for name, body := range map[string][]byte{
|
|
"a setuid search's signature": signedSearch(t, key, SetuidSearchContext, good),
|
|
"another node": signedSearch(t, key, JudgeDirectoriesContext, HandOverAsk{Node: "n2", By: "jo", Expires: good.Expires, Nonce: aNonce}),
|
|
"a path": signedSearch(t, key, JudgeDirectoriesContext, HandOverAsk{Node: "n1", Path: "/x", By: "jo", Expires: good.Expires, Nonce: aNonce}),
|
|
"expired": signedSearch(t, key, JudgeDirectoriesContext, HandOverAsk{Node: "n1", By: "jo", Expires: now, Nonce: aNonce}),
|
|
} {
|
|
if a := AnswerJudgeDirectories(check, handle, body); !strings.HasSuffix(a.Refused, "nothing was judged") || a.Said != "" {
|
|
t.Errorf("%s: %+v", name, a)
|
|
}
|
|
}
|
|
if len(asked) != 0 {
|
|
t.Fatalf("a refused ask reached the handler: %v", asked)
|
|
}
|
|
body := signedSearch(t, key, JudgeDirectoriesContext, good)
|
|
if a := AnswerJudgeDirectories(check, handle, body); a.Said != "3 directories declared" || len(asked) != 1 {
|
|
t.Fatalf("the good ask: %+v, %v", a, asked)
|
|
}
|
|
if a := AnswerJudgeDirectories(check, handle, body); !strings.Contains(a.Refused, "taken already") {
|
|
t.Fatalf("the same ask again: %+v", a)
|
|
}
|
|
if _, answer := JudgeDirectoriesReply(check, handle, "", body); answer {
|
|
t.Fatal("a request with no reply subject was answered")
|
|
}
|
|
}
|