diff --git a/codex-rs/windows-sandbox-rs/src/command_resolution_tests.rs b/codex-rs/windows-sandbox-rs/src/command_resolution_tests.rs index 2f71188a75..43f8d1a421 100644 --- a/codex-rs/windows-sandbox-rs/src/command_resolution_tests.rs +++ b/codex-rs/windows-sandbox-rs/src/command_resolution_tests.rs @@ -58,6 +58,29 @@ fn requested_cwd_precedes_child_path() { ); } +#[test] +fn keeps_long_and_lexical_paths_unchanged() { + let temp = TempDir::new().expect("tempdir"); + let mut cwd = temp.path().to_path_buf(); + while cwd.to_string_lossy().len() <= 270 { + cwd.push("long-working-directory-segment"); + } + fs::create_dir_all(cwd.join("nested")).expect("create long cwd"); + let executable = cwd.join("tool.EXE"); + fs::write(&executable, b"fixture").expect("write executable fixture"); + let lexical = cwd.join("nested").join("..").join("tool.EXE"); + + assert_eq!( + resolve_windows_executable( + &[lexical.to_string_lossy().into_owned()], + &cwd, + &HashMap::new(), + ) + .expect("resolve long lexical executable"), + lexical + ); +} + #[test] fn keeps_extended_length_paths_unchanged() { let executable = dunce::canonicalize(std::env::current_exe().expect("current executable")) diff --git a/codex-rs/windows-sandbox-rs/src/conpty/mod.rs b/codex-rs/windows-sandbox-rs/src/conpty/mod.rs index 0b940518f7..48f3827468 100644 --- a/codex-rs/windows-sandbox-rs/src/conpty/mod.rs +++ b/codex-rs/windows-sandbox-rs/src/conpty/mod.rs @@ -12,7 +12,6 @@ use crate::process::WindowsProcessLaunch; use crate::winutil::format_last_error; use crate::winutil::quote_windows_arg; use crate::winutil::to_wide; -use crate::winutil::to_win32_path_wide; use anyhow::Context; use anyhow::Result; use codex_utils_pty::PsuedoCon; @@ -144,7 +143,7 @@ pub fn spawn_conpty_process_as_user( 0, EXTENDED_STARTUPINFO_PRESENT | CREATE_UNICODE_ENVIRONMENT, env_block.as_ptr() as *mut c_void, - to_win32_path_wide(cwd).as_ptr(), + to_wide(cwd).as_ptr(), &si.StartupInfo, &mut pi, ) diff --git a/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs b/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs index b3bbab4f80..40ea5e9e12 100644 --- a/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs +++ b/codex-rs/windows-sandbox-rs/src/elevated/runner_client.rs @@ -16,7 +16,6 @@ use crate::runner_pipe::find_runner_exe; use crate::runner_pipe::pipe_pair; use crate::winutil::quote_windows_arg; use crate::winutil::to_wide; -use crate::winutil::to_win32_path_wide; use anyhow::Context; use anyhow::Result; use std::ffi::c_void; @@ -343,7 +342,7 @@ pub(crate) fn spawn_runner_transport( ); let mut cmdline_vec = to_wide(&runner_full_cmd); let exe_w = to_wide(&runner_cmdline); - let cwd_w = to_win32_path_wide(cwd); + let cwd_w = to_wide(cwd); let user_w = to_wide(&sandbox_creds.username); let domain_w = to_wide("."); let password_w = to_wide(&sandbox_creds.password); diff --git a/codex-rs/windows-sandbox-rs/src/process.rs b/codex-rs/windows-sandbox-rs/src/process.rs index 53ae4dc475..9d9566cb5b 100644 --- a/codex-rs/windows-sandbox-rs/src/process.rs +++ b/codex-rs/windows-sandbox-rs/src/process.rs @@ -4,7 +4,6 @@ use crate::proc_thread_attr::ProcThreadAttributeList; use crate::winutil::argv_to_command_line; use crate::winutil::format_last_error; use crate::winutil::to_wide; -use crate::winutil::to_win32_path_wide; use anyhow::Context; use anyhow::Result; use anyhow::anyhow; @@ -118,7 +117,7 @@ pub(crate) unsafe fn create_process_as_user( let env_block = make_env_block(env_map); let desktop = LaunchDesktop::prepare(use_private_desktop, logs_base_dir)?; let mut pi: PROCESS_INFORMATION = std::mem::zeroed(); - let cwd_wide = to_win32_path_wide(cwd); + let cwd_wide = to_wide(cwd); let env_block_len = env_block.len(); match stdio { Some((stdin_h, stdout_h, stderr_h)) => { diff --git a/codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs b/codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs index 2ed9c0146c..3d286effa2 100644 --- a/codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs +++ b/codex-rs/windows-sandbox-rs/src/unified_exec/tests.rs @@ -250,7 +250,7 @@ fn legacy_non_tty_uses_explicit_application_path() { } #[test] -fn legacy_resolves_request_pathext_in_a_long_working_directory() { +fn legacy_resolves_request_pathext() { let _guard = legacy_process_test_guard(); let runtime = current_thread_runtime(); runtime.block_on(async move { @@ -259,12 +259,9 @@ fn legacy_resolves_request_pathext_in_a_long_working_directory() { fs::create_dir_all(&tools).expect("create tools directory"); let executable = tools.join("path-only-tool.BIN"); fs::copy(r"C:\Windows\System32\cmd.exe", &executable).expect("copy executable fixture"); - let mut cwd = fixture.path().join("workspace"); - while cwd.to_string_lossy().len() <= 270 { - cwd.push("long-working-directory-segment"); - } - fs::create_dir_all(&cwd).expect("create long working directory"); - let codex_home = sandbox_home("legacy-long-cwd-resolution"); + let cwd = fixture.path().join("workspace"); + fs::create_dir_all(&cwd).expect("create working directory"); + let codex_home = sandbox_home("legacy-request-pathext-resolution"); let permission_profile = PermissionProfile::workspace_write(); let env_map = HashMap::from([ ("Path".to_string(), tools.to_string_lossy().into_owned()), @@ -278,7 +275,7 @@ fn legacy_resolves_request_pathext_in_a_long_working_directory() { vec![ "path-only-tool".to_string(), "/c".to_string(), - "echo REQUEST-PATHEXT-LONG-CWD".to_string(), + "echo REQUEST-PATHEXT".to_string(), ], cwd.as_path(), env_map, @@ -296,10 +293,7 @@ fn legacy_resolves_request_pathext_in_a_long_working_directory() { collect_stdout_and_exit(spawned, codex_home.path(), Duration::from_secs(10)).await; let stdout = String::from_utf8_lossy(&stdout); assert_eq!(exit_code, 0, "stdout={stdout:?}"); - assert!( - stdout.contains("REQUEST-PATHEXT-LONG-CWD"), - "stdout={stdout:?}" - ); + assert!(stdout.contains("REQUEST-PATHEXT"), "stdout={stdout:?}"); }); } diff --git a/codex-rs/windows-sandbox-rs/src/winutil.rs b/codex-rs/windows-sandbox-rs/src/winutil.rs index 71ee4ea662..45378b4bb5 100644 --- a/codex-rs/windows-sandbox-rs/src/winutil.rs +++ b/codex-rs/windows-sandbox-rs/src/winutil.rs @@ -1,7 +1,6 @@ use anyhow::Result; use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; -use std::path::Path; use windows_sys::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER; use windows_sys::Win32::Foundation::GetLastError; use windows_sys::Win32::Foundation::HLOCAL; @@ -23,32 +22,6 @@ pub fn to_wide>(s: S) -> Vec { v } -const MAX_PATH_UTF16_UNITS: usize = 260; -const EXTENDED_PATH_PREFIX: &[u16] = &[b'\\' as u16, b'\\' as u16, b'?' as u16, b'\\' as u16]; -const DEVICE_PATH_PREFIX: &[u16] = &[b'\\' as u16, b'\\' as u16, b'.' as u16, b'\\' as u16]; - -/// Encodes a CreateProcess path, adding an extended-length prefix only when needed. -pub(crate) fn to_win32_path_wide(path: &Path) -> Vec { - let wide = path.as_os_str().encode_wide().collect::>(); - if !path.is_absolute() - || wide.len() < MAX_PATH_UTF16_UNITS - || wide.starts_with(EXTENDED_PATH_PREFIX) - || wide.starts_with(DEVICE_PATH_PREFIX) - { - return wide.into_iter().chain([0]).collect(); - } - - let mut extended = EXTENDED_PATH_PREFIX.to_vec(); - if wide.starts_with(&[b'\\' as u16, b'\\' as u16]) { - extended.extend("UNC\\".encode_utf16()); - extended.extend_from_slice(&wide[2..]); - } else { - extended.extend(wide); - } - extended.push(0); - extended -} - /// Quote a single Windows command-line argument following the rules used by /// CommandLineToArgvW/CRT so that spaces, quotes, and backslashes are preserved. /// Reference behavior matches Rust std::process::Command on Windows. @@ -235,10 +208,7 @@ fn sid_bytes_from_string(sid_str: &str) -> Result> { #[cfg(test)] mod tests { use super::argv_to_command_line; - use super::to_wide; - use super::to_win32_path_wide; use pretty_assertions::assert_eq; - use std::path::Path; #[test] fn argv_to_command_line_quotes_each_argument_independently() { @@ -268,23 +238,4 @@ mod tests { "pwsh.exe -Command \"Write-Output \\\"hello world\\\"\"" ); } - - #[test] - fn create_process_paths_add_extended_prefixes_only_for_long_paths() { - let long_tail = ["long-working-directory-segment"; 10].join(r"\"); - let long_drive = format!(r"C:\{long_tail}"); - let long_unc = format!(r"\\localhost\C$\{long_tail}"); - - assert_eq!( - to_win32_path_wide(Path::new(&long_drive)), - to_wide(format!(r"\\?\{long_drive}")) - ); - assert_eq!( - to_win32_path_wide(Path::new(&long_unc)), - to_wide(format!(r"\\?\UNC\{}", &long_unc[2..])) - ); - for path in [r"C:\short", r"\\localhost\C$\short", r"\\?\C:\already"] { - assert_eq!(to_win32_path_wide(Path::new(path)), to_wide(path)); - } - } }