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:
2026-04-27 08:18:41 +02:00
parent c77dbc10c3
commit 6e81866226
12 changed files with 158 additions and 81 deletions
+77 -8
View File
@@ -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 {