sudo: count any sudo rule as root, fail on what cannot be judged, and commit no binary

The review of 2026-10-09: an unknown account and sudo that could not be asked were answered not root, and
only NOPASSWD on ALL counted, though NOPASSWD on systemctl, tee, bash or pacman reaches root as surely,
and a password the person typed is kept by sudo for minutes. Any rule now counts, as the node-engine's
own judge of an agent account does (ADR 0266); what cannot be judged fails the answer. The bundle built
in the module's directory is removed and ignored.
This commit is contained in:
2026-10-09 14:03:41 +02:00
parent 7e93492d28
commit a3a4897cc0
5 changed files with 120 additions and 48 deletions
+47 -11
View File
@@ -5,24 +5,39 @@ import (
"testing"
)
// novox/hq ADR 0259 §8: who can become root here without a person, as the self-check asks it.
// novox/hq ADR 0259 §8, as the review of 2026-10-09 asked: any sudo rule is root without a person — NOPASSWD on
// a named command (systemctl, tee, bash, pacman) as much as on all of them, and a rule asking a password too —
// and what cannot be judged is an error, never a no.
func TestWhoCanBecomeRootWithoutAPerson(t *testing.T) {
var calls []call
listing := func(account, rule string) Ran {
return Ran{Stdout: "User " + account + " may run the following commands on anchor:\n " + rule + "\n"}
}
run := byLine(map[string]Ran{
"id -nG ops": {Stdout: "ops wheel\n"},
"sudo -n -l -U ops": {Stdout: "User ops may run the following commands on anchor:\n (ALL : ALL) NOPASSWD: ALL\n"},
"id -nG agents": {Stdout: "agents\n"},
"sudo -n -l -U agents": {Stdout: "User agents is not allowed to run sudo on anchor.\n"},
"id -nG builder": {Stdout: "builder docker\n"},
"id -nG careful": {Stdout: "careful wheel\n"},
"sudo -n -l -U careful": {Stdout: "User careful may run the following commands on anchor:\n (ALL : ALL) ALL\n"},
"id -nG ghost": {Status: 1, Stderr: "id: 'ghost': no such user"},
"id -nG ops": {Stdout: "ops wheel\n"},
"sudo -n -l -U ops": listing("ops", "(ALL : ALL) NOPASSWD: ALL"),
"id -nG agents": {Stdout: "agents\n"},
"sudo -n -l -U agents": {Stdout: "User agents is not allowed to run sudo on anchor.\n", Status: 1},
"id -nG builder": {Stdout: "builder docker\n"},
"id -nG careful": {Stdout: "careful wheel\n"},
"sudo -n -l -U careful": listing("careful", "(ALL : ALL) ALL"),
"id -nG units": {Stdout: "units\n"},
"sudo -n -l -U units": listing("units", "(root) NOPASSWD: /usr/bin/systemctl"),
"id -nG writer": {Stdout: "writer\n"},
"sudo -n -l -U writer": listing("writer", "(root) NOPASSWD: /usr/bin/tee /etc/hosts"),
"id -nG packager": {Stdout: "packager\n"},
"sudo -n -l -U packager": listing("packager", "(root) NOPASSWD: /usr/bin/pacman -Syu"),
"id -nG asother": {Stdout: "asother\n"},
"sudo -n -l -U asother": listing("asother", "(ops) NOPASSWD: /bin/bash"),
"id -nG containers": {Stdout: "containers lxd\n"},
}, &calls)
got, err := machine(run, 1000).Escalations([]string{"ops", "agents", "builder", "careful", "ghost", "root"})
got, err := machine(run, 1000).Escalations([]string{"ops", "agents", "builder", "careful", "units", "writer",
"packager", "asother", "containers", "root"})
if err != nil {
t.Fatal(err)
}
want := map[string]bool{"ops": true, "agents": false, "builder": true, "careful": false, "ghost": false, "root": true}
want := map[string]bool{"ops": true, "agents": false, "builder": true, "careful": true, "units": true, "writer": true,
"packager": true, "asother": true, "containers": true, "root": true}
for _, e := range got {
if e.Root != want[e.Account] || e.Why == "" {
t.Errorf("%s: root %v (%s), want %v", e.Account, e.Root, e.Why, want[e.Account])
@@ -32,3 +47,24 @@ func TestWhoCanBecomeRootWithoutAPerson(t *testing.T) {
t.Errorf("an account name that is not one was asked: %v", err)
}
}
// What cannot be judged is never a no: an unknown account, sudo that cannot be asked, a listing that cannot be
// read — each fails the whole answer.
func TestWhatCannotBeJudgedIsAnError(t *testing.T) {
var calls []call
run := byLine(map[string]Ran{
"id -nG ghost": {Status: 1, Stderr: "id: 'ghost': no such user"},
"id -nG agents": {Stdout: "agents\n"},
"sudo -n -l -U agents": {Status: 1, Stderr: "sudo: a password is required"},
"id -nG garbled": {Stdout: "garbled\n"},
"sudo -n -l -U garbled": {Stdout: "something else entirely\n"},
"id -nG fine": {Stdout: "fine\n"},
"sudo -n -l -U fine": {Stdout: "User fine is not allowed to run sudo on anchor.\n", Status: 1},
}, &calls)
for _, a := range []string{"ghost", "agents", "garbled"} {
got, err := machine(run, 1000).Escalations([]string{"fine", a})
if err == nil || !strings.Contains(err.Error(), a+" cannot be judged") {
t.Errorf("%s: answered %+v (%v), want an error", a, got, err)
}
}
}