diff --git a/codex-rs/core/src/tools/handlers/apply_patch.rs b/codex-rs/core/src/tools/handlers/apply_patch.rs index edb94fb4f0..8bdaea059d 100644 --- a/codex-rs/core/src/tools/handlers/apply_patch.rs +++ b/codex-rs/core/src/tools/handlers/apply_patch.rs @@ -68,7 +68,11 @@ fn to_abs_path(cwd: &Path, path: &Path) -> Option { AbsolutePathBuf::resolve_path_against_base(path, cwd).ok() } -fn write_permissions_for_paths(file_paths: &[AbsolutePathBuf]) -> Option { +fn write_permissions_for_paths( + file_paths: &[AbsolutePathBuf], + file_system_sandbox_policy: &codex_protocol::permissions::FileSystemSandboxPolicy, + cwd: &Path, +) -> Option { let write_paths = file_paths .iter() .map(|path| { @@ -76,6 +80,7 @@ fn write_permissions_for_paths(file_paths: &[AbsolutePathBuf]) -> Option>() .into_iter() .map(AbsolutePathBuf::from_absolute_path) @@ -107,16 +112,16 @@ async fn effective_patch_permissions( session.granted_session_permissions().await.as_ref(), session.granted_turn_permissions().await.as_ref(), ); - let effective_additional_permissions = apply_granted_turn_permissions( - session, - crate::sandboxing::SandboxPermissions::UseDefault, - write_permissions_for_paths(&file_paths), - ) - .await; let file_system_sandbox_policy = effective_file_system_sandbox_policy( &turn.file_system_sandbox_policy, granted_permissions.as_ref(), ); + let effective_additional_permissions = apply_granted_turn_permissions( + session, + crate::sandboxing::SandboxPermissions::UseDefault, + write_permissions_for_paths(&file_paths, &file_system_sandbox_policy, turn.cwd.as_path()), + ) + .await; ( file_paths, diff --git a/codex-rs/core/src/tools/handlers/apply_patch_tests.rs b/codex-rs/core/src/tools/handlers/apply_patch_tests.rs index 7f8e8df8b5..86ae586929 100644 --- a/codex-rs/core/src/tools/handlers/apply_patch_tests.rs +++ b/codex-rs/core/src/tools/handlers/apply_patch_tests.rs @@ -1,5 +1,7 @@ use super::*; use codex_apply_patch::MaybeApplyPatchVerified; +use codex_protocol::permissions::FileSystemSandboxPolicy; +use codex_protocol::protocol::SandboxPolicy; use pretty_assertions::assert_eq; use tempfile::TempDir; @@ -26,3 +28,53 @@ fn approval_keys_include_move_destination() { let keys = file_paths_for_action(&action); assert_eq!(keys.len(), 2); } + +#[test] +fn write_permissions_for_paths_skip_dirs_already_writable_under_workspace_root() { + let tmp = TempDir::new().expect("tmp"); + let cwd = tmp.path(); + let nested = cwd.join("nested"); + std::fs::create_dir_all(&nested).expect("create nested dir"); + let file_path = AbsolutePathBuf::try_from(nested.join("file.txt")) + .expect("nested file path should be absolute"); + let sandbox_policy = FileSystemSandboxPolicy::from(&SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + read_only_access: Default::default(), + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: false, + }); + + let permissions = write_permissions_for_paths(&[file_path], &sandbox_policy, cwd); + + assert_eq!(permissions, None); +} + +#[test] +fn write_permissions_for_paths_keep_dirs_outside_workspace_root() { + let tmp = TempDir::new().expect("tmp"); + let cwd = tmp.path().join("workspace"); + let outside = tmp.path().join("outside"); + std::fs::create_dir_all(&cwd).expect("create cwd"); + std::fs::create_dir_all(&outside).expect("create outside dir"); + let file_path = AbsolutePathBuf::try_from(outside.join("file.txt")) + .expect("outside file path should be absolute"); + let sandbox_policy = FileSystemSandboxPolicy::from(&SandboxPolicy::WorkspaceWrite { + writable_roots: vec![], + read_only_access: Default::default(), + network_access: false, + exclude_tmpdir_env_var: true, + exclude_slash_tmp: true, + }); + + let permissions = write_permissions_for_paths(&[file_path], &sandbox_policy, &cwd); + let expected_outside = AbsolutePathBuf::from_absolute_path(dunce::simplified( + &outside.canonicalize().expect("canonicalize outside dir"), + )) + .expect("outside dir should be absolute"); + + assert_eq!( + permissions.and_then(|profile| profile.file_system.and_then(|fs| fs.write)), + Some(vec![expected_outside]) + ); +}