use super::super::NoMcp;
use super::*;
use crate::caveats::{Caveats, CountBound, Scope};
pub(crate) async fn env_lock() -> crate::process_env::EnvGuard {
crate::process_env::lock()
}
#[tokio::test]
async fn ocap_env_guard_shares_the_settings_and_denial_journal_lock() {
let _env = env_lock().await;
assert!(
crate::process_env::held_by_current_thread(),
"the ocap fixture must own the process environment lock"
);
let _settings = crate::test_guard::GlobalSettingsGuard::acquire();
assert!(
std::thread::spawn(|| crate::process_env::try_lock().is_none())
.join()
.unwrap()
);
}
pub(crate) struct EnvVar {
key: &'static str,
saved: Option<String>,
}
impl EnvVar {
pub(crate) fn set(key: &'static str, value: &str) -> Self {
let saved = std::env::var(key).ok();
std::env::set_var(key, value);
Self { key, saved }
}
pub(crate) fn unset(key: &'static str) -> Self {
let saved = std::env::var(key).ok();
std::env::remove_var(key);
Self { key, saved }
}
}
impl Drop for EnvVar {
fn drop(&mut self) {
match self.saved.take() {
Some(v) => std::env::set_var(self.key, v),
None => std::env::remove_var(self.key),
}
}
}
#[tokio::test]
async fn confined_shell_env_does_not_leak_a_parent_only_secret_1600() {
let _lock = env_lock().await;
let _pt = EnvVar::unset("NEWT_SHELL_ENV_PASSTHROUGH");
let _canary = EnvVar::set("NEWT_SAFESUBSET_ENV_CANARY_1600", "s3cr3t-parent-only-1600");
let env = venv_env_map();
assert!(
!env.contains_key("NEWT_SAFESUBSET_ENV_CANARY_1600"),
"the confined shell env leaked a parent-only secret NAME (#1600): {env:?}"
);
assert!(
!env.values().any(|v| v.contains("s3cr3t-parent-only-1600")),
"the confined shell env leaked a parent-only secret VALUE (#1600)"
);
assert_eq!(
env.get("SHELL").map(String::as_str),
Some(shell_engine().as_str())
);
}
#[tokio::test]
async fn confined_shell_env_preserves_parent_timezone() {
let _lock = env_lock().await;
let _pt = EnvVar::unset("NEWT_SHELL_ENV_PASSTHROUGH");
let _config = EnvVar::set("NEWT_CONFIG_DIR", "/nonexistent-newt-timezone-test");
for timezone in ["America/New_York", "UTC-14", ""] {
let _tz = EnvVar::set("TZ", timezone);
assert_eq!(venv_env_map().get("TZ").map(String::as_str), Some(timezone));
}
let _tz = EnvVar::unset("TZ");
assert!(!venv_env_map().contains_key("TZ"));
}
#[tokio::test]
async fn confined_shell_env_timezone_respects_operator_allowlist() {
let _lock = env_lock().await;
let _pt = EnvVar::set("NEWT_SHELL_ENV_PASSTHROUGH", "HOME:USER");
let _config = EnvVar::set("NEWT_CONFIG_DIR", "/nonexistent-newt-timezone-test");
let _tz = EnvVar::set("TZ", "UTC-14");
assert!(!venv_env_map().contains_key("TZ"));
}
#[cfg(any(target_os = "linux", target_os = "macos"))]
#[tokio::test]
#[ignore = "real confined child and grandchild; run serially in integration tier"]
async fn confined_shell_parent_timezone_reaches_child_and_grandchild() {
let _lock = env_lock().await;
let _pt = EnvVar::unset("NEWT_SHELL_ENV_PASSTHROUGH");
let _config = EnvVar::set("NEWT_CONFIG_DIR", "/nonexistent-newt-timezone-test");
let _engine = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
let _venv = EnvVar::unset("NEWT_VENV");
let _virtual = EnvVar::unset("VIRTUAL_ENV");
let _paths = EnvVar::unset("NEWT_EXEC_PATHS");
#[cfg(target_os = "macos")]
let date_args = ["-r", "1705320000", "+%Y-%m-%dT%H:%M:%S%z"];
#[cfg(not(target_os = "macos"))]
let date_args = ["--date=@1705320000", "+%Y-%m-%dT%H:%M:%S%z"];
let command = format!(
"/bin/sh -c '/bin/date {}; status=$?; exit \"$status\"'",
date_args.join(" ")
);
let workspace = tempfile::tempdir().expect("timezone fixture workspace");
let cwd = workspace.path().to_str().unwrap();
for timezone in ["UTC-14", ""] {
let _tz = EnvVar::set("TZ", timezone);
let expected = std::process::Command::new("/bin/date")
.args(date_args)
.env_clear()
.env("TZ", timezone)
.output()
.expect("parent date");
assert!(expected.status.success());
let caveats = Caveats {
fs_read: Scope::only([cwd.to_string()]),
fs_write: Scope::none(),
..Caveats::top()
};
let result = super::shell::dispatch_bridled_shell(
confined_dispatch_args(&command, cwd),
&caveats,
None,
)
.await
.expect("confined dispatch");
assert_eq!(result["exit_code"], 0, "confined date must execute");
assert_eq!(
result["stdout"].as_str(),
Some(std::str::from_utf8(&expected.stdout).unwrap()),
"parent and grandchild must agree for TZ={timezone:?}"
);
}
}
fn caveats_no_exec(ws: &std::path::Path) -> Caveats {
Caveats {
fs_read: Scope::only([ws.to_string_lossy().into_owned()]),
fs_write: Scope::only([ws.to_string_lossy().into_owned()]),
exec: Scope::none(),
net: Scope::none(),
max_calls: CountBound::Unlimited,
valid_for_generation: Scope::All,
}
}
async fn run_tool(
name: &str,
args: serde_json::Value,
ws: &std::path::Path,
caveats: &Caveats,
) -> String {
run_tool_with_floor(name, args, ws, caveats, None).await
}
async fn run_tool_with_floor(
name: &str,
args: serde_json::Value,
ws: &std::path::Path,
caveats: &Caveats,
exec_floor: Option<&Scope<String>>,
) -> String {
execute_tool(
name,
&args,
&ws.to_string_lossy(),
false,
20,
caveats,
&mut NoMcp,
None,
None,
None,
None, None,
exec_floor,
None, None, None, None, None, None, None, )
.await
}
#[test]
fn ocap_disabled_requires_exactly_1() {
let _l = crate::process_env::lock();
{
let _unset = EnvVar::unset("NEWT_DISABLE_OCAP");
assert!(!ocap_disabled(), "absent ⇒ confinement stays on");
}
for (value, expected) in [
("1", true),
("0", false),
("", false),
("true", false),
("yes", false),
("YOLO", false),
] {
let _set = EnvVar::set("NEWT_DISABLE_OCAP", value);
assert_eq!(
ocap_disabled(),
expected,
"NEWT_DISABLE_OCAP={value:?} must read as {expected}"
);
}
}
#[test]
fn full_access_requested_requires_exactly_1() {
let _l = crate::process_env::lock();
{
let _unset = EnvVar::unset("NEWT_FULL_ACCESS");
assert!(!full_access_requested(), "absent ⇒ configured preset rules");
}
for (value, expected) in [
("1", true),
("0", false),
("", false),
("true", false),
("yes", false),
("FULL", false),
] {
let _set = EnvVar::set("NEWT_FULL_ACCESS", value);
assert_eq!(
full_access_requested(),
expected,
"NEWT_FULL_ACCESS={value:?} must read as {expected}"
);
}
}
#[test]
fn shadow_records_iff_the_run_is_unconfined() {
assert!(shadow_records(true, false), "yolo host bypass records");
assert!(
shadow_records(false, true),
"--full-access confined dispatch records (the #1176 parity fix)"
);
assert!(shadow_records(true, true), "both routes still record");
assert!(
!shadow_records(false, false),
"a genuinely confined session has a real leash — nothing to shadow"
);
}
#[tokio::test]
async fn flag_off_run_command_keeps_the_confined_dispatch_verbatim() {
let _l = env_lock().await;
let _off = EnvVar::unset("NEWT_DISABLE_OCAP");
let _eng = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let out = run_tool(
"run_command",
serde_json::json!({"command": "echo hi"}),
ws.path(),
&caveats,
)
.await;
assert!(
out.contains("capability denied"),
"flag off ⇒ the confined dispatch must govern (deny) the command, got: {out}"
);
}
#[cfg(unix)]
#[tokio::test]
async fn yolo_runs_the_denied_command_on_the_host_shell() {
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let out = run_tool(
"run_command",
serde_json::json!({"command": "echo yolo-ok"}),
ws.path(),
&caveats,
)
.await;
assert_eq!(out, "yolo-ok\n");
let out = run_tool(
"run_command",
serde_json::json!({"command": "exit 3"}),
ws.path(),
&caveats,
)
.await;
assert_eq!(out, "error: command exited 3");
}
#[cfg(unix)]
#[tokio::test]
async fn run_command_output_over_budget_is_token_capped() {
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let out = run_tool(
"run_command",
serde_json::json!({"command": "seq 1 60000"}),
ws.path(),
&caveats,
)
.await;
assert!(
out.len() < 41_500,
"model-facing output capped near the ~40k-char budget, got {} bytes",
out.len()
);
assert!(
out.contains("chars elided (head+tail shown"),
"carries the head+tail elision marker: {:?}",
&out[..out.len().min(400)]
);
assert!(
out.starts_with("1\n2\n3\n"),
"head preserved: {:?}",
&out[..out.len().min(160)]
);
assert!(
out.trim_end().ends_with("60000"),
"tail preserved, not dropped by the cap: {:?}",
&out[out.len().saturating_sub(160)..]
);
}
#[test]
fn exec_floor_permits_covers_each_branch() {
use crate::caveats::Scope;
assert!(exec_floor_permits(None, "rm -rf /"));
let only_echo = Scope::only(["echo".to_string()]);
assert!(exec_floor_permits(Some(&only_echo), ""));
assert!(exec_floor_permits(Some(&only_echo), "echo hi"));
assert!(!exec_floor_permits(Some(&only_echo), "rm hi"));
assert!(!exec_floor_permits(Some(&only_echo), "echo hi && rm x"));
assert!(!exec_floor_permits(Some(&only_echo), "echo a | tee b"));
assert!(!exec_floor_permits(Some(&only_echo), "echo $(rm x)"));
let all: Scope<String> = Scope::All;
assert!(exec_floor_permits(Some(&all), "anything goes"));
assert!(!exec_floor_permits(Some(&all), "anything; sneaky"));
}
#[test]
fn exec_floor_refuses_every_metacharacter_form() {
use crate::caveats::Scope;
let echo = Scope::only(["echo".to_string()]);
let attacks = [
"echo ok && rm -rf /tmp/x", "echo ok || rm -rf /tmp/x", "echo ok ; rm -rf /tmp/x", "echo ok | sh", "echo ok|sh", "echo $(rm x)", "echo ${IFS}rm", "echo `rm x`", "echo ok & rm x", "echo ok > /etc/passwd", "echo ok >> /etc/passwd", "echo < /etc/shadow", "echo ok 2> err", "(rm x)", "echo ok\nrm -rf /tmp/x", "echo ok\nrm x\n", ];
for a in attacks {
assert!(
!exec_floor_permits(Some(&echo), a),
"metacharacter form must NOT bypass the floor: {a:?}"
);
}
let leading_token_attacks = [
"rm -rf /tmp/x", "FOO=bar rm x", "/bin/echo ok", " rm x", "env rm x", "bash -c rm", ];
for a in leading_token_attacks {
assert!(
!exec_floor_permits(Some(&echo), a),
"out-of-floor leading token must be refused: {a:?}"
);
}
assert!(exec_floor_permits(Some(&echo), "echo hello world"));
assert!(exec_floor_permits(Some(&echo), "echo -n trailing"));
}
#[tokio::test]
async fn floor_blocks_disable_ocap_for_a_denied_exec() {
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let _eng = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let floor = crate::NamedPermissionPreset {
readonly: true,
..Default::default()
}
.clamp();
let out = run_tool_with_floor(
"run_command",
serde_json::json!({"command": "echo should-not-run"}),
ws.path(),
&caveats,
Some(&floor.exec),
)
.await;
assert_ne!(out, "should-not-run\n", "the floor must block the bypass");
assert!(
out.contains("capability denied"),
"fell to confined dispatch and was denied, got: {out}"
);
}
#[cfg(unix)]
#[tokio::test]
async fn floor_allows_disable_ocap_for_an_in_floor_exec() {
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let floor = crate::NamedPermissionPreset {
readonly: true,
exec_allow: vec!["echo".to_string()],
..Default::default()
}
.clamp();
let out = run_tool_with_floor(
"run_command",
serde_json::json!({"command": "echo in-floor-ok"}),
ws.path(),
&caveats,
Some(&floor.exec),
)
.await;
assert_eq!(out, "in-floor-ok\n", "in-floor command runs unconfined");
}
#[tokio::test]
async fn floor_refuses_bypass_for_a_compound_command() {
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let _eng = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let floor = crate::NamedPermissionPreset {
readonly: true,
exec_allow: vec!["echo".to_string()],
..Default::default()
}
.clamp();
let out = run_tool_with_floor(
"run_command",
serde_json::json!({"command": "echo ok && rm -rf /tmp/x"}),
ws.path(),
&caveats,
Some(&floor.exec),
)
.await;
assert_ne!(out, "ok\n", "a compound command must not bypass the floor");
assert!(
out.contains("capability denied"),
"fell to confined dispatch and was denied, got: {out}"
);
}
#[cfg(unix)]
#[tokio::test]
async fn no_floor_keeps_disable_ocap_bit_for_bit() {
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let out = run_tool_with_floor(
"run_command",
serde_json::json!({"command": "echo no-floor-ok"}),
ws.path(),
&caveats,
None,
)
.await;
assert_eq!(out, "no-floor-ok\n", "no floor ⇒ bypass unchanged");
}
#[cfg(unix)]
#[tokio::test]
async fn host_shell_127_wording_grounds_the_named_missing_rule() {
use crate::ExecOutcome;
let _l = env_lock().await;
let ws = tempfile::TempDir::new().unwrap();
for (cmd, expected) in [
(
"newt-absent-binary-xyzzy-2315 --version",
ExecOutcome::Unavailable,
),
("exit 127", ExecOutcome::Failed),
] {
let envelope = super::shell::host_shell_dispatch(
super::shell::ShellRoute::BashSh,
cmd,
&ws.path().to_string_lossy(),
None,
None,
Default::default(),
)
.await
.expect("host shell runs");
assert_eq!(envelope["exit_code"], 127, "{envelope}");
let (_, class) = super::shell::host_result(&envelope, |_| String::new());
assert_eq!(class, expected, "{cmd}: {envelope}");
}
}
#[cfg(unix)]
#[tokio::test]
async fn host_shell_envelope_matches_the_bridle_shape() {
let _l = env_lock().await;
let ws = tempfile::TempDir::new().unwrap();
let envelope = super::shell::host_shell_dispatch(
super::shell::ShellRoute::BashSh,
"echo out; echo err >&2; exit 3",
&ws.path().to_string_lossy(),
None,
None,
Default::default(),
)
.await
.expect("host shell runs");
assert_eq!(envelope["exit_code"], 3);
assert_eq!(envelope["stdout"], "out\n");
assert_eq!(envelope["stderr"], "err\n");
assert_eq!(envelope["sandbox_kind"], "none");
assert!(envelope.get("denied").is_none(), "got: {envelope}");
assert!(envelope.get("denials").is_none(), "got: {envelope}");
assert!(!envelope_denied(&envelope));
assert_eq!(
shell_envelope_output(&envelope, 20, false, false, None, None),
"error: command exited 3\nout\nerr\n"
);
}
#[test]
fn decode_shell_stream_preserves_valid_utf8() {
let text = "// ── Model — test ──\n";
assert_eq!(decode_shell_stream(text.as_bytes()), text);
}
#[test]
fn decode_shell_stream_repairs_bsd_cat_v_utf8_notation() {
let cat_v = b"\xe2M-^TM-^@ \xe2M-^@M-^T\n";
assert_eq!(decode_shell_stream(cat_v), "─ —\n");
}
#[test]
fn decode_shell_stream_repairs_two_byte_bsd_cat_v_notation() {
let cat_v = b"caf\xc3M-)\n";
assert_eq!(decode_shell_stream(cat_v), "café\n");
}
#[cfg(unix)]
#[tokio::test]
async fn yolo_keeps_the_venv_prefix_logic() {
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let _venv = EnvVar::set("NEWT_VENV", "/opt/fake-venv");
let _virtual = EnvVar::unset("VIRTUAL_ENV");
let _paths = EnvVar::unset("NEWT_EXEC_PATHS");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let out = run_tool(
"run_command",
serde_json::json!({"command": "echo \"$VIRTUAL_ENV\""}),
ws.path(),
&caveats,
)
.await;
assert_eq!(out, "/opt/fake-venv\n");
}
#[tokio::test]
async fn yolo_auto_confirms_unrestricted_write_and_delete_prompts() {
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let ws = tempfile::TempDir::new().unwrap();
let caveats = Caveats::top();
let out = run_tool(
"write_file",
serde_json::json!({"path": "auto.txt", "content": "ok\n"}),
ws.path(),
&caveats,
)
.await;
assert!(out.starts_with("wrote auto.txt"), "got: {out}");
assert_eq!(
std::fs::read_to_string(ws.path().join("auto.txt")).unwrap(),
"ok\n"
);
let out = run_tool(
"delete_file",
serde_json::json!({"path": "auto.txt"}),
ws.path(),
&caveats,
)
.await;
assert!(out.starts_with("deleted auto.txt"), "got: {out}");
assert!(
!ws.path().join("auto.txt").exists(),
"yolo-confirmed delete must remove the file"
);
}
#[test]
fn mutation_confirm_renders_its_frozen_form() {
let definition = mutation_confirm_definition("delete `auto.txt`?");
assert_eq!(
crate::markup::plain::render(&definition),
"delete `auto.txt`?\n[y] to confirm\n[n] to skip"
);
let rendered = crate::markup::plain::render(&definition);
assert!(!rendered.contains('Y'), "alias rendered: {rendered}");
assert!(!rendered.contains('N'), "alias rendered: {rendered}");
}
#[tokio::test]
async fn unrestricted_mutation_confirm_accepts_y_case_insensitively_and_fails_closed() {
let _l = env_lock().await;
let _off = EnvVar::unset("NEWT_DISABLE_OCAP");
let caveats = Caveats::top();
struct ScriptGate(HumanQuestionOutcome);
impl super::PermissionGate for ScriptGate {
fn ask(&mut self, _r: &[super::PermissionRequest]) -> super::PermissionDecision {
super::PermissionDecision::Deny
}
fn ask_question(&mut self, _q: &str) -> HumanQuestionOutcome {
self.0.clone()
}
}
fn confirm(caveats: &Caveats, outcome: Option<HumanQuestionOutcome>) -> bool {
match outcome {
Some(o) => {
let mut gate = ScriptGate(o);
let mut g: Option<&mut dyn super::PermissionGate> = Some(&mut gate);
confirm_unrestricted_fs_mutation(caveats, &mut g, "overwrite ~/x?")
}
None => {
let mut g: Option<&mut dyn super::PermissionGate> = None;
confirm_unrestricted_fs_mutation(caveats, &mut g, "overwrite ~/x?")
}
}
}
assert!(confirm(
&caveats,
Some(HumanQuestionOutcome::Answer("y".into()))
));
assert!(confirm(
&caveats,
Some(HumanQuestionOutcome::Answer("Y".into()))
));
for a in ["n", "N", "maybe", ""] {
assert!(
!confirm(&caveats, Some(HumanQuestionOutcome::Answer(a.into()))),
"answer {a:?} must not confirm the mutation"
);
}
for o in [
HumanQuestionOutcome::Unavailable,
HumanQuestionOutcome::Cancelled,
HumanQuestionOutcome::ExitRequested,
HumanQuestionOutcome::InputClosed,
HumanQuestionOutcome::InputFailed,
] {
assert!(
!confirm(&caveats, Some(o.clone())),
"outcome {o:?} must fail closed"
);
}
assert!(!confirm(&caveats, None));
}
#[tokio::test]
async fn unrestricted_write_and_delete_confirm_through_permission_gate() {
struct ConfirmGate {
answer: Option<String>,
questions: Vec<String>,
}
impl super::PermissionGate for ConfirmGate {
fn ask(&mut self, _requests: &[super::PermissionRequest]) -> super::PermissionDecision {
super::PermissionDecision::Deny
}
fn ask_question(&mut self, question: &str) -> HumanQuestionOutcome {
self.questions.push(question.to_string());
self.answer.clone().map_or(
HumanQuestionOutcome::Unavailable,
HumanQuestionOutcome::Answer,
)
}
}
let _l = env_lock().await;
let _off = EnvVar::unset("NEWT_DISABLE_OCAP");
let ws = tempfile::TempDir::new().unwrap();
let caveats = Caveats::top();
let mut gate = ConfirmGate {
answer: Some("y".to_string()),
questions: Vec::new(),
};
let out = execute_tool(
"write_file",
&serde_json::json!({"path": "guarded.txt", "content": "ok\n"}),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None, Some(&mut gate),
None,
None, None, None, None, None, None, None, )
.await;
assert!(out.starts_with("wrote guarded.txt"), "got: {out}");
assert_eq!(
std::fs::read_to_string(ws.path().join("guarded.txt")).unwrap(),
"ok\n"
);
let out = execute_tool(
"delete_file",
&serde_json::json!({"path": "guarded.txt"}),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None, Some(&mut gate),
None,
None, None, None, None, None, None, None, )
.await;
assert!(out.starts_with("deleted guarded.txt"), "got: {out}");
assert!(!ws.path().join("guarded.txt").exists());
assert_eq!(
gate.questions,
vec![
"Write this file? [y/N]\n[y] to confirm\n[n] to skip".to_string(),
"Delete this file? [y/N]\n[y] to confirm\n[n] to skip".to_string(),
]
);
}
#[tokio::test]
async fn confined_dispatch_uses_env_seam_not_export_prefix_783() {
let _l = env_lock().await;
let _venv = EnvVar::set("NEWT_VENV", "/opt/fake-venv");
let _virtual = EnvVar::unset("VIRTUAL_ENV");
let _paths = EnvVar::unset("NEWT_EXEC_PATHS");
let cmd = "hostname; sw_vers 2>/dev/null | head -1; uname -s";
let args = confined_dispatch_args(cmd, "/work/dir");
assert_eq!(args["cmd"], cmd);
assert!(
!args["cmd"]
.as_str()
.expect("cmd is a string")
.contains("export "),
"confined cmd must not carry an export prefix: {args}"
);
assert_eq!(args["cwd"], "/work/dir");
assert_eq!(args["env"]["VIRTUAL_ENV"], "/opt/fake-venv");
let path = args["env"]["PATH"].as_str().expect("PATH in the env seam");
assert!(
path.starts_with("/opt/fake-venv/bin"),
"venv bin must be prepended to PATH: {path}"
);
}
#[tokio::test]
async fn confined_dispatch_env_seam_without_venv_783() {
let _l = env_lock().await;
let _venv = EnvVar::unset("NEWT_VENV");
let _virtual = EnvVar::unset("VIRTUAL_ENV");
let _paths = EnvVar::unset("NEWT_EXEC_PATHS");
let _pass = EnvVar::unset("NEWT_SHELL_ENV_PASSTHROUGH"); let _home = EnvVar::set("HOME", "/home/testuser");
let args = confined_dispatch_args("ls -la", "/work/dir");
assert_eq!(args["cmd"], "ls -la");
let env = &args["env"];
assert!(
env.get("VIRTUAL_ENV").is_none(),
"no venv ⇒ no VIRTUAL_ENV: {args}"
);
#[cfg(not(target_os = "macos"))]
assert!(
env.get("PATH").is_none(),
"no venv/exec-paths ⇒ no PATH override: {args}"
);
#[cfg(target_os = "macos")]
if let Some(developer) = crate::confined_exec::selected_developer_directory() {
assert!(env["PATH"]
.as_str()
.unwrap()
.starts_with(&format!("{developer}/usr/bin")));
} else {
assert!(env.get("PATH").is_none());
}
assert_eq!(
env["HOME"], "/home/testuser",
"HOME must pass through: {args}"
);
assert!(
env.get("SHELL").is_some(),
"SHELL must identify the confined engine: {args}"
);
}
#[tokio::test]
async fn yolo_keeps_the_fs_workspace_fence() {
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let escape = "/definitely-outside-the-fence/escape.txt";
#[allow(clippy::join_absolute_paths)] let escape_full = ws.path().join(escape).to_string_lossy().into_owned();
let out = run_tool(
"write_file",
serde_json::json!({"path": escape, "content": "nope"}),
ws.path(),
&caveats,
)
.await;
assert_eq!(out, denied_fs_result("fs_write", &escape_full));
assert!(!std::path::Path::new(&escape_full).exists());
let out = run_tool(
"delete_file",
serde_json::json!({"path": escape}),
ws.path(),
&caveats,
)
.await;
assert_eq!(out, denied_fs_result("fs_write", &escape_full));
let outside_read = "/etc/hostname";
#[allow(clippy::join_absolute_paths)] let outside_read_full = ws.path().join(outside_read).to_string_lossy().into_owned();
let out = run_tool(
"read_file",
serde_json::json!({"path": outside_read}),
ws.path(),
&caveats,
)
.await;
assert_eq!(out, denied_fs_result("fs_read", &outside_read_full));
}
#[cfg(unix)]
#[tokio::test]
async fn yolo_never_consults_the_permission_gate_for_exec() {
struct PanicGate;
impl super::PermissionGate for PanicGate {
fn ask(&mut self, requests: &[super::PermissionRequest]) -> super::PermissionDecision {
panic!("yolo exec must never prompt, but the gate was asked: {requests:?}");
}
fn ask_question(&mut self, question: &str) -> HumanQuestionOutcome {
panic!("yolo exec must never prompt, but the gate was asked: {question:?}");
}
}
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let mut gate = PanicGate;
let out = execute_tool(
"run_command",
&serde_json::json!({"command": "echo no-prompt"}),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None, Some(&mut gate),
None,
None, None, None, None, None, None, None, )
.await;
assert_eq!(out, "no-prompt\n");
}
#[tokio::test]
async fn yolo_keeps_the_tool_name_corrective_guard() {
let _l = env_lock().await;
let _on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let out = run_tool(
"run_command",
serde_json::json!({"command": "read_file foo.txt"}),
ws.path(),
&caveats,
)
.await;
assert!(out.contains("is a tool, not a shell command"), "got: {out}");
}
struct UnexpectedEmbeddedGit;
impl crate::agentic::GitTool for UnexpectedEmbeddedGit {
fn dispatch(
&self,
op: &str,
_args: &serde_json::Value,
_caps: &crate::git_caveats::GitCaveats,
_session: &Caveats,
) -> Result<String, String> {
panic!("native command reached the embedded Git operation {op}");
}
}
async fn run_command_with_legacy_git(
command: &str,
ws: &std::path::Path,
caveats: &Caveats,
) -> String {
execute_tool(
"run_command",
&serde_json::json!({ "command": command }),
&ws.to_string_lossy(),
false,
20,
caveats,
&mut NoMcp,
None,
None,
None,
None, None, None, Some(&UnexpectedEmbeddedGit as &dyn crate::agentic::GitTool),
None, None, None, None, None, None, )
.await
}
#[cfg(target_os = "windows")]
async fn run_command_with_legacy_git_and_gate(
command: &str,
ws: &std::path::Path,
caveats: &Caveats,
permission_gate: &mut dyn super::PermissionGate,
) -> String {
execute_tool(
"run_command",
&serde_json::json!({ "command": command }),
&ws.to_string_lossy(),
false,
20,
caveats,
&mut NoMcp,
None,
None,
None,
None, Some(permission_gate),
None, Some(&UnexpectedEmbeddedGit as &dyn crate::agentic::GitTool),
None, None, None, None, None, None, )
.await
}
#[test]
fn routing_disabled_requires_exactly_1_and_is_independent_of_ocap() {
let _l = crate::process_env::lock();
let _no_ocap = EnvVar::unset("NEWT_DISABLE_OCAP");
{
let _unset = EnvVar::unset("NEWT_NO_ROUTE");
assert!(!routing_disabled(), "absent ⇒ routing stays on");
}
for (value, expected) in [("1", true), ("0", false), ("", false), ("true", false)] {
let _set = EnvVar::set("NEWT_NO_ROUTE", value);
assert_eq!(routing_disabled(), expected, "NEWT_NO_ROUTE={value:?}");
assert!(
!ocap_disabled(),
"NEWT_NO_ROUTE must not imply --disable-ocap"
);
}
let _unset_route = EnvVar::unset("NEWT_NO_ROUTE");
let _on_ocap = EnvVar::set("NEWT_DISABLE_OCAP", "1");
assert!(ocap_disabled());
assert!(
!routing_disabled(),
"--disable-ocap must not imply --no-route"
);
}
#[tokio::test]
async fn routed_cargo_test_reaches_the_confined_build_lane() {
struct RecordingDenyGate {
seen: Option<super::PermissionRequest>,
}
impl super::PermissionGate for RecordingDenyGate {
fn ask(&mut self, requests: &[super::PermissionRequest]) -> super::PermissionDecision {
self.seen = requests.first().cloned();
super::PermissionDecision::Deny
}
fn ask_question(&mut self, _question: &str) -> HumanQuestionOutcome {
HumanQuestionOutcome::Cancelled
}
}
let _l = env_lock().await;
let _route_on = EnvVar::unset("NEWT_NO_ROUTE");
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let mut gate = RecordingDenyGate { seen: None };
let out = execute_tool(
"run_command",
&serde_json::json!({ "command": "cargo test -p newt-core" }),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None, Some(&mut gate),
None,
None, None, None, None, None, None, None, )
.await;
assert!(
out.contains("routed `cargo test -p newt-core` to the confined build lane")
&& out.contains("30 min limit")
&& out.contains("network denied"),
"rendered result must say it ran in the build lane with its limit; got: {out}"
);
assert!(
out.contains("capability denied") && out.contains("build authority"),
"denied gate must refuse via the same build-authority path lifecycle action=build \
uses, not silently run; got: {out}"
);
let reason = gate
.seen
.expect("the build-lane gate/leq path must be consulted")
.reason;
assert!(
reason.contains("cargo test -p newt-core"),
"the permission reason must show the model's LITERAL argv, not a resolved \
phase command; got: {reason}"
);
}
#[tokio::test]
async fn routed_cat_goes_through_the_fs_floor_not_a_bypass() {
let _l = env_lock().await;
let _route_on = EnvVar::unset("NEWT_NO_ROUTE");
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let _eng = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path()); let out = run_tool(
"run_command",
serde_json::json!({ "command": "cat /etc/shadow" }),
ws.path(),
&caveats,
)
.await;
assert!(
out.contains("capability denied: fs_read does not permit") && out.contains("/etc/shadow"),
"routed cat must hit the fs floor, not run unconfined; got: {out}"
);
}
#[tokio::test]
async fn native_git_status_requires_exec_and_reads_the_actual_repository() {
let _l = env_lock().await;
let _route_on = EnvVar::unset("NEWT_NO_ROUTE");
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let _eng = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
let ws = tempfile::TempDir::new().unwrap();
let init = {
let mut cmd = crate::git_hardening::hardened_git(ws.path(), &["init", "-q"]).unwrap();
#[cfg(windows)]
if let Ok(canonical) = ws.path().canonicalize() {
if let Some(dir) = canonical.to_str() {
cmd.env("GIT_CONFIG_COUNT", "1")
.env("GIT_CONFIG_KEY_0", "safe.directory")
.env("GIT_CONFIG_VALUE_0", dir);
}
}
cmd.output().unwrap()
};
assert!(init.status.success(), "{init:?}");
std::fs::write(ws.path().join("native-only.txt"), "native evidence\n").unwrap();
let mut caveats = caveats_no_exec(ws.path());
caveats.fs_write = Scope::none();
caveats.net = Scope::All;
let denied = run_command_with_legacy_git("git status", ws.path(), &caveats).await;
assert!(denied.contains("capability denied"), "{denied}");
assert!(
!denied.contains(super::native_git::WINDOWS_APPCONTAINER_GIT_UNAVAILABLE),
"the ordinary exec denial must win before the Windows compatibility guard: {denied}"
);
caveats.exec = Scope::All;
let out = run_command_with_legacy_git("git status", ws.path(), &caveats).await;
#[cfg(target_os = "windows")]
{
assert!(
out.contains(super::native_git::WINDOWS_APPCONTAINER_GIT_UNAVAILABLE),
"restricted AppContainer must refuse before native Git runs: {out}"
);
assert!(!out.contains("routed:"), "{out}");
assert!(!ws.path().join(".git/index").exists());
}
#[cfg(not(target_os = "windows"))]
assert!(
out.contains("native-only.txt"),
"native Git must report the actual untracked file: {out}"
);
#[cfg(not(target_os = "windows"))]
{
assert!(!out.contains("routed:"), "{out}");
assert!(!ws.path().join(".git/index").exists());
}
}
#[cfg(target_os = "windows")]
#[tokio::test]
async fn windows_disable_ocap_keeps_native_git_available() {
let _l = env_lock().await;
let _route_on = EnvVar::unset("NEWT_NO_ROUTE");
let _ocap_on = EnvVar::set("NEWT_DISABLE_OCAP", "1");
let _eng = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
let ws = tempfile::TempDir::new().unwrap();
let init = crate::git_hardening::hardened_git(ws.path(), &["init", "-q"])
.unwrap()
.output()
.unwrap();
assert!(init.status.success(), "{init:?}");
std::fs::write(ws.path().join("native-only.txt"), "host route evidence\n").unwrap();
let out =
run_command_with_legacy_git("git status", ws.path(), &caveats_no_exec(ws.path())).await;
assert!(out.contains("native-only.txt"), "{out}");
assert!(
!out.contains(super::native_git::WINDOWS_APPCONTAINER_GIT_UNAVAILABLE),
"the explicit host route must not take the AppContainer refusal: {out}"
);
assert!(!out.contains("routed:"), "{out}");
}
#[tokio::test]
async fn routed_rm_dispatches_through_delete_file() {
let _l = env_lock().await;
let _route_on = EnvVar::unset("NEWT_NO_ROUTE");
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let ws = tempfile::TempDir::new().unwrap();
std::fs::write(ws.path().join("stale.txt"), "remove me\n").unwrap();
let caveats = caveats_no_exec(ws.path());
let out = run_tool(
"run_command",
serde_json::json!({ "command": "rm stale.txt" }),
ws.path(),
&caveats,
)
.await;
assert!(out.starts_with("deleted stale.txt"), "got: {out}");
assert!(
!ws.path().join("stale.txt").exists(),
"routed rm must remove the file through delete_file"
);
}
#[tokio::test]
async fn state_modifying_git_add_is_not_routed() {
let _l = env_lock().await;
let _route_on = EnvVar::unset("NEWT_NO_ROUTE");
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let _eng = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
let ws = tempfile::TempDir::new().unwrap();
let init = {
let mut cmd = crate::git_hardening::hardened_git(ws.path(), &["init", "-q"]).unwrap();
#[cfg(windows)]
if let Ok(canonical) = ws.path().canonicalize() {
if let Some(dir) = canonical.to_str() {
cmd.env("GIT_CONFIG_COUNT", "1")
.env("GIT_CONFIG_KEY_0", "safe.directory")
.env("GIT_CONFIG_VALUE_0", dir);
}
}
cmd.output().unwrap()
};
assert!(init.status.success(), "{init:?}");
std::fs::write(ws.path().join("stage-me.txt"), "stage this file\n").unwrap();
let mut caveats = caveats_no_exec(ws.path());
caveats.net = Scope::All;
let denied = run_command_with_legacy_git("git add stage-me.txt", ws.path(), &caveats).await;
assert!(denied.contains("capability denied"), "{denied}");
assert!(
!denied.contains(super::native_git::WINDOWS_APPCONTAINER_GIT_UNAVAILABLE),
"the ordinary exec denial must win before the Windows compatibility guard: {denied}"
);
assert!(!ws.path().join(".git/index").exists());
caveats.exec = Scope::All;
let out = run_command_with_legacy_git("git add stage-me.txt", ws.path(), &caveats).await;
assert!(
!out.contains("routed"),
"git add must NOT route to the git built-in; got: {out}"
);
let staged = {
let mut cmd =
crate::git_hardening::hardened_git(ws.path(), &["diff", "--cached", "--name-only"])
.unwrap();
#[cfg(windows)]
if let Ok(canonical) = ws.path().canonicalize() {
if let Some(dir) = canonical.to_str() {
cmd.env("GIT_CONFIG_COUNT", "1")
.env("GIT_CONFIG_KEY_0", "safe.directory")
.env("GIT_CONFIG_VALUE_0", dir);
}
}
cmd.output().unwrap()
};
assert!(staged.status.success(), "{staged:?}; dispatch: {out}");
#[cfg(target_os = "windows")]
{
assert!(
out.contains(super::native_git::WINDOWS_APPCONTAINER_GIT_UNAVAILABLE),
"restricted AppContainer must refuse before native Git runs: {out}"
);
assert!(!ws.path().join(".git/index").exists());
assert_eq!(
String::from_utf8(staged.stdout).unwrap().trim(),
"",
"the unavailable command must not stage the file: {out}"
);
}
#[cfg(not(target_os = "windows"))]
assert_eq!(
String::from_utf8(staged.stdout).unwrap().trim(),
"stage-me.txt",
"native dispatch must actually stage the file: {out}"
);
}
#[cfg(target_os = "windows")]
#[tokio::test]
async fn native_git_exec_grant_retry_is_refused_before_spawn() {
let _l = env_lock().await;
let _route_on = EnvVar::unset("NEWT_NO_ROUTE");
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let _eng = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
let ws = tempfile::TempDir::new().unwrap();
let init = crate::git_hardening::hardened_git(ws.path(), &["init", "-q"])
.unwrap()
.output()
.unwrap();
assert!(init.status.success(), "{init:?}");
std::fs::write(ws.path().join("retry-me.txt"), "retry evidence\n").unwrap();
struct GrantExecGate {
requests: Vec<super::PermissionRequest>,
granted: Caveats,
}
impl super::PermissionGate for GrantExecGate {
fn ask(&mut self, requests: &[super::PermissionRequest]) -> super::PermissionDecision {
self.requests.extend_from_slice(requests);
super::PermissionDecision::Allow(self.granted.clone())
}
fn ask_question(&mut self, _: &str) -> super::HumanQuestionOutcome {
super::HumanQuestionOutcome::Unavailable
}
}
let mut caveats = caveats_no_exec(ws.path());
caveats.net = Scope::All;
let mut granted = caveats.clone();
granted.exec = Scope::All;
let mut gate = GrantExecGate {
requests: vec![],
granted,
};
let out = run_command_with_legacy_git_and_gate(
"git add retry-me.txt",
ws.path(),
&caveats,
&mut gate,
)
.await;
assert!(
out.contains(super::native_git::WINDOWS_APPCONTAINER_GIT_UNAVAILABLE),
"the post-grant retry must be refused before native Git runs: {out}"
);
assert_eq!(gate.requests.len(), 1, "{out}");
assert_eq!(gate.requests[0].kind, super::DenialKind::Exec, "{out}");
assert_eq!(gate.requests[0].target, "git", "{out}");
assert!(
!ws.path().join(".git/index").exists(),
"the refused retry must not stage a file: {out}"
);
}
#[tokio::test]
async fn no_route_bypasses_routing_but_keeps_l3() {
let _l = env_lock().await;
let _route_off = EnvVar::set("NEWT_NO_ROUTE", "1");
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let _eng = EnvVar::set("NEWT_SHELL_ENGINE", "safe-subset");
assert!(routing_disabled() && !ocap_disabled(), "L2 off, L3 on");
let ws = tempfile::TempDir::new().unwrap();
let caveats = caveats_no_exec(ws.path());
let out = run_tool(
"run_command",
serde_json::json!({ "command": "cat /etc/shadow" }),
ws.path(),
&caveats,
)
.await;
assert!(
!out.contains("fs_read does not permit"),
"--no-route must not route to read_file; got: {out}"
);
assert!(
out.contains("capability denied"),
"the L3 confined dispatch must still gate the command; got: {out}"
);
}
#[tokio::test]
async fn confined_shell_env_carries_the_fence_scratch_root_as_tmpdir() {
let _lock = env_lock().await;
let _pt = EnvVar::unset("NEWT_SHELL_ENV_PASSTHROUGH");
let _config = EnvVar::set("NEWT_CONFIG_DIR", "/nonexistent-newt-tmpdir-test");
let _parent = EnvVar::set("TMPDIR", "/parent-only-tmpdir");
{
let _child = EnvVar::unset("NEWT_CHILD_TMPDIR");
assert!(
!venv_env_map().contains_key("TMPDIR"),
"no root published, none passed"
);
}
let _child = EnvVar::set("NEWT_CHILD_TMPDIR", "/srv/scratch");
assert_eq!(
venv_env_map().get("TMPDIR").map(String::as_str),
Some("/srv/scratch")
);
}
#[tokio::test]
async fn failed_routed_build_ledgers_not_ok() {
let _l = env_lock().await;
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let ws = tempfile::TempDir::new().unwrap();
let caveats = Caveats::top();
let out = execute_tool(
"build_exec",
&serde_json::json!({ "argv": ["false"] }),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None, None, None,
None, None, None, None, None, None, None, )
.await;
if crate::confined_exec::kernel_fs_fence_available() {
assert!(
!super::tool_result_ok(&out),
"a routed build whose command failed must not read ok:true; got: {out}"
);
assert!(
out.contains("routed `false` to the confined build lane"),
"the routed note must still be present, appended after the failure \
text, not prepended in front of it; got: {out}"
);
}
let out_ok = execute_tool(
"build_exec",
&serde_json::json!({ "argv": ["true"] }),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
if crate::confined_exec::kernel_fs_fence_available() {
assert!(
super::tool_result_ok(&out_ok),
"a routed build that PASSED must still read ok:true; got: {out_ok}"
);
}
}
#[test]
fn build_route_refuses_unsafe_argv_shapes() {
use super::super::routing::{RouteDecision, RouteTable};
let table = RouteTable::builtin();
let cwd = std::path::Path::new("/never-matches-a-test-cwd");
for cmd in [
r#"cargo test "a b""#,
"cargo test --manifest-path ~/x",
"cargo build --bin *",
r#"cargo test 'a b'"#,
r"cargo test a\ b",
] {
assert_eq!(
table.classify(cmd, cwd, &crate::caveats::Scope::All),
RouteDecision::Exec,
"{cmd:?} carries a token the shell would transform; it must gate to exec"
);
}
assert!(
matches!(
table.classify("cargo test -p newt-core", cwd, &crate::caveats::Scope::All),
RouteDecision::Route {
tool: "build_exec",
..
}
),
"a bare, unquoted argv must still route"
);
}
#[tokio::test]
async fn just_with_justfile_one_level_up_falls_back_to_exec_not_error() {
let _l = env_lock().await;
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let root = tempfile::TempDir::new().unwrap();
std::fs::write(
root.path().join("justfile"),
"check:\n\techo up-one-level\n",
)
.unwrap();
let sub = root.path().join("crates/foo");
std::fs::create_dir_all(&sub).unwrap();
let caveats = Caveats::top();
let out = execute_tool(
"build_exec",
&serde_json::json!({ "argv": ["just", "check"] }),
&sub.to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
assert!(
!out.starts_with("error: no justfile"),
"a justfile one level up must be found, not reported missing; got: {out}"
);
}
#[cfg(not(windows))]
#[tokio::test]
async fn a_routed_short_timeout_wrapper_honours_its_own_wall_not_the_lane_wall() {
let _l = env_lock().await;
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let ws = tempfile::TempDir::new().unwrap();
std::fs::write(ws.path().join("justfile"), "slow:\n\tsleep 10\n").unwrap();
let caveats = Caveats::top();
let out = execute_tool_with_offload(
"build_exec",
&serde_json::json!({
"argv": ["just", "slow"],
"timeout_secs": 2,
}),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None, None, None, None, None, None, None, None, None, None, true, None, None, )
.await;
if crate::confined_exec::kernel_fs_fence_available() {
assert!(
!super::tool_result_ok(&out),
"a call killed by its own 2-second wall must not read ok:true; got: {out}"
);
assert!(
out.contains("`timeout 2` honoured as this lane's wall (2s)"),
"the note must name the 2-second wall actually applied, not the 30 min \
lane wall; got: {out}"
);
}
}
#[cfg(not(windows))]
fn write_failing_scratch_crate(dir: &std::path::Path) {
std::fs::write(
dir.join("Cargo.toml"),
"[package]\nname = \"scratch\"\nversion = \"0.0.0\"\nedition = \"2021\"\n",
)
.unwrap();
std::fs::create_dir_all(dir.join("src")).unwrap();
let body: String = (1..=20)
.map(|i| format!(" let _ = UNDEFINED_VAR_{i};\n"))
.collect();
std::fs::write(dir.join("src/main.rs"), format!("fn main() {{\n{body}}}\n")).unwrap();
}
#[cfg(not(windows))]
fn write_passing_scratch_crate(dir: &std::path::Path) {
std::fs::write(
dir.join("Cargo.toml"),
"[package]\nname = \"scratch\"\nversion = \"0.0.0\"\nedition = \"2021\"\n",
)
.unwrap();
std::fs::create_dir_all(dir.join("src")).unwrap();
let body: String = (1..=10)
.map(|i| format!(" let unused_var_{i} = {i};\n"))
.collect();
std::fs::write(dir.join("src/main.rs"), format!("fn main() {{\n{body}}}\n")).unwrap();
}
#[cfg(not(windows))]
#[tokio::test]
async fn a_failing_build_piped_to_tail_still_reports_the_real_failure() {
let _l = env_lock().await;
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let root = tempfile::TempDir::new().unwrap();
write_failing_scratch_crate(root.path());
let caveats = Caveats::top();
let out = execute_tool(
"run_command",
&serde_json::json!({ "command": "cargo check 2>&1 | tail -10" }),
&root.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
assert!(
out.contains("error: command exited 1"),
"the build's real failure must survive the pipe, never masked by \
tail's own exit code: {out}"
);
assert!(
out.contains("routed") && out.contains("confined build lane"),
"the failing build must still have been ROUTED (not run in the \
confined shell, where the pipe WOULD mask the exit code): {out}"
);
assert!(
out.contains("output trimmed to the last 10 lines"),
"must say the output was trimmed: {out}"
);
assert!(out.contains("UNDEFINED_VAR_20"), "{out}");
assert!(
!out.contains("UNDEFINED_VAR_1`"),
"only the last 10 lines should remain: {out}"
);
}
#[cfg(not(windows))]
#[tokio::test]
async fn a_trimmed_build_retains_full_diagnostics_for_retrieval() {
use crate::agentic::content_spill::{SessionSpillStore, SpillCid, SpillStore};
use crate::agentic::memory_fetch::{execute_memory_fetch, StoreMemorySource};
let _l = env_lock().await;
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let root = tempfile::TempDir::new().unwrap();
write_failing_scratch_crate(root.path());
let spill = SessionSpillStore::new([19u8; 16]);
let out = execute_tool_with_collaborators(
"run_command",
&serde_json::json!({"command": "cargo check 2>&1 | tail -10; echo \"EXIT: $?\""}),
&root.path().to_string_lossy(),
false,
20,
&Caveats::top(),
&mut NoMcp,
ToolCollaborators {
spill_store: Some(&spill),
..Default::default()
},
true,
PromptDisposition::Act,
None,
)
.await
.unwrap()
.unwrap();
assert!(out.contains("error: command exited 1"), "{out}");
assert!(out.contains("output trimmed to the last 10 lines"), "{out}");
assert!(out.contains("UNDEFINED_VAR_20"), "{out}");
assert!(
!out.contains("UNDEFINED_VAR_1`"),
"tail must stay selected: {out}"
);
let handle = out
.split("spill:")
.nth(1)
.and_then(|rest| rest.split('"').next())
.expect("trimmed build must expose the existing full-output retrieval handle");
let cid = SpillCid::parse(handle).unwrap();
let full = spill
.fetch(&cid)
.expect("retained build output")
.redacted_text;
assert!(
full.contains("UNDEFINED_VAR_1`"),
"first error was lost: {full}"
);
assert!(
full.contains("UNDEFINED_VAR_20"),
"last error was lost: {full}"
);
let source = StoreMemorySource::from_stores(None, None).with_spill_store(&spill);
let recovered = execute_memory_fetch(
&serde_json::json!({"address": format!("spill:{handle}"), "grep": "UNDEFINED_VAR_1`"}),
&source,
false,
20,
);
assert!(recovered.contains("UNDEFINED_VAR_1`"), "{recovered}");
}
#[cfg(not(windows))]
#[tokio::test]
async fn a_failing_build_with_a_trailing_exit_echo_still_reports_the_real_failure() {
let _l = env_lock().await;
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let root = tempfile::TempDir::new().unwrap();
write_failing_scratch_crate(root.path());
let caveats = Caveats::top();
let out = execute_tool(
"run_command",
&serde_json::json!({ "command": "cargo check 2>&1; echo \"EXIT: $?\"" }),
&root.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
assert!(
out.contains("error: command exited 1"),
"the build's real failure must survive, never masked by the shell's \
own $? (always 0 after `cmd; echo …$?…`): {out}"
);
assert!(
out.contains("routed") && out.contains("confined build lane"),
"must have been ROUTED (not run in the confined shell, where the \
trailing echo WOULD have kept it compound and masked the exit \
code): {out}"
);
assert!(
out.contains("a trailing exit-code `echo` was dropped"),
"the routed note must say the echo was dropped: {out}"
);
}
#[cfg(not(windows))]
#[tokio::test]
async fn a_passing_build_piped_to_head_keeps_the_pass_and_trims_to_the_first_lines() {
let _l = env_lock().await;
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let root = tempfile::TempDir::new().unwrap();
write_passing_scratch_crate(root.path());
let caveats = Caveats::top();
let out = execute_tool(
"run_command",
&serde_json::json!({ "command": "cargo check 2>&1 | head -5" }),
&root.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
assert!(
!out.contains("error: command exited"),
"a genuinely passing build (warnings only, exit 0) must not be \
reported as failed: {out}"
);
assert!(
out.contains("output trimmed to the first 5 lines"),
"must say the output was trimmed: {out}"
);
assert!(out.contains("unused_var_1`"), "{out}");
assert!(
!out.contains("unused_var_10"),
"only the first 5 lines should remain: {out}"
);
}
#[cfg(not(windows))]
#[tokio::test]
async fn routed_cargo_plus_stable_resolves_the_toolchain_in_the_confined_lane() {
let _l = env_lock().await;
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let root = tempfile::TempDir::new().unwrap();
std::fs::write(
root.path().join("Cargo.toml"),
"[package]\nname = \"scratch\"\nversion = \"0.0.0\"\nedition = \"2021\"\n",
)
.unwrap();
std::fs::create_dir_all(root.path().join("src")).unwrap();
std::fs::write(root.path().join("src/main.rs"), "fn main() {}\n").unwrap();
let caveats = Caveats::top();
let out = execute_tool(
"run_command",
&serde_json::json!({ "command": "cargo +stable check 2>&1 | tail -20" }),
&root.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
assert!(
out.contains("routed `cargo +stable check`") && out.contains("confined build lane"),
"must have been routed with the toolchain selector kept in the argv: {out}"
);
assert!(
!out.contains("toolchain") || !out.contains("is not installed"),
"rustup must resolve +stable inside the confined lane's forwarded \
RUSTUP_HOME/CARGO_HOME, not report it missing: {out}"
);
assert!(
!out.contains("error: command exited"),
"cargo +stable check on a trivial offline crate must actually \
succeed in this environment: {out}"
);
}
#[cfg(not(windows))]
#[tokio::test]
async fn a_noop_cd_to_the_workspace_root_still_routes_and_says_so() {
let _l = env_lock().await;
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let root = tempfile::TempDir::new().unwrap();
write_passing_scratch_crate(root.path());
let caveats = Caveats::top();
let root_str = root.path().to_string_lossy();
let out = execute_tool(
"run_command",
&serde_json::json!({ "command": format!("cd {root_str} && cargo check") }),
&root_str,
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
assert!(
out.contains("routed `cargo check`") && out.contains("confined build lane"),
"the cd-prefixed call must still route, with the cd stripped from \
the displayed argv: {out}"
);
assert!(
!out.contains("cwd="),
"the workspace root resolves to cwd=\".\" — nothing to name: {out}"
);
assert!(
!out.contains("error: command exited"),
"a genuinely passing build must not be reported as failed: {out}"
);
}
#[cfg(not(windows))]
#[tokio::test]
async fn a_cd_prefixed_subdirectory_routes_and_says_so() {
let _l = env_lock().await;
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let root = tempfile::TempDir::new().unwrap();
let sub = root.path().join("sub");
std::fs::create_dir(&sub).unwrap();
write_passing_scratch_crate(&sub);
let caveats = Caveats::top();
let root_str = root.path().to_string_lossy();
let out = execute_tool(
"run_command",
&serde_json::json!({ "command": "cd sub && cargo check" }),
&root_str,
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
assert!(
out.contains("routed `cargo check`") && out.contains("confined build lane"),
"the cd-prefixed subdirectory call must route too: {out}"
);
assert!(
out.contains("cwd=sub"),
"the routed note must name the folded directory: {out}"
);
assert!(
!out.contains("error: command exited"),
"a genuinely passing build must not be reported as failed: {out}"
);
}
#[cfg(not(windows))]
#[tokio::test]
async fn lifecycle_run_of_build_tool_requests_build_authority() {
struct RecordingDenyGate {
seen: Option<super::PermissionRequest>,
}
impl super::PermissionGate for RecordingDenyGate {
fn ask(&mut self, requests: &[super::PermissionRequest]) -> super::PermissionDecision {
self.seen = requests.first().cloned();
super::PermissionDecision::Deny
}
fn ask_question(&mut self, _question: &str) -> HumanQuestionOutcome {
HumanQuestionOutcome::Cancelled
}
}
let _l = env_lock().await;
let _route_on = EnvVar::unset("NEWT_NO_ROUTE");
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let ws = tempfile::TempDir::new().unwrap();
std::fs::write(
ws.path().join("Cargo.toml"),
"[package]\nname = \"dummy\"\n",
)
.unwrap();
let caveats = caveats_no_exec(ws.path());
let mut gate = RecordingDenyGate { seen: None };
let out = execute_tool(
"lifecycle",
&serde_json::json!({"phase": "check"}),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None,
Some(&mut gate),
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
let request = gate
.seen
.expect("lifecycle run of a build-tool command must consult the build-authority gate");
assert_eq!(
request.kind,
super::DenialKind::Build,
"must request Build authority, not exec/fs; got: {:?}",
request.kind
);
assert!(
out.contains("build authority") || out.contains("capability denied"),
"result must name build authority, not 'just isn't available'; got: {out}"
);
assert!(
!out.contains("isn't available") && !out.contains("Unavailable"),
"must not fail in the run lane; got: {out}"
);
}
#[cfg(not(windows))]
#[tokio::test]
async fn lifecycle_run_of_non_build_tool_does_not_request_build_authority() {
struct RecordingDenyGate {
seen: Option<super::PermissionRequest>,
}
impl super::PermissionGate for RecordingDenyGate {
fn ask(&mut self, requests: &[super::PermissionRequest]) -> super::PermissionDecision {
self.seen = requests.first().cloned();
super::PermissionDecision::Deny
}
fn ask_question(&mut self, _question: &str) -> HumanQuestionOutcome {
HumanQuestionOutcome::Cancelled
}
}
let _l = env_lock().await;
let _route_on = EnvVar::unset("NEWT_NO_ROUTE");
let _ocap_off = EnvVar::unset("NEWT_DISABLE_OCAP");
let ws = tempfile::TempDir::new().unwrap();
std::fs::write(
ws.path().join("pyproject.toml"),
"[project]\nname = \"dummy\"\n",
)
.unwrap();
let caveats = caveats_no_exec(ws.path());
let mut gate = RecordingDenyGate { seen: None };
let _out = execute_tool(
"lifecycle",
&serde_json::json!({"phase": "check"}),
&ws.path().to_string_lossy(),
false,
20,
&caveats,
&mut NoMcp,
None,
None,
None,
None,
Some(&mut gate),
None,
None,
None,
None,
None,
None,
None,
None,
)
.await;
let has_build_request = gate
.seen
.as_ref()
.is_some_and(|r| r.kind == super::DenialKind::Build);
assert!(
!has_build_request,
"a non-build-tool lifecycle run must NOT route to the build lane; \
gate saw: {:?}",
gate.seen
);
}
#[cfg(target_os = "macos")]
#[tokio::test]
#[serial_test::serial]
async fn confined_python3_runs_on_macos() {
let _l = env_lock().await;
let _ocap = EnvVar::unset("NEWT_DISABLE_OCAP");
let _full = EnvVar::unset("NEWT_FULL_ACCESS");
let ws = tempfile::TempDir::new().unwrap();
let caveats = Caveats {
exec: Scope::only(["python3".to_string()]),
net: Scope::All,
..caveats_no_exec(ws.path())
};
let out = run_tool(
"run_command",
serde_json::json!({"command": "python3 -c 'print(40 + 2)'"}),
ws.path(),
&caveats,
)
.await;
assert!(out.contains("42"), "{out}");
}
#[cfg(unix)]
#[tokio::test]
#[serial_test::serial]
async fn confined_git_ignores_ambient_config_and_carries_the_agent_identity() {
let _l = env_lock().await;
let _ocap = EnvVar::unset("NEWT_DISABLE_OCAP");
let _full = EnvVar::unset("NEWT_FULL_ACCESS");
let ws = tempfile::TempDir::new().unwrap();
let caveats = Caveats {
exec: Scope::only(["git".to_string()]),
#[cfg(target_os = "macos")]
net: Scope::All,
..caveats_no_exec(ws.path())
};
let out = run_tool(
"run_command",
serde_json::json!({"command": "git init -q && git status --short && git config user.email"}),
ws.path(),
&caveats,
)
.await;
assert!(!out.contains("unable to access"), "{out}");
let identity = crate::AgentIdentity::resolve().unwrap_or_default();
assert!(out.contains(&identity.email), "{out}");
}
#[cfg(unix)]
#[tokio::test]
async fn which_2796_ambient_host_routes() {
let _guard = env_lock().await;
use super::shell::{host_shell_command, ShellRoute};
let root = tempfile::tempdir().unwrap();
for fallback in [false, true] {
let mut command = host_shell_command(
ShellRoute::BashSh,
fallback,
"PATH=; which printf && ! which missing_2796",
root.path().to_str().unwrap(),
)
.unwrap();
let out = command.output().await.unwrap();
assert!(out.status.success(), "{out:?}");
assert_eq!(out.stdout, b"printf\n");
}
}