diff --git a/codex-rs/exec-server/src/environment.rs b/codex-rs/exec-server/src/environment.rs index bc100b9422..e70d3e1db0 100644 --- a/codex-rs/exec-server/src/environment.rs +++ b/codex-rs/exec-server/src/environment.rs @@ -878,11 +878,9 @@ mod tests { #[tokio::test] async fn test_environment_rejects_sandboxed_filesystem_without_runtime_paths() { let environment = Environment::default_for_tests(); - let path = codex_utils_absolute_path::AbsolutePathBuf::from_absolute_path( - std::env::current_exe().expect("current exe").as_path(), - ) - .expect("absolute current exe"); - let path = codex_utils_path_uri::PathUri::from_abs_path(&path).expect("path URI"); + let path = + codex_utils_path_uri::PathUri::from_path(std::env::current_exe().expect("current exe")) + .expect("path URI"); let sandbox = crate::FileSystemSandboxContext::from_permission_profile( codex_protocol::models::PermissionProfile::from_runtime_permissions( &codex_protocol::permissions::FileSystemSandboxPolicy::restricted(Vec::new()), diff --git a/codex-rs/exec-server/src/fs_sandbox.rs b/codex-rs/exec-server/src/fs_sandbox.rs index 8ef20b2d1d..c720f8949c 100644 --- a/codex-rs/exec-server/src/fs_sandbox.rs +++ b/codex-rs/exec-server/src/fs_sandbox.rs @@ -15,6 +15,7 @@ use codex_sandboxing::SandboxTransformRequest; use codex_sandboxing::SandboxablePreference; use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::canonicalize_preserving_symlinks; +use codex_utils_path_uri::PathUri; use tokio::io::AsyncWriteExt; use tokio::process::Command; @@ -63,9 +64,10 @@ impl FileSystemSandboxRunner { let helper_read_roots = if sandbox.use_legacy_landlock { Vec::new() } else { - helper_read_roots(&self.runtime_paths) + helper_read_roots(&self.runtime_paths).map_err(io_error)? }; - add_helper_runtime_permissions(&mut file_system_policy, &helper_read_roots, cwd.as_path()); + add_helper_runtime_permissions(&mut file_system_policy, &helper_read_roots, cwd.as_path()) + .map_err(io_error)?; normalize_file_system_policy_root_aliases(&mut file_system_policy); let network_policy = NetworkSandboxPolicy::Restricted; let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement( @@ -84,7 +86,18 @@ impl FileSystemSandboxRunner { cwd: &AbsolutePathBuf, sandbox_context: &FileSystemSandboxContext, ) -> Result { - let helper = &self.runtime_paths.codex_self_exe; + let helper = self + .runtime_paths + .codex_self_exe + .to_abs_path() + .map_err(io_error)?; + let codex_linux_sandbox_exe = self + .runtime_paths + .codex_linux_sandbox_exe + .as_ref() + .map(PathUri::to_abs_path) + .transpose() + .map_err(io_error)?; let sandbox_manager = SandboxManager::new(); let (file_system_policy, network_policy) = permission_profile.to_runtime_permissions(); let sandbox = sandbox_manager.select_initial( @@ -109,7 +122,7 @@ impl FileSystemSandboxRunner { enforce_managed_network: false, network: None, sandbox_policy_cwd: cwd.as_path(), - codex_linux_sandbox_exe: self.runtime_paths.codex_linux_sandbox_exe.as_deref(), + codex_linux_sandbox_exe: codex_linux_sandbox_exe.as_deref(), use_legacy_landlock: sandbox_context.use_legacy_landlock, windows_sandbox_level: sandbox_context.windows_sandbox_level, windows_sandbox_private_desktop: sandbox_context.windows_sandbox_private_desktop, @@ -134,26 +147,28 @@ fn sandbox_cwd(sandbox: &FileSystemSandboxContext) -> Result Vec { +fn helper_read_roots(runtime_paths: &ExecServerRuntimePaths) -> std::io::Result> { let mut roots = Vec::new(); - for path in std::iter::once(runtime_paths.codex_self_exe.as_path()) - .chain(runtime_paths.codex_linux_sandbox_exe.as_deref()) + for path in std::iter::once(&runtime_paths.codex_self_exe) + .chain(runtime_paths.codex_linux_sandbox_exe.as_ref()) { - if let Some(parent) = path.parent() - && let Ok(root) = AbsolutePathBuf::from_absolute_path(parent) - && !roots.contains(&root) - { + let path = path.to_abs_path()?; + let Some(parent) = path.parent() else { + continue; + }; + let root = PathUri::from_path(parent)?; + if !roots.contains(&root) { roots.push(root); } } - roots + Ok(roots) } fn add_helper_runtime_permissions( file_system_policy: &mut FileSystemSandboxPolicy, - helper_read_roots: &[AbsolutePathBuf], + helper_read_roots: &[PathUri], cwd: &std::path::Path, -) { +) -> std::io::Result<()> { if !file_system_policy.has_full_disk_read_access() { let minimal_read_entry = FileSystemSandboxEntry { path: FileSystemPath::Special { @@ -167,6 +182,7 @@ fn add_helper_runtime_permissions( } for helper_read_root in helper_read_roots { + let helper_read_root = helper_read_root.to_abs_path()?; if file_system_policy.can_read_path_with_cwd(helper_read_root.as_path(), cwd) { continue; } @@ -178,6 +194,7 @@ fn add_helper_runtime_permissions( access: FileSystemAccessMode::Read, }); } + Ok(()) } fn normalize_file_system_policy_root_aliases(file_system_policy: &mut FileSystemSandboxPolicy) { @@ -345,7 +362,8 @@ mod tests { .expect("absolute cwd"); let mut policy = restricted_policy(Vec::new()); - add_helper_runtime_permissions(&mut policy, /*helper_read_roots*/ &[], cwd.as_path()); + add_helper_runtime_permissions(&mut policy, /*helper_read_roots*/ &[], cwd.as_path()) + .expect("helper permissions"); assert!(policy.include_platform_defaults()); } @@ -359,7 +377,8 @@ mod tests { FileSystemAccessMode::Write, )]); - add_helper_runtime_permissions(&mut policy, /*helper_read_roots*/ &[], cwd.as_path()); + add_helper_runtime_permissions(&mut policy, /*helper_read_roots*/ &[], cwd.as_path()) + .expect("helper permissions"); assert!(policy.include_platform_defaults()); } @@ -377,19 +396,19 @@ mod tests { writable.clone(), FileSystemAccessMode::Write, )]); - let readable = AbsolutePathBuf::from_absolute_path( - runtime_paths - .codex_self_exe - .parent() - .expect("current exe parent"), - ) - .expect("absolute readable path"); + let readable = runtime_paths + .codex_self_exe + .parent() + .expect("current exe parent") + .to_abs_path() + .expect("absolute readable path"); add_helper_runtime_permissions( &mut policy, - &helper_read_roots(&runtime_paths), + &helper_read_roots(&runtime_paths).expect("helper read roots"), cwd.as_path(), - ); + ) + .expect("helper permissions"); assert!(policy.can_read_path_with_cwd(readable.as_path(), cwd.as_path())); assert!(policy.can_write_path_with_cwd(writable.as_path(), cwd.as_path())); @@ -546,19 +565,19 @@ mod tests { let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir().as_path()) .expect("absolute cwd"); let mut policy = restricted_policy(Vec::new()); - let readable = AbsolutePathBuf::from_absolute_path( - runtime_paths - .codex_self_exe - .parent() - .expect("current exe parent"), - ) - .expect("absolute readable path"); + let readable = runtime_paths + .codex_self_exe + .parent() + .expect("current exe parent") + .to_abs_path() + .expect("absolute readable path"); add_helper_runtime_permissions( &mut policy, - &helper_read_roots(&runtime_paths), + &helper_read_roots(&runtime_paths).expect("helper read roots"), cwd.as_path(), - ); + ) + .expect("helper permissions"); assert!(policy.can_read_path_with_cwd(readable.as_path(), cwd.as_path())); } @@ -581,14 +600,32 @@ mod tests { add_helper_runtime_permissions( &mut policy, - &helper_read_roots(&runtime_paths), + &helper_read_roots(&runtime_paths).expect("helper read roots"), cwd.as_path(), - ); + ) + .expect("helper permissions"); assert!(policy.can_read_path_with_cwd(codex_parent.as_path(), cwd.as_path())); assert!(policy.can_read_path_with_cwd(alias_parent.as_path(), cwd.as_path())); } + #[cfg(windows)] + #[test] + fn helper_read_roots_preserve_windows_drive_roots() { + use codex_utils_path_uri::PathUri; + + let runtime_paths = ExecServerRuntimePaths::new( + std::path::PathBuf::from(r"C:\codex.exe"), + /*codex_linux_sandbox_exe*/ None, + ) + .expect("runtime paths"); + + assert_eq!( + helper_read_roots(&runtime_paths).expect("helper read roots"), + vec![PathUri::from_path(r"C:\").expect("drive root URI")], + ); + } + fn restricted_policy(entries: Vec) -> FileSystemSandboxPolicy { FileSystemSandboxPolicy::restricted(entries) } diff --git a/codex-rs/exec-server/src/local_file_system.rs b/codex-rs/exec-server/src/local_file_system.rs index ee38d7ff94..8d0431eb61 100644 --- a/codex-rs/exec-server/src/local_file_system.rs +++ b/codex-rs/exec-server/src/local_file_system.rs @@ -1,5 +1,4 @@ use async_trait::async_trait; -use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_path_uri::PathUri; use std::path::Path; use std::path::PathBuf; @@ -264,9 +263,8 @@ impl ExecutorFileSystem for DirectFileSystem { ) -> FileSystemResult { reject_sandbox_context(sandbox)?; let path = path.to_abs_path()?; - let canonicalized = - AbsolutePathBuf::from_absolute_path(tokio::fs::canonicalize(path.as_path()).await?)?; - PathUri::from_abs_path(&canonicalized) + let canonicalized = tokio::fs::canonicalize(path.as_path()).await?; + PathUri::from_path(canonicalized) } async fn read_file( diff --git a/codex-rs/exec-server/src/runtime_paths.rs b/codex-rs/exec-server/src/runtime_paths.rs index 1d3713b1c1..b941d7339d 100644 --- a/codex-rs/exec-server/src/runtime_paths.rs +++ b/codex-rs/exec-server/src/runtime_paths.rs @@ -1,15 +1,15 @@ use std::path::PathBuf; -use codex_utils_absolute_path::AbsolutePathBuf; +use codex_utils_path_uri::PathUri; /// Runtime paths needed by exec-server child processes. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ExecServerRuntimePaths { /// Stable path to the Codex executable used to launch hidden helper modes. - pub codex_self_exe: AbsolutePathBuf, + pub codex_self_exe: PathUri, /// Path to the Linux sandbox helper alias used when the platform sandbox /// needs to re-enter Codex by argv0. - pub codex_linux_sandbox_exe: Option, + pub codex_linux_sandbox_exe: Option, } impl ExecServerRuntimePaths { @@ -31,13 +31,10 @@ impl ExecServerRuntimePaths { codex_linux_sandbox_exe: Option, ) -> std::io::Result { Ok(Self { - codex_self_exe: absolute_path(codex_self_exe)?, - codex_linux_sandbox_exe: codex_linux_sandbox_exe.map(absolute_path).transpose()?, + codex_self_exe: PathUri::from_path(codex_self_exe)?, + codex_linux_sandbox_exe: codex_linux_sandbox_exe + .map(PathUri::from_path) + .transpose()?, }) } } - -fn absolute_path(path: PathBuf) -> std::io::Result { - AbsolutePathBuf::from_absolute_path(path.as_path()) - .map_err(|err| std::io::Error::new(std::io::ErrorKind::InvalidInput, err)) -}