//go:build linux package apply import ( "context" "errors" "os" "path/filepath" "strings" "syscall" "testing" "github.com/novox/mesh-host/internal/store" ) // Defends novox/hq ADR 0266 (the review of 2026-10-08): below a home, the node-engine — root — follows no // symbolic link an account can put there: not to chown or chmod, not to write, not to make a directory. // aLinkedHome is a home the test names as one, with `.claude` replaced by a link to a directory outside it, // as an account would to have root change /etc. func aLinkedHome(t *testing.T) (home, outside string) { t.Helper() root := t.TempDir() home = filepath.Join(root, "home", "agent") outside = filepath.Join(root, "etc") for _, d := range []string{home, outside} { if err := os.MkdirAll(d, 0o755); err != nil { t.Fatal(err) } } if err := os.WriteFile(filepath.Join(outside, "shadow"), []byte("root's"), 0o600); err != nil { t.Fatal(err) } was := homes homes = func() []string { return []string{home} } t.Cleanup(func() { homes = was }) return home, outside } func link(t *testing.T, target, at string) { t.Helper() if err := os.Symlink(target, at); err != nil { t.Fatal(err) } } func applyOneResource(t *testing.T, resource string) error { t.Helper() d := declare(t, resource) _, _, err := Apply(context.Background(), archHost(t), d, store.State{}, store.OriginDeclared, noServices, nil, nil) return err } func mustBeLinkRefusal(t *testing.T, err error) { t.Helper() var refused *LinkUnderHomeError if !errors.As(err, &refused) { t.Fatalf("refused as a link under a home, got %v", err) } if !strings.Contains(err.Error(), "never follows a link") { t.Fatalf("said in words: %v", err) } } func modeOfPath(t *testing.T, p string) os.FileMode { t.Helper() info, err := os.Stat(p) if err != nil { t.Fatal(err) } return info.Mode().Perm() } func TestADirectoryUnderAHomeThatIsALinkIsRefusedAndItsTargetUntouched(t *testing.T) { home, outside := aLinkedHome(t) link(t, outside, filepath.Join(home, ".claude")) err := applyOneResource(t, `{"id":"claude-code.agent-home","type":"directory","path":"`+ filepath.Join(home, ".claude")+`","mode":"0700"}`) mustBeLinkRefusal(t, err) if m := modeOfPath(t, outside); m != 0o755 { t.Fatalf("the link's target was chmodded to %o", m) } } func TestAFileThroughALinkedParentUnderAHomeIsRefused(t *testing.T) { home, outside := aLinkedHome(t) link(t, outside, filepath.Join(home, ".claude")) err := applyOneResource(t, `{"id":"m.f","type":"file","path":"`+filepath.Join(home, ".claude", "shadow")+ `","content":"the mesh's\n","mode":"0644"}`) mustBeLinkRefusal(t, err) if got, _ := os.ReadFile(filepath.Join(outside, "shadow")); string(got) != "root's" { t.Fatalf("written through the link: %q", got) } if m := modeOfPath(t, filepath.Join(outside, "shadow")); m != 0o600 { t.Fatalf("chmodded through the link: %o", m) } } func TestAFileThatIsItselfALinkUnderAHomeIsRefused(t *testing.T) { home, outside := aLinkedHome(t) link(t, filepath.Join(outside, "shadow"), filepath.Join(home, ".zshrc")) err := applyOneResource(t, `{"id":"m.f","type":"file","path":"`+filepath.Join(home, ".zshrc")+ `","content":"x\n"}`) mustBeLinkRefusal(t, err) if got, _ := os.ReadFile(filepath.Join(outside, "shadow")); string(got) != "root's" { t.Fatalf("written through the link: %q", got) } } func TestADirectoryAndAFileUnderAHomeAreMadeAsBefore(t *testing.T) { home, _ := aLinkedHome(t) dir := filepath.Join(home, ".claude") if err := applyOneResource(t, `{"id":"m.d","type":"directory","path":"`+dir+`","mode":"0700"}`); err != nil { t.Fatal(err) } if m := modeOfPath(t, dir); m != 0o700 { t.Fatalf("mode %o", m) } file := filepath.Join(home, ".config", "mesh", "env.sh") if err := applyOneResource(t, `{"id":"m.f","type":"file","path":"`+file+`","content":"A=1\n","mode":"0640"}`); err != nil { t.Fatal(err) } if got, _ := os.ReadFile(file); string(got) != "A=1\n" || modeOfPath(t, file) != 0o640 { t.Fatalf("written %q mode %o", got, modeOfPath(t, file)) } } // The descriptor half: a link put in place after any check is still not followed. func TestTheDescriptorCallsFollowNoLinkBelowAHome(t *testing.T) { home, outside := aLinkedHome(t) link(t, outside, filepath.Join(home, ".claude")) mustBeLinkRefusal(t, chmodPath(filepath.Join(home, ".claude"), 0o777)) mustBeLinkRefusal(t, chmodPath(filepath.Join(home, ".claude", "shadow"), 0o777)) if m := modeOfPath(t, filepath.Join(outside, "shadow")); m != 0o600 { t.Fatalf("chmodded through the link: %o", m) } mustBeLinkRefusal(t, writeAtomically(filepath.Join(home, ".claude", "new"), []byte("x"), 0o644)) if _, err := os.Stat(filepath.Join(outside, "new")); !os.IsNotExist(err) { t.Fatal("written through the link") } if _, err := makeDirsSaying(filepath.Join(home, ".claude", "a", "b"), 0o755, ""); err == nil { t.Fatal("made directories through the link") } if _, err := os.Stat(filepath.Join(outside, "a")); !os.IsNotExist(err) { t.Fatal("made a directory through the link") } if _, err := readPath(filepath.Join(home, ".claude", "shadow")); err == nil { t.Fatal("read through the link") } me := os.Getuid() // A link itself is owned as a link, never its target. if err := chownPath(filepath.Join(home, ".claude"), me, os.Getgid()); err != nil { t.Fatalf("a link is owned as a link: %v", err) } } func TestRemovalThroughALinkUnderAHomeIsRefused(t *testing.T) { home, outside := aLinkedHome(t) link(t, outside, filepath.Join(home, ".claude")) _, _, err := remove(context.Background(), archHost(t), store.Applied{ID: "m.f", Type: "file", Target: filepath.Join(home, ".claude", "shadow")}, nil, nil) mustBeLinkRefusal(t, err) if _, err := os.Stat(filepath.Join(outside, "shadow")); err != nil { t.Fatal("removed through the link") } } func TestAPathOutsideEveryHomeIsHandledAsBefore(t *testing.T) { _, outside := aLinkedHome(t) elsewhere := filepath.Join(filepath.Dir(outside), "srv") if err := os.MkdirAll(elsewhere, 0o755); err != nil { t.Fatal(err) } link(t, outside, filepath.Join(elsewhere, "linked")) if homeAbove(filepath.Join(elsewhere, "linked", "x")) != "" { t.Fatal("not below a home") } if err := refuseLinksUnderHome(filepath.Join(elsewhere, "linked", "x")); err != nil { t.Fatal("a system path may be a link the machine set up; only a home's are refused") } } func TestHomesAreAPersonsOrAnAgentsNotAServicesOrRoots(t *testing.T) { dir := t.TempDir() passwd := filepath.Join(dir, "passwd") if err := os.WriteFile(passwd, []byte("root:x:0:0::/root:/bin/bash\n"+ "postgres:x:968:968::/var/lib/postgres:/usr/bin/nologin\n"+ "nobody:x:65534:65534::/:/usr/bin/nologin\n"+ "operator:x:1000:1000::/home/operator:/bin/zsh\n"+ "agent:x:1001:1001::/home/agent:/bin/bash\n"+ "svc:x:990:990::/home/svc:/usr/bin/nologin\n"), 0o644); err != nil { t.Fatal(err) } was := passwdFile passwdFile = passwd t.Cleanup(func() { passwdFile = was }) got := strings.Join(homes(), ",") if got != "/home/agent,/home/operator,/home/svc" { t.Fatalf("homes %s", got) } if homeAbove("/home/agent/.claude") != "/home/agent" || homeAbove("/home/agent") != "" || homeAbove("/var/lib/postgres/data") != "" || homeAbove("/root/.ssh") != "" { t.Fatal("strictly below a person's or an agent's home, and nothing else") } } // A hard link below a home to a file the home's account does not own is never owned, chmodded or read by // name: fstat on the opened descriptor counts its links first (the re-review of 2026-10-08). func TestAHardLinkedFileBelowAHomeIsRefused(t *testing.T) { home, outside := aLinkedHome(t) shadow := filepath.Join(outside, "shadow") linked := filepath.Join(home, ".claude") if err := os.Link(shadow, linked); err != nil { t.Skipf("no hard link here: %v", err) } var hard *HardLinkUnderHomeError if err := chmodPath(linked, 0o777); !errors.As(err, &hard) { t.Fatalf("chmod of a hard-linked file below a home is refused, got %v", err) } if err := chownPath(linked, os.Getuid(), os.Getgid()); !errors.As(err, &hard) { t.Fatalf("chown of it is refused, got %v", err) } if _, err := readPath(linked); !errors.As(err, &hard) { t.Fatalf("reading it is refused, got %v", err) } if m := modeOfPath(t, shadow); m != 0o600 { t.Fatalf("the other file's mode changed: %o", m) } // As the directory resource sees it: a file where ~/.claude should be is left alone. if err := chmodDirPath(linked, 0o700); err == nil || !strings.Contains(err.Error(), "where a directory was expected") { t.Fatalf("a file where a directory was expected is refused, got %v", err) } } func TestADirectoryWhereAFileWasExpectedAndAFifoAreRefused(t *testing.T) { home, _ := aLinkedHome(t) dir := filepath.Join(home, "d") if err := os.Mkdir(dir, 0o755); err != nil { t.Fatal(err) } if _, err := readPath(dir); err == nil || !strings.Contains(err.Error(), "where a file was expected") { t.Fatalf("a directory where a file was expected is refused, got %v", err) } fifo := filepath.Join(home, "f") if err := syscall.Mkfifo(fifo, 0o600); err != nil { t.Skipf("no fifo here: %v", err) } if err := chmodPath(fifo, 0o644); err == nil || !strings.Contains(err.Error(), "neither a file nor a directory") { t.Fatalf("a fifo is refused, got %v", err) } if err := chmodDirPath(dir, 0o700); err != nil { t.Fatalf("a directory as a directory passes: %v", err) } }