use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use crate::orchestration::{
pop_execution_policy, push_execution_policy, CapabilityPolicy, ExternalRoot, SandboxProfile,
ToolApprovalPolicy,
};
use crate::stdlib::sandbox::child_write::{child_write_disposition, ChildWriteDisposition};
use crate::tool_annotations::{SideEffectLevel, ToolAnnotations, ToolArgSchema, ToolKind};
fn confined_policy(
profile: SandboxProfile,
workspace: &Path,
read_only: &[&Path],
) -> CapabilityPolicy {
let mut annotations = BTreeMap::new();
annotations.insert(
"run".to_string(),
ToolAnnotations {
kind: ToolKind::Execute,
side_effect_level: SideEffectLevel::ProcessExec,
arg_schema: ToolArgSchema {
path_params: vec!["path".to_string()],
..Default::default()
},
..Default::default()
},
);
CapabilityPolicy {
capabilities: BTreeMap::from([
("process".to_string(), vec!["run".to_string()]),
(
"workspace".to_string(),
["read_text", "write_text", "list", "exists"]
.map(str::to_string)
.to_vec(),
),
]),
workspace_roots: vec![workspace.display().to_string()],
read_only_roots: read_only
.iter()
.map(|root| root.display().to_string())
.collect(),
side_effect_level: Some("process_exec".to_string()),
tool_annotations: annotations,
sandbox_profile: profile,
..Default::default()
}
}
fn run_admitted(policy: &CapabilityPolicy, root: &Path, path: &Path) -> bool {
crate::stdlib::sandbox::child_write::install_child_write_probe();
let approval = ToolApprovalPolicy {
external_roots: vec![ExternalRoot::read(root.display().to_string())],
..Default::default()
};
push_execution_policy(policy.clone());
let decision = approval.evaluate_detailed(
"run",
&serde_json::json!({ "path": path.display().to_string() }),
);
pop_execution_policy();
decision.is_allow()
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
fn shell_succeeds(policy: &CapabilityPolicy, script: &str) -> bool {
push_execution_policy(policy.clone());
let output =
crate::stdlib::sandbox::std_command_for("/bin/sh", &["-c".to_string(), script.to_string()])
.and_then(|mut command| {
command
.output()
.map_err(|error| crate::value::VmError::Runtime(error.to_string()))
});
pop_execution_policy();
output.is_ok_and(|output| output.status.success())
}
fn canonical(path: &Path) -> PathBuf {
std::fs::canonicalize(path).unwrap()
}
fn workspace_and_outside() -> Option<(tempfile::TempDir, tempfile::TempDir)> {
let cwd = std::env::current_dir().ok()?;
let home = std::env::var_os("HOME").map(PathBuf::from)?;
if !home.is_dir() || home.starts_with("/tmp") || home.starts_with("/private/tmp") {
return None;
}
Some((
tempfile::tempdir_in(&cwd).ok()?,
tempfile::tempdir_in(home).ok()?,
))
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[test]
fn a_command_under_an_enforced_read_only_root_is_admitted_and_cannot_write() {
if !crate::stdlib::sandbox::active_backend_filesystem_available() {
return;
}
let Some((workspace, outside)) = workspace_and_outside() else {
return;
};
let root = canonical(outside.path());
std::fs::create_dir_all(root.join("sub")).unwrap();
std::fs::write(root.join("data.txt"), "reference").unwrap();
let ws = canonical(workspace.path());
std::os::unix::fs::symlink(&root, ws.join("link")).unwrap();
let policy = confined_policy(SandboxProfile::OsHardened, &ws, &[root.as_path()]);
let file = root.join("data.txt");
assert_eq!(
child_write_disposition(&policy, &file),
ChildWriteDisposition::EnforcedReadOnly
);
assert!(
run_admitted(&policy, &root, &file),
"the boundary must admit the read"
);
assert!(
shell_succeeds(&policy, &format!("cat '{}'", file.display())),
"control: the child can read the root"
);
for (label, target) in [
("direct", root.join("f")),
("dot-dot", root.join("sub").join("..").join("f")),
("workspace symlink", ws.join("link").join("f")),
] {
assert!(
!shell_succeeds(&policy, &format!("echo x > '{}'", target.display())),
"{label}: the kernel must refuse a write into the read-only root"
);
assert!(!root.join("f").exists(), "{label}: nothing may be written");
}
assert_eq!(std::fs::read_to_string(&file).unwrap(), "reference");
assert!(shell_succeeds(
&policy,
&format!("echo x > '{}'", ws.join("ok.txt").display())
));
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[test]
fn a_read_only_root_nested_in_the_workspace_is_judged_by_its_backend() {
if !crate::stdlib::sandbox::active_backend_filesystem_available() {
return;
}
let Some((workspace, _outside)) = workspace_and_outside() else {
return;
};
let ws = canonical(workspace.path());
let nested = ws.join("vendor");
std::fs::create_dir_all(&nested).unwrap();
let policy = confined_policy(SandboxProfile::OsHardened, &ws, &[nested.as_path()]);
let target = nested.join("f");
let write = format!("echo x > '{}'", target.display());
#[cfg(target_os = "macos")]
{
assert_eq!(
child_write_disposition(&policy, &target),
ChildWriteDisposition::EnforcedReadOnly
);
assert!(!shell_succeeds(&policy, &write));
assert!(!target.exists());
}
#[cfg(target_os = "linux")]
{
assert_eq!(
child_write_disposition(&policy, &target),
ChildWriteDisposition::Writable
);
assert!(
shell_succeeds(&policy, &write) && target.exists(),
"the oracle said writable, so the write must really land"
);
}
}
#[test]
fn only_os_hardened_can_admit() {
let Some((workspace, outside)) = workspace_and_outside() else {
return;
};
let root = canonical(outside.path());
let file = root.join("data.txt");
std::fs::write(&file, "reference").unwrap();
let ws = canonical(workspace.path());
for profile in [SandboxProfile::Unrestricted, SandboxProfile::Worktree] {
let policy = confined_policy(profile, &ws, &[root.as_path()]);
assert_eq!(
child_write_disposition(&policy, &file),
ChildWriteDisposition::Unknown,
"{profile:?}"
);
assert!(!run_admitted(&policy, &root, &file), "{profile:?}");
}
}
#[test]
fn a_command_with_no_active_policy_is_refused() {
let Some((_workspace, outside)) = workspace_and_outside() else {
return;
};
let root = canonical(outside.path());
let file = root.join("data.txt");
let approval = ToolApprovalPolicy {
external_roots: vec![ExternalRoot::read(root.display().to_string())],
..Default::default()
};
let decision = approval.evaluate_detailed(
"run",
&serde_json::json!({ "path": file.display().to_string() }),
);
assert!(!decision.is_allow(), "{}", decision.reason);
}
#[cfg(windows)]
#[test]
fn windows_never_admits() {
let workspace = tempfile::tempdir().unwrap();
let outside = tempfile::tempdir().unwrap();
let root = canonical(outside.path());
let file = root.join("data.txt");
std::fs::write(&file, "reference").unwrap();
let policy = confined_policy(
SandboxProfile::OsHardened,
&canonical(workspace.path()),
&[root.as_path()],
);
assert_eq!(
child_write_disposition(&policy, &file),
ChildWriteDisposition::Unknown
);
assert!(!run_admitted(&policy, &root, &file));
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[test]
fn a_session_mount_reaches_the_child_profile_by_its_mode() {
use crate::workspace_anchor::{MountMode, WorkspaceAnchor};
if !crate::stdlib::sandbox::active_backend_filesystem_available() {
eprintln!("MOUNT-TEST-SKIPPED: no filesystem confinement backend");
return;
}
let Some(home) = std::env::var_os("HOME").map(PathBuf::from) else {
return;
};
let (Ok(workspace), Ok(outside), Ok(sibling)) = (
tempfile::tempdir_in(&home),
tempfile::tempdir_in(&home),
tempfile::tempdir_in(&home),
) else {
eprintln!("MOUNT-TEST-SKIPPED: no writable home directory");
return;
};
crate::reset_thread_local_state();
let ws = canonical(workspace.path());
let root = canonical(outside.path());
let other = canonical(sibling.path());
std::fs::write(root.join("data.txt"), "reference").unwrap();
std::fs::write(other.join("data.txt"), "other").unwrap();
let mut policy = confined_policy(SandboxProfile::OsHardened, &ws, &[]);
policy.workspace_roots = vec![ws.display().to_string()];
let session = crate::agent_sessions::open_or_create_for_test(Some("mount-child".to_string()));
let _current = crate::agent_sessions::enter_current_session(session.clone());
let anchor = WorkspaceAnchor {
primary: ws,
additional_roots: Vec::new(),
anchored_at: "0".to_string(),
};
crate::agent_sessions::set_workspace_anchor(&session, Some(anchor)).unwrap();
let read = format!("cat '{}'", root.join("data.txt").display());
let write = format!("echo x > '{}'", root.join("f").display());
assert!(!shell_succeeds(&policy, &read), "unmounted: not readable");
let root_text = root.display().to_string();
crate::stdlib::add_workspace_root(&session, &root_text, Some(MountMode::ReadOnly), None, None)
.unwrap();
assert!(shell_succeeds(&policy, &read), "read_only mount reads");
assert!(
!shell_succeeds(&policy, &write),
"read_only mount never writes"
);
assert!(!root.join("f").exists());
assert!(
!shell_succeeds(
&policy,
&format!("cat '{}'", other.join("data.txt").display())
),
"a different outside path stays unreadable"
);
crate::stdlib::add_workspace_root(&session, &root_text, Some(MountMode::Extend), None, None)
.unwrap();
assert!(shell_succeeds(&policy, &write), "extend mount writes");
assert!(root.join("f").exists());
assert!(
!shell_succeeds(
&policy,
&format!("echo x > '{}'", other.join("f").display())
),
"a write outside the mount stays refused"
);
eprintln!("MOUNT-TEST-RAN");
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[test]
fn a_read_only_mount_nested_in_the_workspace_matches_its_backend() {
use crate::workspace_anchor::{MountMode, WorkspaceAnchor};
if !crate::stdlib::sandbox::active_backend_filesystem_available() {
eprintln!("NESTED-MOUNT-TEST-SKIPPED: no filesystem confinement backend");
return;
}
let Some(home) = std::env::var_os("HOME").map(PathBuf::from) else {
return;
};
let Ok(workspace) = tempfile::tempdir_in(&home) else {
return;
};
crate::reset_thread_local_state();
let ws = canonical(workspace.path());
let nested = ws.join("vendor");
std::fs::create_dir_all(&nested).unwrap();
let policy = confined_policy(SandboxProfile::OsHardened, &ws, &[]);
let session = crate::agent_sessions::open_or_create_for_test(Some("nested-child".to_string()));
let _current = crate::agent_sessions::enter_current_session(session.clone());
let anchor = WorkspaceAnchor {
primary: ws.clone(),
additional_roots: Vec::new(),
anchored_at: "0".to_string(),
};
crate::agent_sessions::set_workspace_anchor(&session, Some(anchor)).unwrap();
let beside = format!("echo x > '{}'", ws.join("ok.txt").display());
let inside = format!("echo x > '{}'", nested.join("f").display());
assert!(
shell_succeeds(&policy, &inside),
"control: writable before the mount"
);
std::fs::remove_file(nested.join("f")).unwrap();
let mounted = crate::stdlib::add_workspace_root(
&session,
&nested.to_string_lossy(),
Some(MountMode::ReadOnly),
None,
Some(&policy),
);
assert!(
shell_succeeds(&policy, &beside),
"the rest of the workspace stays writable"
);
let landed = shell_succeeds(&policy, &inside) && nested.join("f").exists();
#[cfg(target_os = "macos")]
{
mounted.unwrap();
assert!(
!landed,
"macOS must refuse a write into the nested read-only mount"
);
}
#[cfg(target_os = "linux")]
{
assert!(
mounted.is_err_and(|error| error.contains("cannot subtract write access")),
"an unenforceable nested read-only mount must be refused"
);
assert!(
child_write_disposition(&policy, &nested.join("f")) == ChildWriteDisposition::Writable,
"the refusal rests on a measured Writable disposition"
);
assert!(
landed,
"control: the child really could write the nested path"
);
}
eprintln!("NESTED-MOUNT-TEST-RAN landed={landed}");
}
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[test]
fn a_read_only_parent_of_a_writable_root_leaves_that_root_writable() {
use crate::workspace_anchor::{MountMode, WorkspaceAnchor};
if !crate::stdlib::sandbox::active_backend_filesystem_available() {
eprintln!("PARENT-MOUNT-TEST-SKIPPED: no filesystem confinement backend");
return;
}
let Some(home) = std::env::var_os("HOME").map(PathBuf::from) else {
return;
};
let Ok(parent_dir) = tempfile::tempdir_in(&home) else {
return;
};
crate::reset_thread_local_state();
let parent = canonical(parent_dir.path());
let ws = parent.join("ws");
let work = parent.join("work");
std::fs::create_dir_all(&ws).unwrap();
std::fs::create_dir_all(&work).unwrap();
std::fs::write(parent.join("data.txt"), "reference").unwrap();
let policy = confined_policy(SandboxProfile::OsHardened, &ws, &[]);
let session = crate::agent_sessions::open_or_create_for_test(Some("parent-mount".to_string()));
let _current = crate::agent_sessions::enter_current_session(session.clone());
let anchor = WorkspaceAnchor {
primary: ws.clone(),
additional_roots: Vec::new(),
anchored_at: "0".to_string(),
};
crate::agent_sessions::set_workspace_anchor(&session, Some(anchor)).unwrap();
crate::stdlib::add_workspace_root(
&session,
&parent.to_string_lossy(),
Some(MountMode::ReadOnly),
None,
Some(&policy),
)
.expect("a read-only parent of the writable workspace mounts");
crate::stdlib::add_workspace_root(
&session,
&work.to_string_lossy(),
Some(MountMode::Extend),
None,
Some(&policy),
)
.expect("a writable mount inside a read-only one mounts");
let in_ws = format!("echo x > '{}'", ws.join("f").display());
let in_work = format!("echo x > '{}'", work.join("f").display());
let in_parent = format!("echo x > '{}'", parent.join("f").display());
let read_parent = format!("cat '{}'", parent.join("data.txt").display());
assert!(
shell_succeeds(&policy, &in_ws),
"the workspace stays writable"
);
assert!(
shell_succeeds(&policy, &in_work),
"a writable mount inside stays writable"
);
assert!(
!shell_succeeds(&policy, &in_parent) && !parent.join("f").exists(),
"a write in the read-only parent outside both is refused"
);
assert!(
shell_succeeds(&policy, &read_parent),
"the parent stays readable"
);
use crate::stdlib::sandbox::{check_fs_path_scope, FsAccess};
push_execution_policy(policy);
let writable = check_fs_path_scope(&ws.join("g"), FsAccess::Write).is_ok()
&& check_fs_path_scope(&work.join("g"), FsAccess::Write).is_ok();
let refused = check_fs_path_scope(&parent.join("g"), FsAccess::Write).is_err();
let readable = check_fs_path_scope(&parent.join("data.txt"), FsAccess::Read).is_ok();
pop_execution_policy();
assert!(writable, "file builtins write the deeper writable roots");
assert!(
refused,
"file builtins refuse a write in the read-only parent"
);
assert!(readable, "file builtins read the read-only parent");
}