use std::path::Path;
use super::{run_capped, Backend, RunSpec, Sandbox, SandboxOutcome};
use crate::error::Result;
pub struct MacosSandbox;
impl Sandbox for MacosSandbox {
async fn run(&self, spec: RunSpec<'_>) -> Result<SandboxOutcome> {
let profile = profile_for(spec.workdir, spec.allow_network);
let mut wrapped: Vec<String> = vec!["sandbox-exec".into(), "-p".into(), profile];
wrapped.extend(spec.argv.iter().cloned());
let workdir = spec.workdir.to_path_buf();
let wspec = RunSpec {
argv: &wrapped,
workdir: spec.workdir,
limits: spec.limits,
allow_network: spec.allow_network,
};
run_capped(Backend::MacosSandboxExec, wspec, move |cmd| {
cmd.env("TMPDIR", &workdir);
})
.await
}
fn backend(&self) -> Backend {
Backend::MacosSandboxExec
}
}
pub(crate) fn profile_for(workdir: &Path, allow_network: bool) -> String {
let wd = workdir.display();
let net = if allow_network {
"(allow network*)"
} else {
"(deny network*)"
};
format!(
"(version 1)\n\
(allow default)\n\
{net}\n\
(deny file-write* (subpath \"/\"))\n\
(allow file-write* (subpath \"{wd}\"))\n\
(allow file-write* (literal \"/dev/null\") (literal \"/dev/dtracehelper\"))\n\
(allow file-write* (subpath \"/private/var/folders\"))\n"
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn profile_denies_network_by_default_and_confines_writes() {
let p = profile_for(Path::new("/tmp/sbx"), false);
assert!(p.contains("(deny network*)"));
assert!(p.contains("(allow file-write* (subpath \"/tmp/sbx\"))"));
assert!(p.contains("(deny file-write* (subpath \"/\"))"));
}
#[test]
fn profile_allows_network_when_asked() {
let p = profile_for(Path::new("/tmp/sbx"), true);
assert!(p.contains("(allow network*)"));
}
}