Files
mesh-host/internal/apply/placement_guard_test.go
T
jochen b1cb9542cc
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 superseded: a newer head of the same pull request
Refuse a placement at the machine's own directories, and use a found directory as found
A module's places setting can move a directory anywhere, and the engine
chowned whatever it was pointed at as root: a directory at /etc owned by the
agent account would hand it /etc (hq ADR 0266). Refuse the machine's roots,
the kernel's and the engine's trees, another account's home, and an archive
or written-into file below an agent's home; and leave the owner and mode of a
directory the mesh did not make.
2026-10-08 21:50:23 +02:00

142 lines
6.7 KiB
Go

package apply
// Defends novox/hq ADR 0266 (the third review): the engine places nothing at one of the machine's own
// directories, in the kernel's or its own trees, below a home for another account, or — for an archive or a
// file written into — below an agent's home; and a directory it did not make is used as found.
import (
"context"
"errors"
"os"
"path/filepath"
"strings"
"testing"
"github.com/novox/mesh-host/internal/declaration"
"github.com/novox/mesh-host/internal/store"
)
func withHomes(t *testing.T, homes map[string]homeAccount) {
t.Helper()
was := accountsOfHomes
accountsOfHomes = func() map[string]homeAccount { return homes }
t.Cleanup(func() { accountsOfHomes = was })
}
func TestAMachinesOwnDirectoryIsNeverPlaced(t *testing.T) {
withHomes(t, nil)
for _, path := range []string{"/", "/etc", "/etc/", "/usr", "/var/lib", "/var/lib/mesh", "/home", "/root", "/run"} {
err := refusePlacement(&declaration.Directory{ID: "m.d", Type: declaration.TypeDirectory, Path: path, Owner: "agent"}, nil)
var refused *PlacementRefusedError
if !errors.As(err, &refused) {
t.Errorf("%s as a directory: refused, got %v", path, err)
}
}
for _, path := range []string{"/etc/sudoers.d/x", "/var/lib/mesh/daemons", "/var/lib/postgres", "/usr/local/bin/claude-agent"} {
if err := refusePlacement(&declaration.Directory{ID: "m.d", Type: declaration.TypeDirectory, Path: path}, nil); err != nil {
t.Errorf("%s: a module's own place below a root passes, got %v", path, err)
}
}
for _, path := range []string{"/proc/sys/x", "/sys/x", "/dev/x", "/boot/x", "/var/lib/mesh-host/state.json", "/var/lib/mesh-host"} {
if err := refusePlacement(&declaration.File{ID: "m.f", Type: declaration.TypeFile, Path: path, Content: "x"}, nil); err == nil {
t.Errorf("%s: nothing is placed there", path)
}
}
if err := refusePlacement(&declaration.File{ID: "mesh-host.launcher", Type: declaration.TypeFile,
Path: "/usr/lib/nox-mesh-host/launch", Content: "x"}, nil); err != nil {
t.Errorf("the engine's own module places its builds: %v", err)
}
}
func TestBelowAHomeOnlyThatAccountsFilesArePlaced(t *testing.T) {
withHomes(t, map[string]homeAccount{"/home/operator": {"operator", 1000}, "/home/agent": {"agent", 1001}})
cases := []struct {
r declaration.Resource
ok bool
}{
{&declaration.Directory{ID: "a.d", Type: declaration.TypeDirectory, Path: "/home/agent/.claude", Owner: "agent"}, true},
{&declaration.Directory{ID: "a.d", Type: declaration.TypeDirectory, Path: "/home/agent/.claude", Owner: "1001:1001"}, true},
{&declaration.Directory{ID: "a.d", Type: declaration.TypeDirectory, Path: "/home/agent/.claude"}, false},
{&declaration.Directory{ID: "a.d", Type: declaration.TypeDirectory, Path: "/home/agent/.claude", Owner: "operator"}, false},
{&declaration.Directory{ID: "a.d", Type: declaration.TypeDirectory, Path: "/home/agent", Owner: "agent"}, false},
{&declaration.File{ID: "a.f", Type: declaration.TypeFile, Path: "/home/operator/.zshrc", Owner: "operator", Content: "x"}, true},
}
for _, c := range cases {
if err := refusePlacement(c.r, nil); (err == nil) != c.ok {
t.Errorf("%s owned by %q: ok %v, got %v", c.r.Target(), ownerName(c.r), c.ok, err)
}
}
}
func TestNothingIsUnpackedOrWrittenIntoBelowAnAgentsHome(t *testing.T) {
withHomes(t, map[string]homeAccount{"/home/agent": {"agent", 1001}})
agent := []string{"/home/agent"}
if err := refusePlacement(&declaration.Archive{ID: "a.x", Type: declaration.TypeArchive, Path: "/home/agent/.local/x", Owner: "agent"}, agent); err == nil {
t.Error("an archive below an agent's home is refused")
}
if err := refusePlacement(&declaration.File{ID: "a.f", Type: declaration.TypeFile, Path: "/home/agent/.claude.json", Owner: "agent", Into: "json", Content: "{}"}, agent); err == nil {
t.Error("a file written into below an agent's home is refused")
}
if err := refusePlacement(&declaration.File{ID: "a.f", Type: declaration.TypeFile, Path: "/home/agent/x", Owner: "agent", Content: "x"}, agent); err != nil {
t.Errorf("a whole file there passes: %v", err)
}
d := parse(t, `{"declaration":1,"resources":[{"id":"c.agent","type":"user","name":"agent","root":"never"},`+
`{"id":"c.op","type":"user","name":"operator"}]}`)
if got := rootNeverHomes(d); len(got) != 1 || got[0] != "/home/agent" {
t.Errorf("the agent's home is known by the user database: %v", got)
}
}
func TestADirectoryAtEtcIsRefusedThroughTheApplyAndNothingIsTouched(t *testing.T) {
withHomes(t, nil)
l := &logins{shells: map[string]string{}}
report, _, err := Apply(context.Background(), archHost(t), parse(t,
`{"declaration":1,"resources":[{"id":"m.state","type":"directory","path":"/etc","owner":"agent","mode":"0755"}]}`),
store.State{}, store.OriginDeclared, l.run, nil, nil)
if err == nil || !strings.Contains(err.Error(), "machine's own directories") {
t.Fatalf("refused: %v %+v", err, report)
}
}
func TestADirectoryFoundHereIsUsedAsFound(t *testing.T) {
dir := filepath.Join(t.TempDir(), "found")
if err := os.Mkdir(dir, 0o700); err != nil {
t.Fatal(err)
}
r := &declaration.Directory{ID: "m.d", Type: declaration.TypeDirectory, Path: dir, Mode: "0755"}
out, err := applyDirectory(r, store.Applied{}, true)
if err != nil || !out.asFound || !strings.Contains(out.Detail, "used as found") {
t.Fatalf("a found directory is used as found: %+v %v", out, err)
}
if info, _ := os.Stat(dir); info.Mode().Perm() != 0o700 {
t.Fatalf("its mode was changed: %o", info.Mode().Perm())
}
// Recorded as found, it stays found.
out, _ = applyDirectory(r, store.Applied{ID: "m.d", Target: dir, AsFound: true}, true)
if !out.asFound {
t.Fatal("a directory recorded as found stays found")
}
// The mesh's by its record from an earlier apply: converged as before.
out, err = applyDirectory(r, store.Applied{ID: "m.d", Target: dir}, true)
if err != nil || out.asFound {
t.Fatalf("a directory the mesh applied before is converged: %+v %v", out, err)
}
if info, _ := os.Stat(dir); info.Mode().Perm() != 0o755 {
t.Fatalf("not converged: %o", info.Mode().Perm())
}
// Already as declared: the mesh's from here on.
other := filepath.Join(t.TempDir(), "same")
if err := os.Mkdir(other, 0o755); err != nil {
t.Fatal(err)
}
if out, _ := applyDirectory(&declaration.Directory{ID: "m.e", Type: declaration.TypeDirectory, Path: other, Mode: "0755"}, store.Applied{}, true); out.asFound {
t.Fatal("a found directory already as declared is the mesh's")
}
if action, _, err := remove(context.Background(), nil, store.Applied{Type: "directory", Target: dir, AsFound: true}, nil, nil); err != nil || action != "forgotten" {
t.Fatalf("a directory used as found is never removed: %s %v", action, err)
}
if _, err := os.Stat(dir); err != nil {
t.Fatal("it is still there")
}
}