mind-body: mind-control takes --root <dir>; the window probe runs in a tempdir

Soul s3: the window probe wrote a window-probe-<pid> instance under the real
/etc/gamecult/minds. A leading --root argv flag (argv, because sudo -E carries
the environment) replaces the default root; the probe points the real binary at
a tempdir, whose drop removes whatever a failed assert leaves. No test names
/etc/gamecult any more; the path-derivation test uses CONTROL_ROOT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-09 16:53:27 +02:00
commit a50b25c08e
3 changed files with 44 additions and 11 deletions

View file

@ -3,7 +3,7 @@
//! root-owned and the filesystem is the authority.
//!
//! ```text
//! mind-control --instance <slug> show
//! mind-control [--root <dir>] --instance <slug> show
//! mind-control --instance <slug> brake hold
//! mind-control --instance <slug> brake release
//! mind-control --instance <slug> dial set --heat H --base-cooldown-s S --base-run-usd U
@ -21,7 +21,7 @@ use rust_decimal::Decimal;
use crate::control::{BurnRate, ControlWriter, FileSource, control_path, load_state, read_effective};
const USAGE: &str =
"usage: mind-control --instance <slug> show | brake hold | brake release | dial set --heat H --base-cooldown-s S --base-run-usd U";
"usage: mind-control [--root <dir>] --instance <slug> show | brake hold | brake release | dial set --heat H --base-cooldown-s S --base-run-usd U";
/// Who made the change: `SUDO_USER`, else `USER`, else `unknown`. An empty
/// variable counts as unset.
@ -33,10 +33,15 @@ pub fn operator_name(sudo_user: Option<String>, user: Option<String>) -> String
.unwrap_or_else(|| "unknown".to_string())
}
/// Runs one command against `<root>/<instance>/control.cc`. The root, the
/// operator's name, the clock and the output are parameters so a test drives
/// Runs one command against `<root>/<instance>/control.cc`. `root` is the
/// default; a leading `--root <dir>` argument replaces it (argv, not environment,
/// because `sudo -E` carries the environment). The operator's name, the clock and the output are parameters so a test drives
/// the whole command without touching `/etc`.
pub fn run(args: &[String], root: &Path, set_by: &str, now: DateTime<Utc>, out: &mut impl Write) -> Result<()> {
let (root, args) = match args {
[flag, dir, rest @ ..] if flag == "--root" => (Path::new(dir), rest),
_ => (root, args),
};
let (instance, command) = match args {
[flag, instance, command @ ..] if flag == "--instance" => (Slug(instance.clone()), command),
_ => bail!("{USAGE}"),

View file

@ -110,3 +110,27 @@ fn the_binary_exits_nonzero_with_the_usage_when_refused() {
assert!(String::from_utf8_lossy(&output.stderr).contains("usage: mind-control"));
assert!(output.stdout.is_empty());
}
#[test]
fn a_leading_root_flag_replaces_the_default_root() {
let default = tempfile::tempdir().unwrap();
let chosen = tempfile::tempdir().unwrap();
let chosen_arg = chosen.path().to_str().unwrap();
let (result, _) = go(default.path(), &["--root", chosen_arg, "--instance", "eureka", "brake", "release"], "op", 0);
result.unwrap();
assert!(chosen.path().join("eureka").join("control.cc").exists(), "the write went to the chosen root");
assert_eq!(std::fs::read_dir(default.path()).unwrap().count(), 0, "the default root was not touched");
// Without the flag the default applies.
go(default.path(), &["--instance", "eureka", "brake", "release"], "op", 1).0.unwrap();
assert!(default.path().join("eureka").join("control.cc").exists());
}
#[test]
fn a_root_flag_without_a_directory_or_an_instance_is_refused_and_writes_nothing() {
let default = tempfile::tempdir().unwrap();
for args in [&["--root"][..], &["--root", "/nonexistent-root-for-test"][..]] {
assert!(go(default.path(), args, "op", 0).0.is_err(), "{args:?}");
}
assert_eq!(std::fs::read_dir(default.path()).unwrap().count(), 0);
assert!(!std::path::Path::new("/nonexistent-root-for-test").exists());
}

View file

@ -1,6 +1,7 @@
//! Behavioural tests for the brake, the dial and the one reader. Every test
//! works in a temporary directory through the same file door the units use;
//! nothing touches `/etc` except the window probe, which writes a uniquely named
//! nothing touches `/etc`, including the window probe, which points the real
//! binary at a tempdir with `--root`.
//! instance there as root and removes it.
//!
//! The permission boundary is the filesystem's, but the writer refuses to write
@ -290,7 +291,7 @@ fn reading_writes_nothing_and_takes_no_lock_file() {
#[test]
fn the_control_path_is_derived_from_a_valid_instance_only() {
use eureka_pipeline::Slug;
let root = Path::new("/etc/gamecult/minds");
let root = Path::new(mind_body::control::CONTROL_ROOT);
assert_eq!(control_path(root, &Slug("eureka".into())).unwrap(), root.join("eureka").join("control.cc"));
for bad in ["", "../etc", "a/b", "A b", "eureka/.."] {
assert!(control_path(root, &Slug(bad.into())).is_err(), "{bad:?}");
@ -545,13 +546,17 @@ mod unsafe_installs {
#[test]
fn no_non_root_open_for_write_succeeds_while_the_binary_writes_under_umask_000() {
if !running_as_root() || Command::new("setpriv").arg("--version").output().is_err() {
eprintln!("SKIPPED: needs root and setpriv; it writes under /etc/gamecult/minds in the verify container");
eprintln!("SKIPPED: needs root and setpriv");
return;
}
let instance = format!("window-probe-{}", std::process::id());
let live = Path::new(mind_body::control::CONTROL_ROOT).join(&instance);
// A root-owned 0755 tempdir is the control root; its drop removes whatever
// the probe left, even after a failed assert.
let work = tempfile::tempdir().unwrap();
chmod(work.path(), 0o755);
let minds = work.path().join("minds");
std::fs::create_dir(&minds).unwrap();
chmod(&minds, 0o755);
let attacker_bin = work.path().join("attacker");
std::fs::copy(std::env::current_exe().unwrap(), &attacker_bin).unwrap();
chmod(&attacker_bin, 0o755);
@ -561,7 +566,7 @@ mod unsafe_installs {
.arg(&attacker_bin)
.args(["--exact", "modes::mode_child", "--nocapture"])
.env("MIND_BODY_MODE_CHILD", "attacker")
.env("MIND_BODY_MODE_ROOT", live.parent().unwrap())
.env("MIND_BODY_MODE_ROOT", &minds)
.env("MIND_BODY_STOP", &stop)
.env("MIND_BODY_INSTANCE", &instance)
.spawn()
@ -570,14 +575,13 @@ mod unsafe_installs {
for round in 0..100 {
let verb = if round % 2 == 0 { "release" } else { "hold" };
let status = Command::new("sh")
.args(["-c", "umask 000; exec \"$@\"", "sh", bin, "--instance", &instance, "brake", verb])
.args(["-c", "umask 000; exec \"$@\"", "sh", bin, "--root", minds.to_str().unwrap(), "--instance", &instance, "brake", verb])
.status()
.unwrap();
assert!(status.success());
}
std::fs::write(&stop, b"").unwrap();
let attacker_status = attacker.wait().unwrap();
std::fs::remove_dir_all(&live).unwrap();
assert_eq!(attacker_status.code(), Some(0), "a non-root process opened the live store for write");
}
}