package accounts import ( "context" "encoding/binary" "errors" "fmt" "os/exec" "strings" "testing" "time" ) // The ways beyond uid, groups and sudo (novox/hq ADR 0266, the review of 2026-10-08): doas, polkit, a // runtime's socket, an ACL on a secret, a setuid program no package owns — each said; each unread question // unknown; and the judge's commands in the C locale. func TestEachFurtherWayToRootIsSaid(t *testing.T) { const broker = "/var/lib/mesh/node-tools/broker" for _, c := range []struct { name string set func(*agentMachine) said string }{ {"doas by name", func(m *agentMachine) { m.texts = map[string]string{"/etc/doas.conf": "permit persist operator\npermit nopass agent as root\n"} }, "doas permits it: permit nopass agent as root"}, {"doas by group", func(m *agentMachine) { m.groups = "agent builders" m.texts = map[string]string{"/etc/opendoas.conf": "permit :builders # the builders\n"} }, "doas permits it: permit :builders"}, {"polkit by name", func(m *agentMachine) { m.dirs = map[string][]string{"/etc/polkit-1/rules.d": {"10-agent.rules"}} m.texts = map[string]string{"/etc/polkit-1/rules.d/10-agent.rules": `polkit.addRule(function(a, s) { if (s.user == "agent") return polkit.Result.YES; });`} }, "a polkit rule names it or a group of it: /etc/polkit-1/rules.d/10-agent.rules"}, {"polkit by group", func(m *agentMachine) { m.groups = "agent network" m.dirs = map[string][]string{"/usr/share/polkit-1/rules.d": {"50-nm.rules"}} m.texts = map[string]string{"/usr/share/polkit-1/rules.d/50-nm.rules": `if (subject.isInGroup("network")) return polkit.Result.YES;`} }, "a polkit rule names it or a group of it: /usr/share/polkit-1/rules.d/50-nm.rules"}, {"docker socket by group bits", func(m *agentMachine) { m.gids = "1600 970" m.files["/run/docker.sock"] = FileMode{UID: 0, GID: 970, Perm: 0o660} }, "it can write the container runtime's socket /run/docker.sock"}, {"docker socket by ACL", func(m *agentMachine) { m.files["/run/docker.sock"] = FileMode{UID: 0, GID: 970, Perm: 0o660} m.acls = map[string][]ACLEntry{"/run/docker.sock": {{User: true, ID: 1600, Read: true, Write: true}}} }, "it can write the container runtime's socket /run/docker.sock"}, {"secret by ACL", func(m *agentMachine) { m.acls = map[string][]ACLEntry{broker: {{User: false, ID: 1600, Read: true}}} }, "an ACL lets it read the secret " + broker}, {"setuid no package owns", func(m *agentMachine) { m.setuid = "/usr/bin/sudo\n/usr/local/bin/rootshell\n" m.packaged = map[string]bool{"/usr/bin/sudo": true} }, "a setuid-root program no package owns: /usr/local/bin/rootshell"}, } { t.Run(c.name, func(t *testing.T) { m := clean() c.set(m) v := lookAgent(t, m) if v.State != Unhealthy || !strings.HasPrefix(v.Reason, ReasonRoot+": ") || !strings.Contains(v.Reason, c.said) { t.Fatalf("want %q said: %+v", c.said, v) } }) } } func TestWhatGrantsSomebodyElseIsNoWay(t *testing.T) { m := clean() m.texts = map[string]string{"/etc/doas.conf": "permit operator\npermit :wheel\n# permit agent\n", "/usr/share/polkit-1/rules.d/50-default.rules": `polkit.addAdminRule(function(a, s) { return ["unix-group:wheel"]; });`} m.dirs = map[string][]string{"/usr/share/polkit-1/rules.d": {"50-default.rules"}} m.files["/run/docker.sock"] = FileMode{UID: 0, GID: 970, Perm: 0o660} m.setuid = "/usr/bin/sudo\n/usr/bin/passwd\n" m.packaged = map[string]bool{"/usr/bin/sudo": true, "/usr/bin/passwd": true} if v := lookAgent(t, m); v.State != Healthy { t.Fatalf("the operator's and wheel's grants, a socket of another group, packaged setuid programs: %+v", v) } } func TestDoasWithoutSudoIsJudgedFromItsRules(t *testing.T) { m := clean() m.sudo, m.sudoErr = "", fmt.Errorf("sudo: %w", exec.ErrNotFound) m.texts = map[string]string{"/etc/doas.conf": "permit nopass agent\n"} v := lookAgent(t, m) if v.State != Unhealthy || !strings.Contains(v.Reason, "doas permits it") { t.Fatalf("no sudo is no sudo rule, and doas is read for itself: %+v", v) } } func TestAFurtherQuestionUnansweredIsUnknown(t *testing.T) { m := clean() m.findErr = errors.New("find: interrupted") if v := lookAgent(t, m); v.State != Unknown { t.Fatalf("a search that did not finish: %+v", v) } m = clean() m.setuid = "/usr/local/bin/x\n" j := New(Exec{Run: func(ctx context.Context, name string, args ...string) (string, error) { if name == "pacman" || name == "apk" { return "", fmt.Errorf("%s: %w", name, exec.ErrNotFound) } return m.run(ctx, name, args...) }, Stat: m.stat, ReadFile: m.readFile, ReadDir: m.readDir, ACL: m.acl}) j.Set([]Account{agent}) if st, _ := j.Look(t.Context()); st.Accounts[0].State != Unknown { t.Fatalf("no package manager to ask: %+v", st.Accounts[0]) } } func TestTheSetuidSearchIsKeptForItsInterval(t *testing.T) { c := &SetuidCache{Every: time.Hour} searched := 0 search := func() ([]string, error) { searched++; return nil, nil } at := time.Date(2026, 10, 8, 19, 0, 0, 0, time.UTC) c.get(at, search) c.get(at.Add(30*time.Minute), search) c.get(at.Add(61*time.Minute), search) if searched != 2 { t.Fatalf("searched %d times in 61 minutes, want 2", searched) } } func TestParseACL(t *testing.T) { entry := func(tag, perm uint16, id uint32) []byte { b := make([]byte, 8) binary.LittleEndian.PutUint16(b, tag) binary.LittleEndian.PutUint16(b[2:], perm) binary.LittleEndian.PutUint32(b[4:], id) return b } raw := []byte{2, 0, 0, 0} raw = append(raw, entry(0x01, 6, 0xffffffff)...) raw = append(raw, entry(0x02, 6, 1600)...) // user agent rw raw = append(raw, entry(0x04, 4, 0xffffffff)...) raw = append(raw, entry(0x08, 6, 970)...) // group 970 rw raw = append(raw, entry(0x10, 4, 0xffffffff)...) // mask r-- raw = append(raw, entry(0x20, 0, 0xffffffff)...) acl, err := ParseACL(raw) if err != nil || len(acl) != 2 { t.Fatalf("%v %v", acl, err) } if !acl[0].User || acl[0].ID != 1600 || !acl[0].Read || acl[0].Write { t.Fatalf("the mask takes write away: %+v", acl[0]) } if grantsByACL(acl, 1600, nil, true) || !grantsByACL(acl, 1601, map[int]bool{970: true}, false) { t.Fatal("grants") } if _, err := ParseACL([]byte{2, 0, 0}); err == nil { t.Fatal("a short ACL") } } func TestTheJudgesCommandsRunInTheCLocale(t *testing.T) { t.Setenv("LC_ALL", "de_DE.UTF-8") t.Setenv("LANG", "de_DE.UTF-8") out, err := CLocale(t.Context(), "sh", "-c", `echo "$LC_ALL $LANG"`) if err != nil || strings.TrimSpace(out) != "C C" { t.Fatalf("%q %v", out, err) } if _, err := CLocale(t.Context(), "sh", "-c", "echo nope >&2; exit 3"); err == nil || !strings.Contains(err.Error(), "exited 3: nope") { t.Fatalf("an exit is said with its words: %v", err) } } func TestTheJudgedListIsWrittenDown(t *testing.T) { if len(Judged) < 8 || len(NotJudged) == 0 { t.Fatal("what is judged and what is not are both written down") } }