6e81866226
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.
110 lines
2.8 KiB
Rust
110 lines
2.8 KiB
Rust
use std::fs;
|
|
use std::ops::{Deref, DerefMut};
|
|
use std::process::Command;
|
|
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
|
|
|
use tempfile::TempDir;
|
|
|
|
// 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()
|
|
}
|
|
}
|
|
|
|
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 {
|
|
_dir: TempDir,
|
|
path: String,
|
|
}
|
|
|
|
impl ConfigFile {
|
|
pub fn new(content: &str) -> Self {
|
|
let dir = TempDir::new().unwrap();
|
|
let path = dir.path().join("config.toml");
|
|
fs::write(&path, content).expect("failed to write config");
|
|
Self {
|
|
_dir: dir,
|
|
path: path.to_str().unwrap().to_string(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::ops::Deref for ConfigFile {
|
|
type Target = str;
|
|
fn deref(&self) -> &str {
|
|
&self.path
|
|
}
|
|
}
|