Files
mesh-host/internal/apply/root_moved.go
T

50 lines
1.8 KiB
Go

package apply
import (
"path/filepath"
"strings"
"git.novox.be/novox/mesh-sdk/go/declaration"
)
// rootPaths are the files and directories through which an account comes to root, or by which it is told who it
// is: the user and group databases, sudo's, doas's and polkit's rules. A file written there changes what the
// judge of an agent account reads (novox/hq ADR 0266).
var rootPaths = []string{"/etc/passwd", "/etc/shadow", "/etc/group", "/etc/gshadow", "/etc/sudoers",
"/etc/sudoers.d", "/etc/doas.conf", "/etc/doas.d", "/etc/polkit-1", "/usr/share/polkit-1"}
// MovesRoot says an apply changed something through which root's reach could have changed — an account, a
// package, an action run as root, or a file in rootPaths (novox/hq issue 361). The engine then asks for a new
// search for setuid programs: root acted, and a complete search is sound only until root acts. Outcomes that
// moved nothing (unchanged, held, waiting, held still, skipped, kept, forgotten) do not count.
func MovesRoot(outcomes []Outcome) bool {
for _, o := range outcomes {
// Only what moved the machine: skipped, kept and forgotten touch nothing, and counting them made one
// broken module start a search on every reconcile (the review of 2026-10-10).
switch o.Action {
case "created", "updated", "corrected", "removed":
default:
continue
}
switch declaration.Type(o.Type) {
case declaration.TypeUser, declaration.TypePackage, declaration.TypeAction:
return true
case declaration.TypeFile, declaration.TypeDirectory, declaration.TypeArchive:
if underRootPath(o.Target) {
return true
}
}
}
return false
}
func underRootPath(target string) bool {
t := filepath.Clean(target)
for _, p := range rootPaths {
if t == p || strings.HasPrefix(t, p+"/") {
return true
}
}
return false
}