Merge eureka-body/self-run-scrub-knob: MIND_SELF_SCRUB=off lets a root-holding Self run use sudo
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
e5cd9d94e1
2 changed files with 28 additions and 1 deletions
|
|
@ -90,6 +90,9 @@ pub const SEED_VARIABLE: &str = "MIND_SELF_CONFIG_SEED";
|
|||
const TOKEN_FD_VARIABLE: &str = "CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR";
|
||||
/// Tells `claude` to scrub credentials from the environment of what it starts.
|
||||
const ENV_SCRUB_VARIABLE: &str = "CLAUDE_CODE_SUBPROCESS_ENV_SCRUB";
|
||||
/// Optional, set by the unit: `off` turns that scrub off for a run whose user holds root, where it
|
||||
/// protects nothing and its sandbox's no_new_privs would only stop `sudo`. Anything else keeps it on.
|
||||
const SCRUB_VARIABLE: &str = "MIND_SELF_SCRUB";
|
||||
/// The name the start message tells her to admit as.
|
||||
pub const AGENT: &str = "mind-self-run";
|
||||
/// The namespace of the session id: the run id is the name in it.
|
||||
|
|
@ -761,6 +764,11 @@ fn child_environment(unit: &[(OsString, OsString)]) -> Vec<(&OsString, &OsString
|
|||
unit.iter().filter(|(name, _)| child_may_see(name)).map(|(name, value)| (name, value)).collect()
|
||||
}
|
||||
|
||||
/// Whether the unit turned the child's subprocess scrub off: only `MIND_SELF_SCRUB=off` does.
|
||||
fn scrub_is_off(unit: &[(OsString, OsString)]) -> bool {
|
||||
unit.iter().any(|(name, value)| name == SCRUB_VARIABLE && value == "off")
|
||||
}
|
||||
|
||||
/// Starts `program` with a duplicate of the token descriptor open in it, and
|
||||
/// closes that duplicate here the moment the child exists. The lease's own
|
||||
/// descriptor is close-on-exec, so this duplicate is the child's only way to the
|
||||
|
|
@ -773,7 +781,7 @@ fn spawn_program(program: &Path, args: &[String], token: &TokenFd, unit: &[(OsSt
|
|||
command.args(args).stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::inherit());
|
||||
command.env_clear().envs(child_environment(unit));
|
||||
command.env("HOME", &config.0).env(CONFIG_DIR_VARIABLE, &config.0);
|
||||
command.env(TOKEN_FD_VARIABLE, inheritable.number().to_string()).env(ENV_SCRUB_VARIABLE, "1");
|
||||
command.env(TOKEN_FD_VARIABLE, inheritable.number().to_string()).env(ENV_SCRUB_VARIABLE, if scrub_is_off(unit) { "0" } else { "1" });
|
||||
// Its own process group, so that a signal reaches what the child started too.
|
||||
#[cfg(unix)]
|
||||
std::os::unix::process::CommandExt::process_group(&mut command, 0);
|
||||
|
|
|
|||
|
|
@ -156,3 +156,22 @@ fn the_parent_closes_the_duplicate_it_lent_the_child() {
|
|||
child.stop(Stop::Kill);
|
||||
drain(&mut *child);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_child_scrubs_its_subprocesses_unless_the_unit_says_off() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let program = script(&dir, "echo \"scrub=$CLAUDE_CODE_SUBPROCESS_ENV_SCRUB\"");
|
||||
let scrub_with = |value: Option<&str>| {
|
||||
let mut unit = unit_environment();
|
||||
unit.extend(value.map(|value| (SCRUB_VARIABLE.into(), value.into())));
|
||||
let config = FreshConfig::create(dir.path(), None).unwrap();
|
||||
let mut child = spawn_program(&program, &[], &token_holding("tok-canary-7f3a"), &unit, config).unwrap();
|
||||
child.close_input();
|
||||
let (lines, _) = drain(&mut *child);
|
||||
field(&lines, "scrub")
|
||||
};
|
||||
assert_eq!(scrub_with(None), "1", "unset keeps the scrub on");
|
||||
assert_eq!(scrub_with(Some("on")), "1", "any other value keeps it on");
|
||||
assert_eq!(scrub_with(Some("OFF")), "1", "only the exact word turns it off");
|
||||
assert_eq!(scrub_with(Some("off")), "0", "a unit for a user that holds root turns it off");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue