Let the setuid search run to its 15-minute bound, ask pacman once, and call a walk with errors incomplete
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 fail: its merge-check.sh failed: --- FAIL: TestHomesAreAPersonsOrAnAgentsNotAServicesOrRoots (0.00s)
mesh/delivery superseded: a newer head of the same pull request
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 fail: its merge-check.sh failed: --- FAIL: TestHomesAreAPersonsOrAnAgentsNotAServicesOrRoots (0.00s)
mesh/delivery superseded: a newer head of the same pull request
The search kept a two-minute limit of its own inside its 15-minute bound, so a long walk always ended incomplete; pacman was asked once per path; and find exiting with an error but some output was read as a complete walk. The bound and the not-judged reasons move to the public rootsearch package so the controller reads the same values (the review of 2026-10-09).
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/novox/mesh-host/rootsearch"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -318,3 +319,72 @@ polkit.addRule(function(a, s) { /* isInGroup("wheel") */ return polkit.Result.YE
|
||||
t.Error("each .pkla section on its own")
|
||||
}
|
||||
}
|
||||
|
||||
// The search's bound is the one the controller reads (rootsearch.Bound): a search may run its whole fifteen
|
||||
// minutes, with no shorter limit inside it that would end every long walk incomplete (the review of 2026-10-09).
|
||||
func TestTheSearchRunsToItsBoundWithNoShorterLimitInside(t *testing.T) {
|
||||
if rootsearch.Bound != 15*time.Minute {
|
||||
t.Fatalf("the shared bound is %s", rootsearch.Bound)
|
||||
}
|
||||
m := clean()
|
||||
var left time.Duration
|
||||
run := func(ctx context.Context, name string, args ...string) (string, error) {
|
||||
if name == "find" {
|
||||
if d, ok := ctx.Deadline(); ok {
|
||||
left = time.Until(d)
|
||||
}
|
||||
}
|
||||
return m.run(ctx, name, args...)
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(t.Context(), SearchBound)
|
||||
defer cancel()
|
||||
if _, err := (Exec{Run: run}).setuidSearch(ctx, []string{"/"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if left < SearchBound-time.Minute {
|
||||
t.Fatalf("find was given %s, less than the search's bound of %s", left, SearchBound)
|
||||
}
|
||||
}
|
||||
|
||||
// The package manager is asked about every path found in one call, and answers each: owned, or owned by nothing.
|
||||
func TestThePackageManagerIsAskedOnceForEveryPathFound(t *testing.T) {
|
||||
m := clean()
|
||||
m.setuid = "/usr/bin/sudo\n/usr/bin/passwd\n/usr/local/bin/x\n/opt/y\n"
|
||||
m.packaged = map[string]bool{"/usr/bin/sudo": true, "/usr/bin/passwd": true}
|
||||
found, err := (Exec{Run: m.run}).setuidSearch(t.Context(), []string{"/"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if m.pacmanCalls != 1 {
|
||||
t.Errorf("pacman was asked %d times for four paths", m.pacmanCalls)
|
||||
}
|
||||
if strings.Join(found, ",") != "/opt/y,/usr/local/bin/x" {
|
||||
t.Errorf("unowned: %v", found)
|
||||
}
|
||||
// An error pacman says that is not "owned by nothing" is no answer.
|
||||
odd := func(ctx context.Context, name string, args ...string) (string, error) {
|
||||
if name == "pacman" {
|
||||
return "", errors.New("pacman exited 1: error: could not open database")
|
||||
}
|
||||
return m.run(ctx, name, args...)
|
||||
}
|
||||
if _, err := (Exec{Run: odd}).setuidSearch(t.Context(), []string{"/"}); err == nil {
|
||||
t.Error("a package manager that could not answer was read as an answer")
|
||||
}
|
||||
}
|
||||
|
||||
// find that exits with an error has not walked everything: the search is incomplete, unless every error was a
|
||||
// file that vanished while it walked.
|
||||
func TestAWalkFindDidNotCompleteIsIncomplete(t *testing.T) {
|
||||
m := clean()
|
||||
m.setuid = "/usr/local/bin/x\n"
|
||||
m.findErr = errors.New("find exited 1: find: '/mnt/data/private': Permission denied")
|
||||
if _, err := (Exec{Run: m.run}).setuidSearch(t.Context(), []string{"/"}); err == nil {
|
||||
t.Fatal("a walk that could not enter a directory was read as complete")
|
||||
}
|
||||
m.findErr = errors.New("find exited 1: find: '/tmp/a': No such file or directory\nfind: '/tmp/b': No such file or directory")
|
||||
found, err := (Exec{Run: m.run}).setuidSearch(t.Context(), []string{"/"})
|
||||
if err != nil || len(found) != 1 {
|
||||
t.Fatalf("files that vanished while find walked: %v %v", found, err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user