Refactor preserved path guard

This commit is contained in:
Eva Wong
2026-04-24 10:07:38 -07:00
parent 1270a45a69
commit e8e3f6896e
6 changed files with 295 additions and 489 deletions

View File

@@ -3,7 +3,6 @@ mod pid_tracker;
#[cfg(target_os = "macos")]
mod seatbelt;
use std::path::Path;
use std::path::PathBuf;
use std::process::Stdio;
@@ -16,9 +15,7 @@ use codex_core::exec_env::create_env;
use codex_core::spawn::CODEX_SANDBOX_ENV_VAR;
use codex_core::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
use codex_protocol::config_types::SandboxMode;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::permissions::forbidden_agent_preserved_path_write;
use codex_sandboxing::landlock::create_linux_sandbox_command_args_for_policies;
#[cfg(target_os = "macos")]
use codex_sandboxing::seatbelt::CreateSeatbeltCommandArgsParams;
@@ -145,7 +142,7 @@ async fn run_command_under_sandbox(
// sandbox policy. In the future, we could add a CLI option to set them
// separately.
let sandbox_policy_cwd = cwd.clone();
if let Some(reason) = preserved_path_write_forbidden_reason(
if let Some(reason) = codex_shell_command::preserved_path_write_forbidden_reason(
&command,
cwd.as_path(),
&config.permissions.file_system_sandbox_policy,
@@ -289,126 +286,6 @@ async fn run_command_under_sandbox(
handle_exit_status(status);
}
fn preserved_path_write_forbidden_reason(
command: &[String],
cwd: &Path,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
) -> Option<String> {
let commands = codex_shell_command::bash::parse_shell_lc_plain_commands(command)
.or_else(|| codex_shell_command::bash::parse_shell_lc_command_word_prefixes(command))
.unwrap_or_else(|| vec![command.to_vec()]);
for simple_command in commands {
if let Some(name) =
simple_command_preserved_path_write(&simple_command, cwd, file_system_sandbox_policy)
{
return Some(preserved_path_write_reason(name));
}
}
if let Some(targets) =
codex_shell_command::bash::parse_shell_lc_write_redirection_targets(command)
{
for target in targets {
if let Some(name) = forbidden_agent_preserved_path_write(
Path::new(&target),
cwd,
file_system_sandbox_policy,
) {
return Some(preserved_path_write_reason(name));
}
}
}
None
}
fn preserved_path_write_reason(name: &str) -> String {
format!("command targets preserved workspace metadata path `{name}`")
}
fn simple_command_preserved_path_write(
command: &[String],
cwd: &Path,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
) -> Option<&'static str> {
let program = command.first().map(|program| {
Path::new(program)
.file_name()
.and_then(|name| name.to_str())
.unwrap_or(program)
})?;
match program {
"git" => git_init_preserved_path_write(command, cwd, file_system_sandbox_policy),
"touch" | "mkdir" | "rm" | "rmdir" | "ln" | "mv" | "cp" | "install" => command
.iter()
.skip(1)
.filter(|arg| !arg.starts_with('-'))
.find_map(|arg| {
forbidden_agent_preserved_path_write(
Path::new(arg),
cwd,
file_system_sandbox_policy,
)
}),
_ => None,
}
}
fn git_init_preserved_path_write(
command: &[String],
cwd: &Path,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
) -> Option<&'static str> {
let mut git_cwd = PathBuf::from(cwd);
let mut index = 1;
while index < command.len() {
match command[index].as_str() {
"-C" => {
let next = command.get(index + 1)?;
git_cwd = resolve_shell_operand(Path::new(next), &git_cwd);
index += 2;
}
"--" => {
index += 1;
break;
}
arg if arg.starts_with('-') => {
index += 1;
}
_ => break,
}
}
if command.get(index).map(String::as_str) != Some("init") {
return None;
}
let init_target = command
.iter()
.skip(index + 1)
.find(|arg| !arg.starts_with('-'))
.map_or_else(
|| git_cwd.clone(),
|arg| resolve_shell_operand(Path::new(arg), &git_cwd),
);
forbidden_agent_preserved_path_write(
init_target.join(".git").as_path(),
&git_cwd,
file_system_sandbox_policy,
)
}
fn resolve_shell_operand(path: &Path, cwd: &Path) -> PathBuf {
if path.is_absolute() {
path.to_path_buf()
} else {
cwd.join(path)
}
}
#[cfg(target_os = "windows")]
async fn run_command_under_windows_session(
config: &Config,
@@ -911,111 +788,4 @@ mod tests {
Ok(())
}
fn legacy_workspace_write_policy(cwd: &std::path::Path) -> FileSystemSandboxPolicy {
let policy = codex_protocol::protocol::SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
read_only_access: codex_protocol::protocol::ReadOnlyAccess::Restricted {
include_platform_defaults: false,
readable_roots: vec![],
},
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
FileSystemSandboxPolicy::from_legacy_sandbox_policy(&policy, cwd)
}
#[test]
fn debug_sandbox_preserved_path_guard_blocks_git_init_under_parent_repo() {
let repo = TempDir::new().expect("tempdir");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"git init".to_string(),
],
&cwd,
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
#[test]
fn debug_sandbox_preserved_path_guard_allows_normal_git_under_parent_repo() {
let repo = TempDir::new().expect("tempdir");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"git status --short".to_string(),
],
&cwd,
&policy,
);
assert_eq!(reason, None);
}
#[test]
fn debug_sandbox_preserved_path_guard_blocks_preserved_path_redirections() {
let repo = TempDir::new().expect("tempdir");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"printf pwned > .git".to_string(),
],
&cwd,
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
#[test]
fn debug_sandbox_preserved_path_guard_blocks_git_init_inside_complex_script() {
let repo = TempDir::new().expect("tempdir");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"set -e\nif git init -q; then\n exit 22\nfi".to_string(),
],
&cwd,
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
}

