From 87f3e39fdf7e676d0ba25b0587f78e5b85e695e2 Mon Sep 17 00:00:00 2001 From: zm-oai Date: Mon, 13 Jul 2026 18:19:24 +0000 Subject: [PATCH] Hide Windows filesystem helper console windows (#32849) ## What changed - Add a console mode to the Windows sandbox process launcher. - Launch `--codex-run-as-fs-helper` subprocesses with `CREATE_NO_WINDOW`. - Preserve inherited-console behavior for regular sandboxed commands. GitOrigin-RevId: 927be36b82903b6a5fb1a243ba7f406f9ed85f3f --- .../src/bin/command_runner/win.rs | 9 +++++++++ codex-rs/windows-sandbox-rs/src/lib.rs | 4 ++++ codex-rs/windows-sandbox-rs/src/process.rs | 19 ++++++++++++++++++- .../src/unified_exec/backends/legacy.rs | 2 ++ 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/codex-rs/windows-sandbox-rs/src/bin/command_runner/win.rs b/codex-rs/windows-sandbox-rs/src/bin/command_runner/win.rs index bc8c4caddc..c167b661fd 100644 --- a/codex-rs/windows-sandbox-rs/src/bin/command_runner/win.rs +++ b/codex-rs/windows-sandbox-rs/src/bin/command_runner/win.rs @@ -13,6 +13,7 @@ mod cwd_junction; use anyhow::Context; use anyhow::Result; +use codex_windows_sandbox::ConsoleMode; use codex_windows_sandbox::ErrorPayload; use codex_windows_sandbox::ErrorStage; use codex_windows_sandbox::ExitPayload; @@ -77,6 +78,9 @@ use windows_sys::Win32::System::Threading::PROCESS_INFORMATION; use windows_sys::Win32::System::Threading::TerminateProcess; use windows_sys::Win32::System::Threading::WaitForSingleObject; +// Kept in sync with codex_exec_server::CODEX_FS_HELPER_ARG1 without introducing +// a dependency cycle. +const FS_HELPER_ARG: &str = "--codex-run-as-fs-helper"; const READ_ACL_MUTEX_NAME: &str = "Local\\CodexSandboxReadAcl"; const WAIT_TIMEOUT: u32 = 0x0000_0102; @@ -339,6 +343,11 @@ fn spawn_ipc_process(req: &SpawnRequest) -> Result { &req.env, stdin_mode, StderrMode::Separate, + if req.command.get(1).is_some_and(|arg| arg == FS_HELPER_ARG) { + ConsoleMode::NoWindow + } else { + ConsoleMode::Inherit + }, req.use_private_desktop, Some(log_dir.as_path()), )?; diff --git a/codex-rs/windows-sandbox-rs/src/lib.rs b/codex-rs/windows-sandbox-rs/src/lib.rs index 2a394f11ff..c2e1dd4b74 100644 --- a/codex-rs/windows-sandbox-rs/src/lib.rs +++ b/codex-rs/windows-sandbox-rs/src/lib.rs @@ -235,6 +235,8 @@ pub use logging::log_writer; #[cfg(target_os = "windows")] pub use path_normalization::canonicalize_path; #[cfg(target_os = "windows")] +pub use process::ConsoleMode; +#[cfg(target_os = "windows")] pub use process::PipeSpawnHandles; #[cfg(target_os = "windows")] pub use process::StderrMode; @@ -352,6 +354,7 @@ mod windows_impl { use super::WindowsSandboxCancellationToken; use super::logging::log_failure; use super::logging::log_success; + use super::process::ConsoleMode; use super::process::create_process_as_user; use super::sandbox_utils::ensure_codex_home_exists; use super::spawn_prep::LegacyAclSids; @@ -569,6 +572,7 @@ mod windows_impl { &env_map, logs_base_dir, Some((in_r, out_w, err_w)), + ConsoleMode::Inherit, use_private_desktop, ) }; diff --git a/codex-rs/windows-sandbox-rs/src/process.rs b/codex-rs/windows-sandbox-rs/src/process.rs index 86859baf10..d2aeac5383 100644 --- a/codex-rs/windows-sandbox-rs/src/process.rs +++ b/codex-rs/windows-sandbox-rs/src/process.rs @@ -23,6 +23,7 @@ use windows_sys::Win32::System::Console::STD_ERROR_HANDLE; use windows_sys::Win32::System::Console::STD_INPUT_HANDLE; use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE; use windows_sys::Win32::System::Pipes::CreatePipe; +use windows_sys::Win32::System::Threading::CREATE_NO_WINDOW; use windows_sys::Win32::System::Threading::CREATE_UNICODE_ENVIRONMENT; use windows_sys::Win32::System::Threading::CreateProcessAsUserW; use windows_sys::Win32::System::Threading::EXTENDED_STARTUPINFO_PRESENT; @@ -37,6 +38,12 @@ pub struct CreatedProcess { _desktop: LaunchDesktop, } +/// Controls console creation for pipe-backed child processes. +pub enum ConsoleMode { + Inherit, + NoWindow, +} + pub fn make_env_block(env: &HashMap) -> Vec { let mut items: Vec<(String, String)> = env.iter().map(|(k, v)| (k.clone(), v.clone())).collect(); @@ -76,6 +83,8 @@ unsafe fn ensure_inheritable_stdio(si: &mut STARTUPINFOW) -> Result<()> { /// # Safety /// Caller must provide a valid primary token handle (`h_token`) with appropriate access, /// and the `argv`, `cwd`, and `env_map` must remain valid for the duration of the call. +// Low-level CreateProcessAsUserW wrapper mirrors the Windows API shape. +#[allow(clippy::too_many_arguments)] pub unsafe fn create_process_as_user( h_token: HANDLE, argv: &[String], @@ -83,6 +92,7 @@ pub unsafe fn create_process_as_user( env_map: &HashMap, logs_base_dir: Option<&Path>, stdio: Option<(HANDLE, HANDLE, HANDLE)>, + console_mode: ConsoleMode, use_private_desktop: bool, ) -> Result { let cmdline_str = argv_to_command_line(argv); @@ -120,7 +130,12 @@ pub unsafe fn create_process_as_user( attrs.set_handle_list(inherited_handles)?; si.lpAttributeList = attrs.as_mut_ptr(); - let creation_flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT; + let creation_flags = CREATE_UNICODE_ENVIRONMENT + | EXTENDED_STARTUPINFO_PRESENT + | match console_mode { + ConsoleMode::Inherit => 0, + ConsoleMode::NoWindow => CREATE_NO_WINDOW, + }; let ok = CreateProcessAsUserW( h_token, std::ptr::null(), @@ -232,6 +247,7 @@ pub fn spawn_process_with_pipes( env_map: &HashMap, stdin_mode: StdinMode, stderr_mode: StderrMode, + console_mode: ConsoleMode, use_private_desktop: bool, logs_base_dir: Option<&Path>, ) -> Result { @@ -275,6 +291,7 @@ pub fn spawn_process_with_pipes( env_map, logs_base_dir, stdio, + console_mode, use_private_desktop, ) }; diff --git a/codex-rs/windows-sandbox-rs/src/unified_exec/backends/legacy.rs b/codex-rs/windows-sandbox-rs/src/unified_exec/backends/legacy.rs index f3c8ada8c9..a09f949135 100644 --- a/codex-rs/windows-sandbox-rs/src/unified_exec/backends/legacy.rs +++ b/codex-rs/windows-sandbox-rs/src/unified_exec/backends/legacy.rs @@ -4,6 +4,7 @@ use crate::conpty::spawn_conpty_process_as_user; use crate::desktop::LaunchDesktop; use crate::logging::log_failure; use crate::logging::log_success; +use crate::process::ConsoleMode; use crate::process::StderrMode; use crate::process::StdinMode; use crate::process::read_handle_loop; @@ -98,6 +99,7 @@ fn spawn_legacy_process( StdinMode::Closed }, StderrMode::Separate, + ConsoleMode::Inherit, use_private_desktop, logs_base_dir, )?;