From afec29ecbc00cc57d6bf9f26ea177410650f565b Mon Sep 17 00:00:00 2001 From: Chris Bookholt Date: Mon, 11 May 2026 20:06:14 +0000 Subject: [PATCH] core: tighten unified exec sandbox setup Refine unified exec sandbox initialization while preserving the requested process workdir. Co-authored-by: Codex --- .../core/src/tools/runtimes/unified_exec.rs | 38 +++++++++++++++++-- .../core/src/unified_exec/process_manager.rs | 4 +- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/codex-rs/core/src/tools/runtimes/unified_exec.rs b/codex-rs/core/src/tools/runtimes/unified_exec.rs index 5bc6687c37..b3633a6dde 100644 --- a/codex-rs/core/src/tools/runtimes/unified_exec.rs +++ b/codex-rs/core/src/tools/runtimes/unified_exec.rs @@ -216,10 +216,6 @@ impl Approvable for UnifiedExecRuntime<'_> { } impl<'a> ToolRuntime for UnifiedExecRuntime<'a> { - fn sandbox_cwd<'b>(&self, req: &'b UnifiedExecRequest) -> Option<&'b AbsolutePathBuf> { - Some(&req.cwd) - } - fn network_approval_spec( &self, req: &UnifiedExecRequest, @@ -361,7 +357,10 @@ impl<'a> ToolRuntime for UnifiedExecRunt mod tests { use super::*; use crate::exec::DEFAULT_EXEC_COMMAND_TIMEOUT_MS; + use crate::tools::sandboxing::ToolRuntime; + use codex_exec_server::Environment; use std::time::Duration; + use tempfile::tempdir; #[test] fn unified_exec_options_combines_default_timeout_with_network_denial_cancellation() { @@ -384,4 +383,35 @@ mod tests { other => panic!("expected timeout-or-cancellation expiration, got {other:?}"), } } + + #[tokio::test] + async fn unified_exec_keeps_sandbox_policy_anchored_to_turn_cwd() { + let dir = tempdir().expect("create temp dir"); + let cwd = AbsolutePathBuf::try_from(dir.path().to_path_buf()).expect("absolute temp dir"); + let manager = UnifiedExecProcessManager::default(); + let runtime = UnifiedExecRuntime::new(&manager, UnifiedExecShellMode::Direct); + let request = UnifiedExecRequest { + command: vec!["pwd".to_string()], + hook_command: "pwd".to_string(), + process_id: 1000, + cwd, + environment: Arc::new(Environment::default_for_tests()), + env: HashMap::new(), + exec_server_env_config: None, + explicit_env_overrides: HashMap::new(), + network: None, + tty: false, + sandbox_permissions: SandboxPermissions::UseDefault, + additional_permissions: None, + #[cfg(unix)] + additional_permissions_preapproved: false, + justification: None, + exec_approval_requirement: ExecApprovalRequirement::Skip { + bypass_sandbox: false, + proposed_execpolicy_amendment: None, + }, + }; + + assert_eq!(runtime.sandbox_cwd(&request), None); + } } diff --git a/codex-rs/core/src/unified_exec/process_manager.rs b/codex-rs/core/src/unified_exec/process_manager.rs index 73afca2da8..6c8cdc0224 100644 --- a/codex-rs/core/src/unified_exec/process_manager.rs +++ b/codex-rs/core/src/unified_exec/process_manager.rs @@ -1010,7 +1010,9 @@ impl UnifiedExecProcessManager { approval_policy: context.turn.approval_policy.value(), permission_profile: context.turn.permission_profile(), file_system_sandbox_policy: &file_system_sandbox_policy, - sandbox_cwd: cwd.as_path(), + // The process cwd may be model-controlled, but policy resolution + // must stay anchored to the trusted turn cwd. + sandbox_cwd: context.turn.cwd.as_path(), sandbox_permissions: if request.additional_permissions_preapproved { crate::sandboxing::SandboxPermissions::UseDefault } else {