#![allow(dead_code)]
pub mod backend;
pub mod opensandbox;
pub mod policy;
pub mod process_hardening;
#[cfg(target_os = "macos")]
pub mod seatbelt;
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
pub mod seccomp;
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
pub mod bwrap;
#[cfg(target_os = "windows")]
pub mod windows;
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::Duration;
pub use policy::SandboxPolicy;
#[allow(dead_code)] pub const PUBLIC_SANDBOX_BACKENDS: &[&str] = &[
"seatbelt (macOS, when available)",
"bubblewrap (Linux, opt-in when installed)",
];
#[derive(Debug, Clone)]
pub struct CommandSpec {
pub program: String,
pub args: Vec<String>,
pub cwd: PathBuf,
pub env: HashMap<String, String>,
pub timeout: Duration,
pub sandbox_policy: SandboxPolicy,
pub justification: Option<String>,
pub requested_command: Option<String>,
}
impl CommandSpec {
pub fn shell(command: &str, cwd: PathBuf, timeout: Duration) -> Self {
let dispatcher = crate::shell_dispatcher::global_dispatcher();
#[cfg(windows)]
let (program, args) = {
let kind = dispatcher.kind();
let cmd = if matches!(
kind,
crate::shell_dispatcher::ShellKind::Pwsh
| crate::shell_dispatcher::ShellKind::WindowsPowerShell
) {
format!("[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; {command}")
} else if matches!(kind, crate::shell_dispatcher::ShellKind::Cmd) {
format!("chcp 65001 >NUL & {command}")
} else {
command.to_string()
};
dispatcher.build_command_parts(&cmd)
};
#[cfg(not(windows))]
let (program, args) = dispatcher.build_command_parts(command);
let env = {
#[cfg(windows)]
{
windows_shell_default_env()
}
#[cfg(not(windows))]
{
HashMap::new()
}
};
Self {
program,
args,
cwd,
env,
timeout,
sandbox_policy: SandboxPolicy::default(),
justification: None,
requested_command: Some(command.to_string()),
}
}
pub fn program(program: &str, args: Vec<String>, cwd: PathBuf, timeout: Duration) -> Self {
Self {
program: program.to_string(),
args,
cwd,
env: HashMap::new(),
timeout,
sandbox_policy: SandboxPolicy::default(),
justification: None,
requested_command: None,
}
}
pub fn with_policy(mut self, policy: SandboxPolicy) -> Self {
self.sandbox_policy = policy;
self
}
pub fn with_env(mut self, env: HashMap<String, String>) -> Self {
self.env = env;
self
}
pub fn with_env_var(mut self, key: &str, value: &str) -> Self {
self.env.insert(key.to_string(), value.to_string());
self
}
pub fn with_justification(mut self, justification: &str) -> Self {
self.justification = Some(justification.to_string());
self
}
pub fn display_command(&self) -> String {
if let Some(requested) = &self.requested_command {
return requested.clone();
}
if self.args.len() == 2
&& self.args[0] == "-c"
&& matches!(
self.program.as_str(),
"sh" | "bash" | "/bin/sh" | "/bin/bash" | "/usr/bin/sh" | "/usr/bin/bash"
)
{
self.args[1].clone()
} else if self.args.len() == 2
&& self.args[0] == "-c"
&& !self.program.eq_ignore_ascii_case("cmd")
&& !self.program.eq_ignore_ascii_case("pwsh")
&& !self.program.eq_ignore_ascii_case("pwsh.exe")
&& !self.program.eq_ignore_ascii_case("powershell")
&& !self.program.eq_ignore_ascii_case("powershell.exe")
{
self.args[1].clone()
} else if self.program.eq_ignore_ascii_case("cmd")
&& self.args.len() == 2
&& self.args[0].eq_ignore_ascii_case("/C")
{
let raw = &self.args[1];
raw.strip_prefix("chcp 65001 >NUL & ")
.unwrap_or(raw)
.to_string()
} else if {
let program = self.program.to_ascii_lowercase();
program == "pwsh"
|| program == "pwsh.exe"
|| program == "powershell"
|| program == "powershell.exe"
} && self.args.len() >= 3
&& self.args[0].eq_ignore_ascii_case("-NoProfile")
&& self.args[1].eq_ignore_ascii_case("-Command")
{
let raw = &self.args[2];
raw.strip_prefix("[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ")
.unwrap_or(raw)
.to_string()
} else {
let mut parts = vec![self.program.clone()];
parts.extend(self.args.clone());
parts.join(" ")
}
}
}
fn windows_shell_default_env() -> HashMap<String, String> {
HashMap::from([("PYTHONIOENCODING".to_string(), "utf-8".to_string())])
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SandboxType {
#[default]
None,
#[cfg(target_os = "macos")]
MacosSeatbelt,
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
LinuxBubblewrap,
#[cfg(target_os = "windows")]
Windows,
}
impl std::fmt::Display for SandboxType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SandboxType::None => write!(f, "none"),
#[cfg(target_os = "macos")]
SandboxType::MacosSeatbelt => write!(f, "macos-seatbelt"),
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
SandboxType::LinuxBubblewrap => write!(f, "linux-bwrap"),
#[cfg(target_os = "windows")]
SandboxType::Windows => write!(f, "windows-sandbox"),
}
}
}
#[derive(Debug)]
pub struct ExecEnv {
pub command: Vec<String>,
pub cwd: PathBuf,
pub env: HashMap<String, String>,
pub timeout: Duration,
pub sandbox_type: SandboxType,
pub policy: SandboxPolicy,
}
impl ExecEnv {
pub fn program(&self) -> &str {
self.command
.first()
.map_or("sh", std::string::String::as_str)
}
pub fn args(&self) -> &[String] {
if self.command.len() > 1 {
&self.command[1..]
} else {
&[]
}
}
pub fn is_sandboxed(&self) -> bool {
!matches!(self.sandbox_type, SandboxType::None)
}
}
pub fn get_platform_sandbox() -> Option<SandboxType> {
get_platform_sandbox_with_bwrap_preference(false)
}
pub fn get_platform_sandbox_with_bwrap_preference(prefer_bwrap: bool) -> Option<SandboxType> {
#[cfg(target_os = "macos")]
{
if seatbelt::is_available() {
return Some(SandboxType::MacosSeatbelt);
}
}
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
{
if prefer_bwrap && bwrap::is_available() {
return Some(SandboxType::LinuxBubblewrap);
}
}
#[cfg(not(all(target_os = "linux", not(target_env = "ohos"))))]
let _ = prefer_bwrap;
#[cfg(target_os = "windows")]
{
if windows::is_available() {
return Some(SandboxType::Windows);
}
}
None
}
pub fn is_sandbox_available() -> bool {
get_platform_sandbox().is_some()
}
#[derive(Debug, Default)]
pub struct SandboxManager {
sandbox_available: Option<bool>,
#[allow(dead_code)]
forced_sandbox: Option<SandboxType>,
prefer_bwrap: bool,
}
impl SandboxManager {
pub fn new() -> Self {
Self::default()
}
pub fn with_bwrap_preference(prefer_bwrap: bool) -> Self {
Self {
prefer_bwrap,
..Self::default()
}
}
pub fn set_prefer_bwrap(&mut self, prefer: bool) {
self.prefer_bwrap = prefer;
self.sandbox_available = None;
}
pub fn is_available(&mut self) -> bool {
if let Some(available) = self.sandbox_available {
return available;
}
let available = self.configured_sandbox().is_some();
self.sandbox_available = Some(available);
available
}
pub fn configured_sandbox(&self) -> Option<SandboxType> {
get_platform_sandbox_with_bwrap_preference(self.prefer_bwrap)
}
pub fn select_sandbox(&self, policy: &SandboxPolicy) -> SandboxType {
if !policy.should_sandbox() {
return SandboxType::None;
}
if let Some(forced) = self.forced_sandbox {
return forced;
}
self.configured_sandbox().unwrap_or(SandboxType::None)
}
pub fn prepare(&self, spec: &CommandSpec) -> ExecEnv {
let sandbox_type = self.select_sandbox(&spec.sandbox_policy);
match sandbox_type {
SandboxType::None => Self::prepare_unsandboxed(spec),
#[cfg(target_os = "macos")]
SandboxType::MacosSeatbelt => Self::prepare_seatbelt(spec),
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
SandboxType::LinuxBubblewrap => Self::prepare_bwrap(spec),
#[cfg(target_os = "windows")]
SandboxType::Windows => Self::prepare_windows(spec),
}
}
fn prepare_unsandboxed(spec: &CommandSpec) -> ExecEnv {
let mut command = vec![spec.program.clone()];
command.extend(spec.args.clone());
ExecEnv {
command,
cwd: spec.cwd.clone(),
env: spec.env.clone(),
timeout: spec.timeout,
sandbox_type: SandboxType::None,
policy: spec.sandbox_policy.clone(),
}
}
#[cfg(target_os = "macos")]
fn prepare_seatbelt(spec: &CommandSpec) -> ExecEnv {
let mut original_command = vec![spec.program.clone()];
original_command.extend(spec.args.clone());
let seatbelt_args =
seatbelt::create_seatbelt_args(original_command, &spec.sandbox_policy, &spec.cwd);
let mut command = vec![seatbelt::SANDBOX_EXEC_PATH.to_string()];
command.extend(seatbelt_args);
let mut env = spec.env.clone();
env.insert("DEEPSEEK_SANDBOX".to_string(), "seatbelt".to_string());
ExecEnv {
command,
cwd: spec.cwd.clone(),
env,
timeout: spec.timeout,
sandbox_type: SandboxType::MacosSeatbelt,
policy: spec.sandbox_policy.clone(),
}
}
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
fn prepare_bwrap(spec: &CommandSpec) -> ExecEnv {
let writable_roots = spec.sandbox_policy.get_writable_roots(&spec.cwd);
let command = bwrap::build_bwrap_command(
&spec.cwd,
&spec.program,
&spec.args,
&writable_roots,
spec.sandbox_policy.has_network_access(),
);
let mut env = spec.env.clone();
env.insert("DEEPSEEK_SANDBOX".to_string(), "bwrap".to_string());
ExecEnv {
command,
cwd: spec.cwd.clone(),
env,
timeout: spec.timeout,
sandbox_type: SandboxType::LinuxBubblewrap,
policy: spec.sandbox_policy.clone(),
}
}
#[cfg(target_os = "windows")]
fn prepare_windows(spec: &CommandSpec) -> ExecEnv {
let mut command = vec![spec.program.clone()];
command.extend(spec.args.clone());
let mut env = spec.env.clone();
let kind = windows::select_best_kind(&spec.sandbox_policy, &spec.cwd);
env.insert("DEEPSEEK_SANDBOX".to_string(), format!("windows:{kind}"));
if !spec.sandbox_policy.has_network_access() {
env.insert(
"DEEPSEEK_SANDBOX_BLOCK_NETWORK".to_string(),
"1".to_string(),
);
}
ExecEnv {
command,
cwd: spec.cwd.clone(),
env,
timeout: spec.timeout,
sandbox_type: SandboxType::Windows,
policy: spec.sandbox_policy.clone(),
}
}
pub fn was_denied(sandbox_type: SandboxType, exit_code: i32, stderr: &str) -> bool {
#[cfg(not(any(
target_os = "macos",
all(target_os = "linux", not(target_env = "ohos"))
)))]
let _ = (exit_code, stderr);
match sandbox_type {
SandboxType::None => false,
#[cfg(target_os = "macos")]
SandboxType::MacosSeatbelt => seatbelt::detect_denial(exit_code, stderr),
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
SandboxType::LinuxBubblewrap => bwrap::detect_denial(exit_code, stderr),
#[cfg(target_os = "windows")]
SandboxType::Windows => windows::detect_denial(exit_code, stderr),
}
}
pub fn denial_message(sandbox_type: SandboxType, stderr: &str) -> String {
#[cfg(not(any(
target_os = "macos",
all(target_os = "linux", not(target_env = "ohos"))
)))]
let _ = stderr;
match sandbox_type {
SandboxType::None => "Command failed (no sandbox)".to_string(),
#[cfg(target_os = "macos")]
SandboxType::MacosSeatbelt => {
if stderr.contains("file-write") {
"Sandbox blocked write access. The command tried to write to a protected location.".to_string()
} else if stderr.contains("network") {
"Sandbox blocked network access. Enable network_access in sandbox policy if needed.".to_string()
} else {
format!(
"Sandbox blocked operation: {}",
stderr.lines().next().unwrap_or("unknown")
)
}
}
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
SandboxType::LinuxBubblewrap => {
if let Some(error) = stderr
.lines()
.map(str::trim_start)
.find(|line| line.starts_with("bwrap:"))
{
format!("Bubblewrap could not create the sandbox: {}", error)
} else if stderr.contains("Read-only file system") {
"Bubblewrap blocked access outside the writable workspace view.".to_string()
} else {
format!(
"Bubblewrap blocked operation: {}",
stderr.lines().next().unwrap_or("unknown")
)
}
}
#[cfg(target_os = "windows")]
SandboxType::Windows => {
if stderr.contains("Access is denied") {
"Windows sandbox blocked access. The command lacked required privileges."
.to_string()
} else if stderr.contains("network") {
"Windows sandbox blocked network access. Enable network_access in policy if needed."
.to_string()
} else {
format!(
"Windows sandbox blocked operation: {}",
stderr.lines().next().unwrap_or("unknown")
)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_command_spec_shell() {
let spec = CommandSpec::shell("echo hello", PathBuf::from("/tmp"), Duration::from_secs(30));
assert!(!spec.program.is_empty(), "program must not be empty");
assert!(!spec.args.is_empty(), "args must not be empty");
assert_eq!(spec.display_command(), "echo hello");
}
#[test]
fn test_command_spec_shell_custom_posix_path_display() {
let spec = CommandSpec {
program: "/bin/zsh".to_string(),
args: vec!["-c".to_string(), "echo hello".to_string()],
cwd: PathBuf::from("/tmp"),
env: HashMap::new(),
timeout: Duration::from_secs(30),
sandbox_policy: SandboxPolicy::default(),
justification: None,
requested_command: None,
};
assert_eq!(spec.display_command(), "echo hello");
}
#[test]
fn test_command_spec_shell_quoted_arg_not_split() {
let cmd = r#"git commit -m "feat: complete sub-pages""#;
let spec = CommandSpec::shell(cmd, PathBuf::from("/tmp"), Duration::from_secs(30));
let dispatcher = crate::shell_dispatcher::global_dispatcher();
assert_eq!(spec.program, dispatcher.kind().binary());
let carriers: Vec<&String> = spec
.args
.iter()
.filter(|arg| arg.contains(r#""feat: complete sub-pages""#))
.collect();
assert_eq!(carriers.len(), 1, "args: {:?}", spec.args);
assert!(
!spec
.args
.iter()
.any(|arg| arg == "feat:" || arg == "complete" || arg == "sub-pages\""),
"args: {:?}",
spec.args
);
assert_eq!(spec.display_command(), cmd);
}
#[test]
fn test_command_spec_program() {
let spec = CommandSpec::program(
"cargo",
vec!["build".to_string(), "--release".to_string()],
PathBuf::from("/project"),
Duration::from_secs(300),
);
assert_eq!(spec.program, "cargo");
assert_eq!(spec.display_command(), "cargo build --release");
}
#[test]
fn test_command_spec_builder() {
let spec = CommandSpec::shell("test", PathBuf::from("."), Duration::from_secs(10))
.with_policy(SandboxPolicy::ReadOnly)
.with_env_var("FOO", "bar")
.with_justification("Testing");
assert!(matches!(spec.sandbox_policy, SandboxPolicy::ReadOnly));
assert_eq!(spec.env.get("FOO"), Some(&"bar".to_string()));
assert_eq!(spec.justification, Some("Testing".to_string()));
}
#[test]
fn windows_shell_default_env_forces_python_pipe_stdio_utf8() {
let env = windows_shell_default_env();
assert_eq!(
env.get("PYTHONIOENCODING").map(String::as_str),
Some("utf-8")
);
}
#[test]
fn test_sandbox_manager_new() {
let manager = SandboxManager::new();
assert!(manager.sandbox_available.is_none());
}
#[test]
fn test_sandbox_manager_select_sandbox() {
let manager = SandboxManager::new();
let no_sandbox = manager.select_sandbox(&SandboxPolicy::DangerFullAccess);
assert_eq!(no_sandbox, SandboxType::None);
let external = manager.select_sandbox(&SandboxPolicy::ExternalSandbox {
network_access: true,
});
assert_eq!(external, SandboxType::None);
}
#[test]
fn test_prepare_unsandboxed() {
let manager = SandboxManager::new();
let spec = CommandSpec::shell("echo test", PathBuf::from("/tmp"), Duration::from_secs(30))
.with_policy(SandboxPolicy::DangerFullAccess);
let env = manager.prepare(&spec);
assert_eq!(env.sandbox_type, SandboxType::None);
let mut expected = vec![spec.program.clone()];
expected.extend(spec.args.iter().cloned());
assert_eq!(env.command, expected);
assert!(!env.is_sandboxed());
}
#[test]
fn test_exec_env_helpers() {
let env = ExecEnv {
command: vec![
"sandbox-exec".to_string(),
"-p".to_string(),
"policy".to_string(),
"--".to_string(),
"echo".to_string(),
"hello".to_string(),
],
cwd: PathBuf::from("/tmp"),
env: HashMap::new(),
timeout: Duration::from_secs(30),
sandbox_type: SandboxType::None,
policy: SandboxPolicy::default(),
};
assert_eq!(env.program(), "sandbox-exec");
assert_eq!(env.args().len(), 5);
}
#[test]
fn test_sandbox_type_display() {
assert_eq!(format!("{}", SandboxType::None), "none");
#[cfg(target_os = "macos")]
assert_eq!(format!("{}", SandboxType::MacosSeatbelt), "macos-seatbelt");
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
assert_eq!(format!("{}", SandboxType::LinuxBubblewrap), "linux-bwrap");
}
#[test]
fn test_parity_platform_sandbox_detection() {
let sandbox_type = get_platform_sandbox();
let available = is_sandbox_available();
if available {
assert!(sandbox_type.is_some());
}
}
#[test]
#[cfg(target_os = "macos")]
fn test_parity_macos_seatbelt_available() {
match get_platform_sandbox() {
Some(SandboxType::MacosSeatbelt) => {}
None => {
eprintln!("skipping: MacosSeatbelt unavailable via get_platform_sandbox()");
}
Some(other) => panic!("unexpected macOS sandbox type: {other:?}"),
}
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
fn linux_default_never_claims_an_unwired_sandbox() {
assert_eq!(get_platform_sandbox(), None);
assert_eq!(get_platform_sandbox_with_bwrap_preference(false), None);
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
fn linux_bwrap_selection_requires_opt_in_and_executable() {
let expected = bwrap::is_available().then_some(SandboxType::LinuxBubblewrap);
assert_eq!(get_platform_sandbox_with_bwrap_preference(true), expected);
let manager = SandboxManager::with_bwrap_preference(true);
let selected = manager.select_sandbox(&SandboxPolicy::default());
assert_eq!(selected, expected.unwrap_or(SandboxType::None));
}
#[test]
fn test_parity_denial_zero_exit_never_denied() {
assert!(!SandboxManager::was_denied(
SandboxType::None,
0,
"anything"
));
#[cfg(target_os = "macos")]
assert!(!SandboxManager::was_denied(
SandboxType::MacosSeatbelt,
0,
""
));
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
assert!(!SandboxManager::was_denied(
SandboxType::LinuxBubblewrap,
0,
""
));
#[cfg(target_os = "windows")]
assert!(!SandboxManager::was_denied(SandboxType::Windows, 0, ""));
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
fn bwrap_denial_is_not_inferred_from_seccomp_text() {
assert!(!SandboxManager::was_denied(
SandboxType::LinuxBubblewrap,
1,
"Bad system call"
));
assert!(SandboxManager::was_denied(
SandboxType::LinuxBubblewrap,
1,
"Read-only file system"
));
}
#[test]
#[cfg(target_os = "macos")]
fn test_parity_seatbelt_file_write_detected() {
assert!(SandboxManager::was_denied(
SandboxType::MacosSeatbelt,
1,
"Sandbox: ls denied file-write*"
));
assert!(SandboxManager::was_denied(
SandboxType::MacosSeatbelt,
1,
"Operation not permitted"
));
}
#[test]
fn test_parity_manager_default_no_bwrap() {
let manager = SandboxManager::default();
let spec = CommandSpec::shell("true", PathBuf::from("/tmp"), Duration::from_secs(5))
.with_policy(SandboxPolicy::default());
let env = manager.prepare(&spec);
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
{
let marker = env.env.get("DEEPSEEK_SANDBOX");
assert!(marker.is_none());
assert_eq!(env.sandbox_type, SandboxType::None);
}
let _ = env;
}
#[test]
fn test_parity_manager_with_bwrap() {
let manager = SandboxManager::with_bwrap_preference(true);
let spec = CommandSpec::shell("true", PathBuf::from("/tmp"), Duration::from_secs(5))
.with_policy(SandboxPolicy::default());
let env = manager.prepare(&spec);
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
{
if crate::sandbox::bwrap::is_available() {
let marker = env.env.get("DEEPSEEK_SANDBOX");
assert_eq!(marker.map(String::as_str), Some("bwrap"));
assert_eq!(env.sandbox_type, SandboxType::LinuxBubblewrap);
assert_eq!(env.program(), bwrap::BWRAP_PATH);
} else {
assert_eq!(env.sandbox_type, SandboxType::None);
assert!(!env.env.contains_key("DEEPSEEK_SANDBOX"));
}
}
let _ = env;
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
fn bwrap_read_only_policy_keeps_the_working_directory_read_only() {
let manager = SandboxManager {
forced_sandbox: Some(SandboxType::LinuxBubblewrap),
..SandboxManager::default()
};
let spec = CommandSpec::shell("true", PathBuf::from("/tmp"), Duration::from_secs(5))
.with_policy(SandboxPolicy::ReadOnly);
let env = manager.prepare(&spec);
assert_eq!(env.sandbox_type, SandboxType::LinuxBubblewrap);
assert!(!env.command.iter().any(|arg| arg == "--bind"));
assert!(!env.command.iter().any(|arg| arg == "--share-net"));
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
fn bwrap_workspace_policy_maps_additional_roots_and_network_access() {
let dir = tempfile::tempdir().expect("tempdir");
let workspace = dir.path().join("workspace");
let extra = dir.path().join("extra");
std::fs::create_dir_all(&workspace).expect("workspace");
std::fs::create_dir_all(&extra).expect("extra");
let manager = SandboxManager {
forced_sandbox: Some(SandboxType::LinuxBubblewrap),
..SandboxManager::default()
};
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![extra.clone()],
network_access: true,
exclude_tmpdir: true,
exclude_slash_tmp: true,
};
let spec = CommandSpec::shell("true", workspace.clone(), Duration::from_secs(5))
.with_policy(policy);
let env = manager.prepare(&spec);
for root in [workspace, extra] {
let root = root
.canonicalize()
.expect("writable root")
.to_string_lossy()
.into_owned();
assert!(env.command.windows(3).any(|args| args[0] == "--bind"
&& args[1].as_str() == root.as_str()
&& args[2].as_str() == root.as_str()));
}
assert!(env.command.iter().any(|arg| arg == "--share-net"));
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
fn full_access_and_external_policies_bypass_forced_bwrap() {
let manager = SandboxManager {
forced_sandbox: Some(SandboxType::LinuxBubblewrap),
..SandboxManager::default()
};
for policy in [
SandboxPolicy::DangerFullAccess,
SandboxPolicy::ExternalSandbox {
network_access: false,
},
] {
let spec = CommandSpec::shell("true", PathBuf::from("/tmp"), Duration::from_secs(5))
.with_policy(policy);
let env = manager.prepare(&spec);
assert_eq!(env.sandbox_type, SandboxType::None);
assert_ne!(env.program(), bwrap::BWRAP_PATH);
}
}
#[test]
fn test_parity_exec_env_for_all_policies() {
let manager = SandboxManager::new();
let policies = [
SandboxPolicy::DangerFullAccess,
SandboxPolicy::ReadOnly,
SandboxPolicy::workspace_with_network(),
SandboxPolicy::default(),
];
for policy in &policies {
let spec = CommandSpec::shell("true", PathBuf::from("/tmp"), Duration::from_secs(5))
.with_policy(policy.clone());
let env = manager.prepare(&spec);
assert_eq!(env.policy, *policy);
}
}
}