Default to whitelist mode and parallelize tests
Flips the default sandbox mode from blacklist to whitelist and replaces the global RUST_TEST_THREADS=1 with a targeted RwLock that only serializes blacklist sandboxes against tests mutating glob-matching host paths. A new Sandbox newtype acquires the guard automatically when --blacklist is in args.
This commit is contained in:
+14
-14
@@ -2,7 +2,7 @@ use crate::common::*;
|
||||
|
||||
#[test]
|
||||
fn dry_run_prints_and_exits() {
|
||||
let output = sandbox(&["--dry-run"])
|
||||
let output = Sandbox::new(&["--dry-run"])
|
||||
.args(["--", "bash", "-c", "exit 42"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -20,7 +20,7 @@ fn dry_run_prints_and_exits() {
|
||||
|
||||
#[test]
|
||||
fn dry_run_output_is_copy_pasteable_shell() {
|
||||
let dry = sandbox(&["--dry-run"])
|
||||
let dry = Sandbox::new(&["--dry-run"])
|
||||
.args(["--", "bash", "-c", "echo $HOME"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -34,7 +34,7 @@ fn dry_run_output_is_copy_pasteable_shell() {
|
||||
|
||||
#[test]
|
||||
fn empty_home_rejected() {
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&[])
|
||||
.env("HOME", "")
|
||||
.args(["--", "true"])
|
||||
.output()
|
||||
@@ -53,7 +53,7 @@ fn empty_home_rejected() {
|
||||
|
||||
#[test]
|
||||
fn config_missing_file_errors() {
|
||||
let output = sandbox_withconfig(&["--config", "/nonexistent/config.toml"])
|
||||
let output = Sandbox::new_with_config(&["--config", "/nonexistent/config.toml"])
|
||||
.args(["--", "true"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
@@ -70,7 +70,7 @@ fn config_missing_file_errors() {
|
||||
fn config_invalid_toml_errors() {
|
||||
let cfg = ConfigFile::new("not valid {{{{ toml");
|
||||
|
||||
let output = sandbox_withconfig(&["--config", &cfg])
|
||||
let output = Sandbox::new_with_config(&["--config", &cfg])
|
||||
.args(["--", "true"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
@@ -87,7 +87,7 @@ fn config_invalid_toml_errors() {
|
||||
fn config_unknown_key_errors() {
|
||||
let cfg = ConfigFile::new("hardened = true\nbogus = \"nope\"\n");
|
||||
|
||||
let output = sandbox_withconfig(&["--config", &cfg])
|
||||
let output = Sandbox::new_with_config(&["--config", &cfg])
|
||||
.args(["--", "true"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
@@ -102,7 +102,7 @@ fn config_unknown_key_errors() {
|
||||
|
||||
#[test]
|
||||
fn bwrap_arg_setenv_passes_through() {
|
||||
let output = sandbox(&["--bwrap-arg", "--setenv MYVAR hello"])
|
||||
let output = Sandbox::new(&["--bwrap-arg", "--setenv MYVAR hello"])
|
||||
.args(["--", "bash", "-c", "echo $MYVAR"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -123,7 +123,7 @@ fn config_entrypoint_appends_passthrough_args() {
|
||||
"#,
|
||||
);
|
||||
|
||||
let output = sandbox_withconfig(&["--config", &cfg, "--profile", "test"])
|
||||
let output = Sandbox::new_with_config(&["--config", &cfg, "--profile", "test"])
|
||||
.args(["--", "echo entrypoint-works"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
@@ -145,7 +145,7 @@ fn config_entrypoint_falls_back_to_command_defaults() {
|
||||
"#,
|
||||
);
|
||||
|
||||
let output = sandbox_withconfig(&["--config", &cfg, "--profile", "test"])
|
||||
let output = Sandbox::new_with_config(&["--config", &cfg, "--profile", "test"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
|
||||
@@ -165,7 +165,7 @@ fn config_entrypoint_alone_without_command_or_passthrough() {
|
||||
"#,
|
||||
);
|
||||
|
||||
let output = sandbox_withconfig(&["--config", &cfg, "--profile", "test"])
|
||||
let output = Sandbox::new_with_config(&["--config", &cfg, "--profile", "test"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
|
||||
@@ -178,7 +178,7 @@ fn config_entrypoint_alone_without_command_or_passthrough() {
|
||||
|
||||
#[test]
|
||||
fn cli_entrypoint_appends_passthrough_args() {
|
||||
let output = sandbox(&["--entrypoint", "bash"])
|
||||
let output = Sandbox::new(&["--entrypoint", "bash"])
|
||||
.args(["--", "-c", "echo cli-entrypoint-works"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
@@ -198,7 +198,7 @@ fn cli_entrypoint_overrides_config_entrypoint() {
|
||||
"#,
|
||||
);
|
||||
|
||||
let output = sandbox_withconfig(&["--config", &cfg, "--entrypoint", "bash"])
|
||||
let output = Sandbox::new_with_config(&["--config", &cfg, "--entrypoint", "bash"])
|
||||
.args(["--", "-c", "echo override-works"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
@@ -219,7 +219,7 @@ fn config_command_alone_without_passthrough() {
|
||||
"#,
|
||||
);
|
||||
|
||||
let output = sandbox_withconfig(&["--config", &cfg, "--profile", "test"])
|
||||
let output = Sandbox::new_with_config(&["--config", &cfg, "--profile", "test"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
|
||||
@@ -239,7 +239,7 @@ fn config_command_replaced_by_passthrough() {
|
||||
"#,
|
||||
);
|
||||
|
||||
let output = sandbox_withconfig(&["--config", &cfg, "--profile", "test"])
|
||||
let output = Sandbox::new_with_config(&["--config", &cfg, "--profile", "test"])
|
||||
.args(["--", "bash", "-c", "echo replaced"])
|
||||
.output()
|
||||
.expect("failed to execute");
|
||||
|
||||
+77
-8
@@ -1,18 +1,87 @@
|
||||
use std::fs;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::process::Command;
|
||||
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use tempfile::TempDir;
|
||||
|
||||
pub fn sandbox_withconfig(extra_args: &[&str]) -> Command {
|
||||
let mut cmd = Command::new(env!("CARGO_BIN_EXE_agent-sandbox"));
|
||||
cmd.args(extra_args);
|
||||
cmd
|
||||
// Blacklist mode globs the host filesystem at sandbox startup to find
|
||||
// sensitive paths to mask. A matching host file that vanishes mid-startup
|
||||
// makes bwrap fail in any concurrent blacklist sandbox.
|
||||
pub struct HostGlobsLock;
|
||||
|
||||
impl HostGlobsLock {
|
||||
/// Acquire shared access. Hold while running a blacklist sandbox.
|
||||
pub fn for_scan() -> RwLockReadGuard<'static, ()> {
|
||||
LOCK.read().unwrap()
|
||||
}
|
||||
|
||||
/// Acquire exclusive access. Hold while creating or deleting a host
|
||||
/// path that may match a blacklist glob (e.g. `/tmp/ssh-*`).
|
||||
pub fn for_mutation() -> RwLockWriteGuard<'static, ()> {
|
||||
LOCK.write().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sandbox(extra_args: &[&str]) -> Command {
|
||||
let mut cmd = sandbox_withconfig(&["--no-config"]);
|
||||
cmd.args(extra_args);
|
||||
cmd
|
||||
static LOCK: RwLock<()> = RwLock::new(());
|
||||
|
||||
#[allow(dead_code)]
|
||||
enum HostGlobsGuard {
|
||||
Scan(RwLockReadGuard<'static, ()>),
|
||||
Mutation(RwLockWriteGuard<'static, ()>),
|
||||
}
|
||||
|
||||
pub struct Sandbox {
|
||||
cmd: Command,
|
||||
_guard: Option<HostGlobsGuard>,
|
||||
}
|
||||
|
||||
impl Sandbox {
|
||||
pub fn new(extra_args: &[&str]) -> Self {
|
||||
Self::build(&["--no-config"], extra_args, scan_guard_for(extra_args))
|
||||
}
|
||||
|
||||
pub fn new_with_config(extra_args: &[&str]) -> Self {
|
||||
Self::build(&[], extra_args, scan_guard_for(extra_args))
|
||||
}
|
||||
|
||||
pub fn new_for_host_mutation(extra_args: &[&str]) -> Self {
|
||||
debug_assert!(
|
||||
extra_args.contains(&"--blacklist"),
|
||||
"new_for_host_mutation is only meaningful for blacklist sandboxes"
|
||||
);
|
||||
Self::build(
|
||||
&["--no-config"],
|
||||
extra_args,
|
||||
Some(HostGlobsGuard::Mutation(HostGlobsLock::for_mutation())),
|
||||
)
|
||||
}
|
||||
|
||||
fn build(prefix: &[&str], extra_args: &[&str], guard: Option<HostGlobsGuard>) -> Self {
|
||||
let mut cmd = Command::new(env!("CARGO_BIN_EXE_agent-sandbox"));
|
||||
cmd.args(prefix);
|
||||
cmd.args(extra_args);
|
||||
Self { cmd, _guard: guard }
|
||||
}
|
||||
}
|
||||
|
||||
fn scan_guard_for(extra_args: &[&str]) -> Option<HostGlobsGuard> {
|
||||
extra_args
|
||||
.contains(&"--blacklist")
|
||||
.then(|| HostGlobsGuard::Scan(HostGlobsLock::for_scan()))
|
||||
}
|
||||
|
||||
impl Deref for Sandbox {
|
||||
type Target = Command;
|
||||
fn deref(&self) -> &Command {
|
||||
&self.cmd
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Sandbox {
|
||||
fn deref_mut(&mut self) -> &mut Command {
|
||||
&mut self.cmd
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ConfigFile {
|
||||
|
||||
+16
-8
@@ -6,7 +6,7 @@ fn printenv_inside(args: &[&str], vars: &[(&str, &str)], query: &[&str]) -> Stri
|
||||
.map(|v| format!("printenv {v} || echo MISSING:{v}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("; ");
|
||||
let mut cmd = sandbox(args);
|
||||
let mut cmd = Sandbox::new(args);
|
||||
for (k, v) in vars {
|
||||
cmd.env(k, v);
|
||||
}
|
||||
@@ -219,7 +219,7 @@ fn whitelist_unsetenv_overrides_kept_var() {
|
||||
#[test]
|
||||
fn blacklist_drops_token_and_secret_vars() {
|
||||
let stdout = printenv_inside(
|
||||
&[],
|
||||
&["--blacklist"],
|
||||
&[
|
||||
("GH_TOKEN", "gh-secret"),
|
||||
("AWS_SECRET_ACCESS_KEY", "aws-secret"),
|
||||
@@ -252,7 +252,7 @@ fn blacklist_drops_token_and_secret_vars() {
|
||||
#[test]
|
||||
fn blacklist_carves_out_vendor_api_keys() {
|
||||
let stdout = printenv_inside(
|
||||
&[],
|
||||
&["--blacklist"],
|
||||
&[
|
||||
("ANTHROPIC_API_KEY", "anthropic-key"),
|
||||
("OPENAI_API_KEY", "openai-key"),
|
||||
@@ -272,7 +272,7 @@ fn blacklist_carves_out_vendor_api_keys() {
|
||||
#[test]
|
||||
fn blacklist_suffix_match_does_not_catch_substring() {
|
||||
let stdout = printenv_inside(
|
||||
&[],
|
||||
&["--blacklist"],
|
||||
&[
|
||||
("TOKENIZER_PATH", "/opt/tok"),
|
||||
("MY_TOKEN_HOLDER", "holder"),
|
||||
@@ -291,14 +291,18 @@ fn blacklist_suffix_match_does_not_catch_substring() {
|
||||
|
||||
#[test]
|
||||
fn blacklist_keeps_unrelated_host_var() {
|
||||
let stdout = printenv_inside(&[], &[("MY_NICE_VAR", "hello")], &["MY_NICE_VAR"]);
|
||||
let stdout = printenv_inside(
|
||||
&["--blacklist"],
|
||||
&[("MY_NICE_VAR", "hello")],
|
||||
&["MY_NICE_VAR"],
|
||||
);
|
||||
assert!(stdout.contains("hello"), "MY_NICE_VAR stripped: {stdout}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn blacklist_keeps_dbus_vars() {
|
||||
let stdout = printenv_inside(
|
||||
&[],
|
||||
&["--blacklist"],
|
||||
&[
|
||||
("DBUS_SESSION_BUS_ADDRESS", "unix:path=/tmp/fake"),
|
||||
("DBUS_SYSTEM_BUS_ADDRESS", "unix:path=/tmp/fake-system"),
|
||||
@@ -324,7 +328,11 @@ fn no_env_filter_whitelist_keeps_arbitrary_host_var() {
|
||||
|
||||
#[test]
|
||||
fn no_env_filter_blacklist_keeps_secrets() {
|
||||
let stdout = printenv_inside(&["--no-env-filter"], &[("GH_TOKEN", "kept")], &["GH_TOKEN"]);
|
||||
let stdout = printenv_inside(
|
||||
&["--blacklist", "--no-env-filter"],
|
||||
&[("GH_TOKEN", "kept")],
|
||||
&["GH_TOKEN"],
|
||||
);
|
||||
assert!(
|
||||
stdout.contains("kept"),
|
||||
"expected --no-env-filter to pass secrets through, got: {stdout}"
|
||||
@@ -347,7 +355,7 @@ fn no_env_filter_still_honors_user_env() {
|
||||
#[test]
|
||||
fn blacklist_env_overrides_builtin_deny() {
|
||||
let stdout = printenv_inside(
|
||||
&["--env", "GH_TOKEN=overridden"],
|
||||
&["--blacklist", "--env", "GH_TOKEN=overridden"],
|
||||
&[("GH_TOKEN", "original")],
|
||||
&["GH_TOKEN"],
|
||||
);
|
||||
|
||||
+10
-10
@@ -2,7 +2,7 @@ use crate::common::*;
|
||||
|
||||
#[test]
|
||||
fn whitelist_hides_home_contents() {
|
||||
let output = sandbox(&["--whitelist"])
|
||||
let output = Sandbox::new(&["--whitelist"])
|
||||
.args(["--", "bash", "-c", "ls ~/Documents 2>&1 || echo hidden"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -16,7 +16,7 @@ fn whitelist_hides_home_contents() {
|
||||
|
||||
#[test]
|
||||
fn whitelist_sys_is_readable() {
|
||||
let output = sandbox(&["--whitelist"])
|
||||
let output = Sandbox::new(&["--whitelist"])
|
||||
.args(["--", "bash", "-c", "cat /sys/class/net/lo/address"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -30,7 +30,7 @@ fn whitelist_sys_is_readable() {
|
||||
|
||||
#[test]
|
||||
fn blacklist_run_is_tmpfs() {
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&["--blacklist"])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -49,7 +49,7 @@ fn blacklist_run_is_tmpfs() {
|
||||
|
||||
#[test]
|
||||
fn blacklist_run_dbus_socket_accessible() {
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&["--blacklist"])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -71,7 +71,7 @@ fn blacklist_runuser_is_tmpfs() {
|
||||
let run_user = agent_sandbox::require_run_user().expect("failed to determine XDG_RUNTIME_DIR");
|
||||
let script = format!("ls -A {} | grep -v '^bus$'", run_user);
|
||||
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&["--blacklist"])
|
||||
.args(["--", "bash", "-c", &script])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -86,7 +86,7 @@ fn blacklist_runuser_is_tmpfs() {
|
||||
|
||||
#[test]
|
||||
fn blacklist_dev_input_hidden() {
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&["--blacklist"])
|
||||
.args(["--", "bash", "-c", "ls /dev/input/ 2>/dev/null | wc -l"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -100,7 +100,7 @@ fn blacklist_dev_input_hidden() {
|
||||
|
||||
#[test]
|
||||
fn blacklist_root_is_readonly() {
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&["--blacklist"])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -124,7 +124,7 @@ fn blacklist_root_is_readonly() {
|
||||
|
||||
#[test]
|
||||
fn whitelist_root_is_readonly() {
|
||||
let output = sandbox(&["--whitelist"])
|
||||
let output = Sandbox::new(&["--whitelist"])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -148,7 +148,7 @@ fn whitelist_root_is_readonly() {
|
||||
|
||||
#[test]
|
||||
fn whitelist_mountpoint_parents_are_readonly() {
|
||||
let output = sandbox(&["--whitelist"])
|
||||
let output = Sandbox::new(&["--whitelist"])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -177,7 +177,7 @@ fn whitelist_mountpoint_parents_are_readonly() {
|
||||
|
||||
#[test]
|
||||
fn whitelist_tmp_still_writable() {
|
||||
let output = sandbox(&["--whitelist"])
|
||||
let output = Sandbox::new(&["--whitelist"])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
|
||||
+20
-18
@@ -16,7 +16,7 @@ impl Drop for CleanupFile {
|
||||
fn cwd_is_writable() {
|
||||
let _cleanup = CleanupFile("./sandbox_canary");
|
||||
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&[])
|
||||
.args(["--", "bash", "-c", "touch ./sandbox_canary && echo ok"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -30,7 +30,7 @@ fn cwd_is_writable() {
|
||||
|
||||
#[test]
|
||||
fn host_fs_is_readonly() {
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&[])
|
||||
.args(["--", "bash", "-c", "touch /etc/pwned 2>&1 || echo readonly"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -45,7 +45,7 @@ fn host_fs_is_readonly() {
|
||||
|
||||
#[test]
|
||||
fn ssh_dir_is_hidden() {
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&[])
|
||||
.args(["--", "bash", "-c", "ls ~/.ssh 2>/dev/null | wc -l"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -60,7 +60,7 @@ fn extra_ro_mount() {
|
||||
fs::write(dir.path().join("hello.txt"), "hi").expect("failed to write test file");
|
||||
let dir_str = dir.path().to_str().unwrap();
|
||||
|
||||
let output = sandbox(&["--ro", dir_str])
|
||||
let output = Sandbox::new(&["--ro", dir_str])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -86,7 +86,7 @@ fn extra_rw_mount() {
|
||||
let dir = TempDir::new().expect("failed to create temp dir");
|
||||
let dir_str = dir.path().to_str().unwrap();
|
||||
|
||||
let output = sandbox(&["--rw", dir_str])
|
||||
let output = Sandbox::new(&["--rw", dir_str])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -111,7 +111,7 @@ fn ro_mount_with_remapped_target() {
|
||||
let src_str = dir.path().to_str().unwrap();
|
||||
let target = "/tmp/agent-sandbox-remap-test";
|
||||
|
||||
let output = sandbox(&["--ro", &format!("{src_str}:{target}")])
|
||||
let output = Sandbox::new(&["--ro", &format!("{src_str}:{target}")])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -138,7 +138,7 @@ fn rw_refines_ro_parent() {
|
||||
let parent_str = parent.path().to_str().unwrap();
|
||||
let child_str = child.to_str().unwrap();
|
||||
|
||||
let output = sandbox(&["--ro", parent_str, "--rw", child_str])
|
||||
let output = Sandbox::new(&["--ro", parent_str, "--rw", child_str])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -164,10 +164,11 @@ fn rw_refines_ro_parent() {
|
||||
|
||||
#[test]
|
||||
fn blacklist_overlays_survive_tmp_bind() {
|
||||
let mut sandbox = Sandbox::new_for_host_mutation(&["--blacklist"]);
|
||||
fs::write("/tmp/ssh-sandbox-test", "secret").expect("failed to write sentinel");
|
||||
let _cleanup = CleanupFile("/tmp/ssh-sandbox-test");
|
||||
|
||||
let output = sandbox(&[])
|
||||
let output = sandbox
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -190,7 +191,7 @@ fn blacklist_overlays_survive_tmp_bind() {
|
||||
|
||||
#[test]
|
||||
fn relative_chdir_works() {
|
||||
let output = sandbox(&["--chdir", "src"])
|
||||
let output = Sandbox::new(&["--chdir", "src"])
|
||||
.args(["--", "bash", "-c", "pwd"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -209,7 +210,7 @@ fn relative_chdir_works() {
|
||||
|
||||
#[test]
|
||||
fn relative_rw_path_works() {
|
||||
let output = sandbox(&["--rw", "src"])
|
||||
let output = Sandbox::new(&["--rw", "src"])
|
||||
.args(["--", "bash", "-c", "echo ok"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -223,7 +224,7 @@ fn relative_rw_path_works() {
|
||||
|
||||
#[test]
|
||||
fn relative_ro_path_works() {
|
||||
let output = sandbox(&["--ro", "src"])
|
||||
let output = Sandbox::new(&["--ro", "src"])
|
||||
.args(["--", "bash", "-c", "echo ok"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -237,7 +238,7 @@ fn relative_ro_path_works() {
|
||||
|
||||
#[test]
|
||||
fn rw_missing_path_errors() {
|
||||
let output = sandbox(&["--rw", "/nonexistent/xyz"])
|
||||
let output = Sandbox::new(&["--rw", "/nonexistent/xyz"])
|
||||
.args(["--", "true"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -259,7 +260,7 @@ fn mask_hides_directory() {
|
||||
fs::write(dir.path().join("secret.txt"), "sensitive").expect("failed to write");
|
||||
let dir_str = dir.path().canonicalize().unwrap();
|
||||
|
||||
let output = sandbox(&["--mask", dir_str.to_str().unwrap()])
|
||||
let output = Sandbox::new(&["--mask", dir_str.to_str().unwrap()])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -283,7 +284,7 @@ fn mask_hides_file() {
|
||||
fs::write(&file, "sensitive").expect("failed to write");
|
||||
let file_str = file.canonicalize().unwrap();
|
||||
|
||||
let output = sandbox(&["--mask", file_str.to_str().unwrap()])
|
||||
let output = Sandbox::new(&["--mask", file_str.to_str().unwrap()])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -309,7 +310,7 @@ fn whitelist_ro_symlink_visible_at_link_path() {
|
||||
std::os::unix::fs::symlink(&target, &link).expect("failed to create symlink");
|
||||
let link_str = link.to_str().unwrap();
|
||||
|
||||
let output = sandbox(&["--whitelist", "--ro", link_str])
|
||||
let output = Sandbox::new(&["--whitelist", "--ro", link_str])
|
||||
.args(["--", "cat", link_str])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -328,7 +329,7 @@ fn mask_nonexistent_path_becomes_tmpfs() {
|
||||
let fake = dir.path().join("does_not_exist");
|
||||
let fake_str = fake.to_str().unwrap();
|
||||
|
||||
let output = sandbox(&["--mask", fake_str])
|
||||
let output = Sandbox::new(&["--mask", fake_str])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -357,7 +358,8 @@ fn blacklist_overlays_survive_absolute_var_run_symlink() {
|
||||
// like --tmpfs /var/run/dbus trip bwrap's re-rooted symlink resolution.
|
||||
// Arch ships /var/run -> ../run (relative) so we synthesize the absolute
|
||||
// layout inside the sandbox to reproduce on any host.
|
||||
let mut bwrap_args = build_bwrap_command(&["--no-seccomp", "--", "true"]);
|
||||
let _guard = HostGlobsLock::for_scan();
|
||||
let mut bwrap_args = build_bwrap_command(&["--blacklist", "--no-seccomp", "--", "true"]);
|
||||
inject_absolute_var_run_symlink(&mut bwrap_args);
|
||||
|
||||
let output = Command::new(&bwrap_args[0])
|
||||
@@ -373,7 +375,7 @@ fn blacklist_overlays_survive_absolute_var_run_symlink() {
|
||||
);
|
||||
}
|
||||
fn build_bwrap_command(sandbox_args: &[&str]) -> Vec<String> {
|
||||
let output = sandbox(&["--dry-run"])
|
||||
let output = Sandbox::new(&["--dry-run"])
|
||||
.args(sandbox_args)
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
|
||||
@@ -13,7 +13,7 @@ fn read_sid_from_stat(stat: &str) -> u32 {
|
||||
}
|
||||
|
||||
fn read_sid_inside_sandbox(extra_args: &[&str]) -> u32 {
|
||||
let output = sandbox(extra_args)
|
||||
let output = Sandbox::new(extra_args)
|
||||
.args(["--", "bash", "-c", "cat /proc/self/stat"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -26,7 +26,7 @@ fn read_sid_current_process() -> u32 {
|
||||
}
|
||||
#[test]
|
||||
fn unshare_net_blocks_network() {
|
||||
let output = sandbox(&["--unshare-net"])
|
||||
let output = Sandbox::new(&["--unshare-net"])
|
||||
.args([
|
||||
"--",
|
||||
"bash",
|
||||
@@ -45,7 +45,7 @@ fn unshare_net_blocks_network() {
|
||||
|
||||
#[test]
|
||||
fn hardened_pid_namespace() {
|
||||
let output = sandbox(&["--hardened"])
|
||||
let output = Sandbox::new(&["--hardened"])
|
||||
.args(["--", "bash", "-c", "ls /proc | grep -cE '^[0-9]+$'"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -65,7 +65,7 @@ fn chdir_override() {
|
||||
let dir = TempDir::new().expect("failed to create temp dir");
|
||||
let dir_str = dir.path().to_str().unwrap();
|
||||
|
||||
let output = sandbox(&["--chdir", dir_str])
|
||||
let output = Sandbox::new(&["--chdir", dir_str])
|
||||
.args(["--", "bash", "-c", "pwd"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -82,7 +82,7 @@ fn chdir_under_hardened_tmp() {
|
||||
let dir = TempDir::new().expect("failed to create temp dir");
|
||||
let dir_str = dir.path().to_str().unwrap();
|
||||
|
||||
let output = sandbox(&["--hardened", "--chdir", dir_str])
|
||||
let output = Sandbox::new(&["--hardened", "--chdir", dir_str])
|
||||
.args(["--", "bash", "-c", "pwd && touch ./ok && echo done"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -106,12 +106,12 @@ fn hardened_isolates_sid() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_mode_shares_session() {
|
||||
let inner_sid = read_sid_inside_sandbox(&[]);
|
||||
fn blacklist_mode_shares_session() {
|
||||
let inner_sid = read_sid_inside_sandbox(&["--blacklist"]);
|
||||
let outer_sid = read_sid_current_process();
|
||||
|
||||
assert_eq!(
|
||||
inner_sid, outer_sid,
|
||||
"default-mode sandbox should share the session ID (got {inner_sid} != {outer_sid})"
|
||||
"blacklist-mode sandbox should share the session ID (got {inner_sid} != {outer_sid})"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::common::*;
|
||||
|
||||
#[test]
|
||||
fn seccomp_on_by_default_blocks_unshare() {
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&[])
|
||||
.args(["--", "unshare", "--user", "--map-root-user", "/bin/true"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -15,7 +15,7 @@ fn seccomp_on_by_default_blocks_unshare() {
|
||||
|
||||
#[test]
|
||||
fn seccomp_off_allows_blocked_syscall() {
|
||||
let output = sandbox(&["--no-seccomp"])
|
||||
let output = Sandbox::new(&["--no-seccomp"])
|
||||
.args(["--", "unshare", "--user", "--map-root-user", "/bin/true"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -29,7 +29,7 @@ fn seccomp_off_allows_blocked_syscall() {
|
||||
|
||||
#[test]
|
||||
fn seccomp_dry_run_emits_seccomp_arg() {
|
||||
let output = sandbox(&["--dry-run"])
|
||||
let output = Sandbox::new(&["--dry-run"])
|
||||
.args(["--", "/bin/true"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -43,7 +43,7 @@ fn seccomp_dry_run_emits_seccomp_arg() {
|
||||
|
||||
#[test]
|
||||
fn seccomp_dry_run_no_seccomp_omits_arg() {
|
||||
let output = sandbox(&["--dry-run", "--no-seccomp"])
|
||||
let output = Sandbox::new(&["--dry-run", "--no-seccomp"])
|
||||
.args(["--", "/bin/true"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -57,7 +57,7 @@ fn seccomp_dry_run_no_seccomp_omits_arg() {
|
||||
|
||||
#[test]
|
||||
fn seccomp_normal_workload_succeeds() {
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&[])
|
||||
.args(["--", "bash", "-c", "ls /etc > /dev/null && date"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -72,7 +72,7 @@ fn seccomp_normal_workload_succeeds() {
|
||||
fn seccomp_bash_pthread_fallback_works() {
|
||||
// Verifies the ENOSYS-not-EPERM choice for clone3 doesn't break libc's
|
||||
// clone3 -> clone fallback path that bash uses internally.
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&[])
|
||||
.args(["--", "bash", "-c", "true"])
|
||||
.output()
|
||||
.expect("agent-sandbox binary failed to execute");
|
||||
@@ -92,7 +92,7 @@ fn seccomp_blocks_tiocsti() {
|
||||
// On kernels >= 6.2 with CONFIG_LEGACY_TIOCSTI=n, the kernel blocks TIOCSTI
|
||||
// before seccomp sees it. We test with --no-seccomp first to detect that and
|
||||
// skip, so the test only asserts our filter's behaviour.
|
||||
let baseline = sandbox(&["--no-seccomp"])
|
||||
let baseline = Sandbox::new(&["--no-seccomp"])
|
||||
.args([
|
||||
"--",
|
||||
"python3",
|
||||
@@ -107,7 +107,7 @@ fn seccomp_blocks_tiocsti() {
|
||||
return;
|
||||
}
|
||||
|
||||
let output = sandbox(&[])
|
||||
let output = Sandbox::new(&[])
|
||||
.args([
|
||||
"--",
|
||||
"python3",
|
||||
|
||||
Reference in New Issue
Block a user