View File

@@ -1,11 +1,7 @@
use codex_protocol::ThreadId;
use codex_protocol::models::ShellCommandToolCallParams;
use codex_protocol::models::ShellToolCallParams;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::forbidden_agent_preserved_path_write;
use serde_json::Value as JsonValue;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use crate::exec::ExecCapturePolicy;
@@ -93,126 +89,6 @@ struct RunExecLikeArgs {
shell_runtime_backend: ShellRuntimeBackend,
}
fn preserved_path_write_forbidden_reason(
command: &[String],
cwd: &Path,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
) -> Option<String> {
let commands = codex_shell_command::bash::parse_shell_lc_plain_commands(command)
.or_else(|| codex_shell_command::bash::parse_shell_lc_command_word_prefixes(command))
.unwrap_or_else(|| vec![command.to_vec()]);
for simple_command in commands {
if let Some(name) =
simple_command_preserved_path_write(&simple_command, cwd, file_system_sandbox_policy)
{
return Some(preserved_path_write_reason(name));
}
}
if let Some(targets) =
codex_shell_command::bash::parse_shell_lc_write_redirection_targets(command)
{
for target in targets {
if let Some(name) = forbidden_agent_preserved_path_write(
Path::new(&target),
cwd,
file_system_sandbox_policy,
) {
return Some(preserved_path_write_reason(name));
}
}
}
None
}
fn preserved_path_write_reason(name: &str) -> String {
format!("command targets preserved workspace metadata path `{name}`")
}
fn simple_command_preserved_path_write(
command: &[String],
cwd: &Path,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
) -> Option<&'static str> {
let program = command.first().map(|program| {
Path::new(program)
.file_name()
.and_then(|name| name.to_str())
.unwrap_or(program)
})?;
match program {
"git" => git_init_preserved_path_write(command, cwd, file_system_sandbox_policy),
"touch" | "mkdir" | "rm" | "rmdir" | "ln" | "mv" | "cp" | "install" => command
.iter()
.skip(1)
.filter(|arg| !arg.starts_with('-'))
.find_map(|arg| {
forbidden_agent_preserved_path_write(
Path::new(arg),
cwd,
file_system_sandbox_policy,
)
}),
_ => None,
}
}
fn git_init_preserved_path_write(
command: &[String],
cwd: &Path,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
) -> Option<&'static str> {
let mut git_cwd = PathBuf::from(cwd);
let mut index = 1;
while index < command.len() {
match command[index].as_str() {
"-C" => {
let next = command.get(index + 1)?;
git_cwd = resolve_shell_operand(Path::new(next), &git_cwd);
index += 2;
}
"--" => {
index += 1;
break;
}
arg if arg.starts_with('-') => {
index += 1;
}
_ => break,
}
}
if command.get(index).map(String::as_str) != Some("init") {
return None;
}
let init_target = command
.iter()
.skip(index + 1)
.find(|arg| !arg.starts_with('-'))
.map_or_else(
|| git_cwd.clone(),
|arg| resolve_shell_operand(Path::new(arg), &git_cwd),
);
forbidden_agent_preserved_path_write(
init_target.join(".git").as_path(),
&git_cwd,
file_system_sandbox_policy,
)
}
fn resolve_shell_operand(path: &Path, cwd: &Path) -> PathBuf {
if path.is_absolute() {
path.to_path_buf()
} else {
cwd.join(path)
}
}
impl ShellHandler {
fn to_exec_params(
params: &ShellToolCallParams,
@@ -655,7 +531,7 @@ impl ShellHandler {
prefix_rule,
})
.await;
let exec_approval_requirement = preserved_path_write_forbidden_reason(
let exec_approval_requirement = codex_shell_command::preserved_path_write_forbidden_reason(
&exec_params.command,
&exec_params.cwd,
&turn.file_system_sandbox_policy,

View File

@@ -2,9 +2,6 @@ use std::path::PathBuf;
use std::sync::Arc;
use codex_protocol::models::ShellCommandToolCallParams;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::protocol::ReadOnlyAccess;
use codex_protocol::protocol::SandboxPolicy;
use core_test_support::PathBufExt;
use core_test_support::test_path_buf;
use pretty_assertions::assert_eq;
@@ -28,7 +25,6 @@ use codex_shell_command::is_safe_command::is_known_safe_command;
use codex_shell_command::powershell::try_find_powershell_executable_blocking;
use codex_shell_command::powershell::try_find_pwsh_executable_blocking;
use serde_json::json;
use tempfile::TempDir;
use tokio::sync::Mutex;
use tokio::sync::watch;
@@ -79,134 +75,6 @@ fn assert_safe(shell: &Shell, command: &str) {
)));
}
fn legacy_workspace_write_policy(cwd: &std::path::Path) -> FileSystemSandboxPolicy {
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
read_only_access: ReadOnlyAccess::Restricted {
include_platform_defaults: false,
readable_roots: vec![],
},
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
FileSystemSandboxPolicy::from_legacy_sandbox_policy(&policy, cwd)
}
#[test]
fn preserved_path_detector_blocks_git_init_under_parent_repo() {
let repo = TempDir::new().expect("tempdir");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = super::preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"git init".to_string(),
],
&cwd,
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
#[test]
fn preserved_path_detector_allows_normal_git_under_parent_repo() {
let repo = TempDir::new().expect("tempdir");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = super::preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"git status --short".to_string(),
],
&cwd,
&policy,
);
assert_eq!(reason, None);
}
#[test]
fn preserved_path_detector_blocks_direct_preserved_path_writes() {
let cwd = TempDir::new().expect("tempdir");
let policy = legacy_workspace_write_policy(cwd.path());
let reason = super::preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"touch .git && mkdir -p .codex".to_string(),
],
cwd.path(),
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
#[test]
fn preserved_path_detector_blocks_preserved_path_redirections() {
let repo = TempDir::new().expect("tempdir");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = super::preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"printf pwned > .git".to_string(),
],
&cwd,
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
#[test]
fn preserved_path_detector_blocks_git_init_inside_complex_script() {
let repo = TempDir::new().expect("tempdir");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = super::preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"set -e\nif git init -q; then\n exit 22\nfi".to_string(),
],
&cwd,
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
#[tokio::test]
async fn shell_command_handler_to_exec_params_uses_session_shell_and_turn_context() {
let (session, turn_context) = make_session_and_context().await;

View File

@@ -6,6 +6,8 @@ pub mod bash;
pub(crate) mod command_safety;
pub mod parse_command;
pub mod powershell;
mod preserved_path_write;
pub use command_safety::is_dangerous_command;
pub use command_safety::is_safe_command;
pub use preserved_path_write::preserved_path_write_forbidden_reason;

View File

@@ -0,0 +1,290 @@
use std::path::Path;
use std::path::PathBuf;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::forbidden_agent_preserved_path_write;
pub fn preserved_path_write_forbidden_reason(
command: &[String],
cwd: &Path,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
) -> Option<String> {
let commands = crate::bash::parse_shell_lc_plain_commands(command)
.or_else(|| crate::bash::parse_shell_lc_command_word_prefixes(command))
.unwrap_or_else(|| vec![command.to_vec()]);
for simple_command in commands {
if let Some(name) =
simple_command_preserved_path_write(&simple_command, cwd, file_system_sandbox_policy)
{
return Some(preserved_path_write_reason(name));
}
}
if let Some(targets) = crate::bash::parse_shell_lc_write_redirection_targets(command) {
for target in targets {
if let Some(name) = forbidden_agent_preserved_path_write(
Path::new(&target),
cwd,
file_system_sandbox_policy,
) {
return Some(preserved_path_write_reason(name));
}
}
}
None
}
fn preserved_path_write_reason(name: &str) -> String {
format!("command targets preserved workspace metadata path `{name}`")
}
fn simple_command_preserved_path_write(
command: &[String],
cwd: &Path,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
) -> Option<&'static str> {
let program = command.first().map(|program| {
Path::new(program)
.file_name()
.and_then(|name| name.to_str())
.unwrap_or(program)
})?;
match program {
"git" => git_init_preserved_path_write(command, cwd, file_system_sandbox_policy),
"touch" | "mkdir" | "rm" | "rmdir" | "ln" | "mv" | "cp" | "install" => command
.iter()
.skip(1)
.filter(|arg| !arg.starts_with('-'))
.find_map(|arg| {
forbidden_agent_preserved_path_write(
Path::new(arg),
cwd,
file_system_sandbox_policy,
)
}),
_ => None,
}
}
fn git_init_preserved_path_write(
command: &[String],
cwd: &Path,
file_system_sandbox_policy: &FileSystemSandboxPolicy,
) -> Option<&'static str> {
let mut git_cwd = PathBuf::from(cwd);
let mut index = 1;
while index < command.len() {
match command[index].as_str() {
"-C" => {
let next = command.get(index + 1)?;
git_cwd = resolve_shell_operand(Path::new(next), &git_cwd);
index += 2;
}
"--" => {
index += 1;
break;
}
arg if arg.starts_with('-') => {
index += 1;
}
_ => break,
}
}
if command.get(index).map(String::as_str) != Some("init") {
return None;
}
let init_target = command
.iter()
.skip(index + 1)
.find(|arg| !arg.starts_with('-'))
.map_or_else(
|| git_cwd.clone(),
|arg| resolve_shell_operand(Path::new(arg), &git_cwd),
);
forbidden_agent_preserved_path_write(
init_target.join(".git").as_path(),
&git_cwd,
file_system_sandbox_policy,
)
}
fn resolve_shell_operand(path: &Path, cwd: &Path) -> PathBuf {
if path.is_absolute() {
path.to_path_buf()
} else {
cwd.join(path)
}
}
#[cfg(test)]
mod tests {
use std::path::Path;
use std::path::PathBuf;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::protocol::ReadOnlyAccess;
use codex_protocol::protocol::SandboxPolicy;
use pretty_assertions::assert_eq;
use super::preserved_path_write_forbidden_reason;
struct TestDir {
path: PathBuf,
}
impl TestDir {
fn new(name: &str) -> Self {
let path = std::env::temp_dir().join(format!(
"codex-preserved-path-write-{name}-{}",
std::process::id()
));
let _ = std::fs::remove_dir_all(&path);
std::fs::create_dir(&path).expect("create tempdir");
Self { path }
}
fn path(&self) -> &Path {
&self.path
}
}
impl Drop for TestDir {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.path);
}
}
fn legacy_workspace_write_policy(cwd: &Path) -> FileSystemSandboxPolicy {
let policy = SandboxPolicy::WorkspaceWrite {
writable_roots: vec![],
read_only_access: ReadOnlyAccess::Restricted {
include_platform_defaults: false,
readable_roots: vec![],
},
network_access: false,
exclude_tmpdir_env_var: true,
exclude_slash_tmp: true,
};
FileSystemSandboxPolicy::from_legacy_sandbox_policy(&policy, cwd)
}
#[test]
fn preserved_path_detector_blocks_git_init_under_parent_repo() {
let repo = TestDir::new("git-init-under-parent-repo");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"git init".to_string(),
],
&cwd,
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
#[test]
fn preserved_path_detector_allows_normal_git_under_parent_repo() {
let repo = TestDir::new("normal-git-under-parent-repo");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"git status --short".to_string(),
],
&cwd,
&policy,
);
assert_eq!(reason, None);
}
#[test]
fn preserved_path_detector_blocks_direct_preserved_path_writes() {
let cwd = TestDir::new("direct-preserved-path-writes");
let policy = legacy_workspace_write_policy(cwd.path());
let reason = preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"touch .git && mkdir -p .codex".to_string(),
],
cwd.path(),
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
#[test]
fn preserved_path_detector_blocks_preserved_path_redirections() {
let repo = TestDir::new("preserved-path-redirections");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"printf pwned > .git".to_string(),
],
&cwd,
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
#[test]
fn preserved_path_detector_blocks_git_init_inside_complex_script() {
let repo = TestDir::new("git-init-inside-complex-script");
std::fs::create_dir(repo.path().join(".git")).expect("create parent .git");
let cwd = repo.path().join("sub");
std::fs::create_dir(&cwd).expect("create cwd");
let policy = legacy_workspace_write_policy(&cwd);
let reason = preserved_path_write_forbidden_reason(
&[
"/bin/bash".to_string(),
"-lc".to_string(),
"set -e\nif git init -q; then\n exit 22\nfi".to_string(),
],
&cwd,
&policy,
);
assert_eq!(
reason,
Some("command targets preserved workspace metadata path `.git`".to_string())
);
}
}

View File

@@ -1706,7 +1706,7 @@ mod tests {
&workspace_write,
)),
);
assert_eq!(sandbox, Some(workspace_write.clone().into()));
assert_eq!(sandbox, Some(workspace_write.into()));
assert_eq!(profile, None);
let external_sandbox = SandboxPolicy::ExternalSandbox {