use std::path::{Path, PathBuf};
use std::process::Command;
use crate::orchestration::{CapabilityPolicy, SandboxProfile};
use crate::stdlib::sandbox::child_write::{child_write_disposition, ChildWriteDisposition};
use crate::stdlib::sandbox::{ProcessCommandConfig, SandboxBackend};
const CHILD_ENV: &str = "CHILD_WRITE_NESTED_CHILD";
const WORKSPACE_ENV: &str = "CHILD_WRITE_NESTED_WORKSPACE";
const ROOT_ENV: &str = "CHILD_WRITE_NESTED_ROOT";
const RESULT_PREFIX: &str = "CHILD_WRITE_NESTED_RESULT ";
fn policy(workspace: &Path, root: &Path) -> CapabilityPolicy {
CapabilityPolicy {
sandbox_profile: SandboxProfile::OsHardened,
workspace_roots: vec![workspace.display().to_string()],
read_only_roots: vec![root.display().to_string()],
..CapabilityPolicy::default()
}
}
fn run_child() {
let workspace = PathBuf::from(std::env::var(WORKSPACE_ENV).expect("workspace"));
let root = PathBuf::from(std::env::var(ROOT_ENV).expect("root"));
let policy = policy(&workspace, &root);
let target = root.join("f");
let disposition = child_write_disposition(&policy, &target);
let config = ProcessCommandConfig {
cwd: Some(workspace),
..ProcessCommandConfig::default()
};
let spawned = super::Backend::run_to_output(
"/bin/sh",
&["-c".to_string(), format!("echo x > '{}'", target.display())],
&config,
&policy,
SandboxProfile::OsHardened,
)
.is_ok();
let report = serde_json::json!({
"disposition": format!("{disposition:?}"),
"missing": format!("{:?}", child_write_disposition(&policy, &root.join("missing"))),
"nesting": format!("{:?}", super::nested::nesting(&policy)),
"spawned": spawned,
});
println!("{RESULT_PREFIX}{report}");
}
fn run_nested(test: &str, extra: impl FnOnce(&Path) -> String) -> (serde_json::Value, bool) {
let workspace = tempfile::tempdir().expect("workspace");
let exe = std::env::current_exe().expect("test binary");
let fixture_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../.build/test-fixtures");
std::fs::create_dir_all(&fixture_root).expect("build fixture root");
let outside = tempfile::tempdir_in(&fixture_root).expect("root");
let workspace_path = workspace.path().canonicalize().unwrap();
let root = outside.path().canonicalize().unwrap();
std::fs::write(root.join("f"), "reference").expect("seed the target");
let build_root = exe.ancestors().nth(3).unwrap_or(&exe).to_path_buf();
let profile = format!(
"{}(allow file-read* (subpath \"{}\"))\n{}",
super::render_profile_for_program(&policy(&workspace_path, &root), "/bin/sh"),
build_root.display(),
extra(&root)
);
let output = Command::new("/usr/bin/sandbox-exec")
.arg("-p")
.arg(&profile)
.arg(&exe)
.args([test, "--exact", "--nocapture", "--test-threads=1"])
.env(CHILD_ENV, "1")
.env(WORKSPACE_ENV, &workspace_path)
.env(ROOT_ENV, &root)
.output()
.expect("run the test binary under the outer profile");
let stdout = String::from_utf8_lossy(&output.stdout);
let report = stdout
.lines()
.find_map(|line| line.split_once(RESULT_PREFIX).map(|(_, json)| json))
.map(|json| serde_json::from_str(json).expect("child report"))
.unwrap_or_else(|| {
panic!(
"the child printed no report; status={:?}\nstdout={stdout}\nstderr={}",
output.status,
String::from_utf8_lossy(&output.stderr)
)
});
let written = std::fs::read_to_string(root.join("f")).unwrap_or_default() != "reference";
(report, written)
}
#[test]
fn an_inherited_profile_that_denies_the_root_is_read_only() {
if std::env::var_os(CHILD_ENV).is_some() {
return run_child();
}
if !Path::new("/usr/bin/sandbox-exec").exists() {
return;
}
let (report, written) = run_nested(
"stdlib::sandbox::macos::child_write_nested_tests::an_inherited_profile_that_denies_the_root_is_read_only",
|_| String::new(),
);
assert_eq!(
report["disposition"],
format!("{:?}", ChildWriteDisposition::EnforcedReadOnly),
"{report}"
);
assert_eq!(report["nesting"], "Inherit", "{report}");
assert!(!written, "{report}");
assert_eq!(
report["missing"],
format!("{:?}", ChildWriteDisposition::Unknown),
"{report}"
);
}
#[test]
fn an_inherited_profile_that_allows_the_root_is_unknown() {
if std::env::var_os(CHILD_ENV).is_some() {
return run_child();
}
if !Path::new("/usr/bin/sandbox-exec").exists() {
return;
}
let (report, written) = run_nested(
"stdlib::sandbox::macos::child_write_nested_tests::an_inherited_profile_that_allows_the_root_is_unknown",
|root| format!("(allow file-write* (subpath \"{}\"))\n", root.display()),
);
assert_eq!(
report["disposition"],
format!("{:?}", ChildWriteDisposition::Unknown),
"{report}"
);
assert!(
report["spawned"] == true && written,
"the inherited profile allows the write, so it must land: {report}"
);
}