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
This commit is contained in:
zm-oai authored and copyberry committed 2026-07-13 18:25:55 +00:00
1 parent 84696e3d57
commit 87f3e39fdf
4 files changed
+33 -1

No files matched your search

@@ -13,6 +13,7 @@ mod cwd_junction;
use anyhow::Context; use anyhow::Context;
use anyhow::Result; use anyhow::Result;
use codex_windows_sandbox::ConsoleMode;
use codex_windows_sandbox::ErrorPayload; use codex_windows_sandbox::ErrorPayload;
use codex_windows_sandbox::ErrorStage; use codex_windows_sandbox::ErrorStage;
use codex_windows_sandbox::ExitPayload; 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::TerminateProcess;
use windows_sys::Win32::System::Threading::WaitForSingleObject; 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 READ_ACL_MUTEX_NAME: &str = "Local\\CodexSandboxReadAcl";
const WAIT_TIMEOUT: u32 = 0x0000_0102; const WAIT_TIMEOUT: u32 = 0x0000_0102;
@@ -339,6 +343,11 @@ fn spawn_ipc_process(req: &SpawnRequest) -> Result<IpcSpawnedProcess> {
&req.env, &req.env,
stdin_mode, stdin_mode,
StderrMode::Separate, StderrMode::Separate,
if req.command.get(1).is_some_and(|arg| arg == FS_HELPER_ARG) {
ConsoleMode::NoWindow
} else {
ConsoleMode::Inherit
},
req.use_private_desktop, req.use_private_desktop,
Some(log_dir.as_path()), Some(log_dir.as_path()),
)?; )?;
+4
View File
@@ -235,6 +235,8 @@ pub use logging::log_writer;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use path_normalization::canonicalize_path; pub use path_normalization::canonicalize_path;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use process::ConsoleMode;
#[cfg(target_os = "windows")]
pub use process::PipeSpawnHandles; pub use process::PipeSpawnHandles;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use process::StderrMode; pub use process::StderrMode;
@@ -352,6 +354,7 @@ mod windows_impl {
use super::WindowsSandboxCancellationToken; use super::WindowsSandboxCancellationToken;
use super::logging::log_failure; use super::logging::log_failure;
use super::logging::log_success; use super::logging::log_success;
use super::process::ConsoleMode;
use super::process::create_process_as_user; use super::process::create_process_as_user;
use super::sandbox_utils::ensure_codex_home_exists; use super::sandbox_utils::ensure_codex_home_exists;
use super::spawn_prep::LegacyAclSids; use super::spawn_prep::LegacyAclSids;
@@ -569,6 +572,7 @@ mod windows_impl {
&env_map, &env_map,
logs_base_dir, logs_base_dir,
Some((in_r, out_w, err_w)), Some((in_r, out_w, err_w)),
ConsoleMode::Inherit,
use_private_desktop, use_private_desktop,
) )
}; };
+18 -1
View File
@@ -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_INPUT_HANDLE;
use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE; use windows_sys::Win32::System::Console::STD_OUTPUT_HANDLE;
use windows_sys::Win32::System::Pipes::CreatePipe; 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::CREATE_UNICODE_ENVIRONMENT;
use windows_sys::Win32::System::Threading::CreateProcessAsUserW; use windows_sys::Win32::System::Threading::CreateProcessAsUserW;
use windows_sys::Win32::System::Threading::EXTENDED_STARTUPINFO_PRESENT; use windows_sys::Win32::System::Threading::EXTENDED_STARTUPINFO_PRESENT;
@@ -37,6 +38,12 @@ pub struct CreatedProcess {
_desktop: LaunchDesktop, _desktop: LaunchDesktop,
} }
/// Controls console creation for pipe-backed child processes.
pub enum ConsoleMode {
Inherit,
NoWindow,
}
pub fn make_env_block(env: &HashMap<String, String>) -> Vec<u16> { pub fn make_env_block(env: &HashMap<String, String>) -> Vec<u16> {
let mut items: Vec<(String, String)> = let mut items: Vec<(String, String)> =
env.iter().map(|(k, v)| (k.clone(), v.clone())).collect(); env.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
@@ -76,6 +83,8 @@ unsafe fn ensure_inheritable_stdio(si: &mut STARTUPINFOW) -> Result<()> {
/// # Safety /// # Safety
/// Caller must provide a valid primary token handle (`h_token`) with appropriate access, /// 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. /// 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( pub unsafe fn create_process_as_user(
h_token: HANDLE, h_token: HANDLE,
argv: &[String], argv: &[String],
@@ -83,6 +92,7 @@ pub unsafe fn create_process_as_user(
env_map: &HashMap<String, String>, env_map: &HashMap<String, String>,
logs_base_dir: Option<&Path>, logs_base_dir: Option<&Path>,
stdio: Option<(HANDLE, HANDLE, HANDLE)>, stdio: Option<(HANDLE, HANDLE, HANDLE)>,
console_mode: ConsoleMode,
use_private_desktop: bool, use_private_desktop: bool,
) -> Result<CreatedProcess> { ) -> Result<CreatedProcess> {
let cmdline_str = argv_to_command_line(argv); 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)?; attrs.set_handle_list(inherited_handles)?;
si.lpAttributeList = attrs.as_mut_ptr(); 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( let ok = CreateProcessAsUserW(
h_token, h_token,
std::ptr::null(), std::ptr::null(),
@@ -232,6 +247,7 @@ pub fn spawn_process_with_pipes(
env_map: &HashMap<String, String>, env_map: &HashMap<String, String>,
stdin_mode: StdinMode, stdin_mode: StdinMode,
stderr_mode: StderrMode, stderr_mode: StderrMode,
console_mode: ConsoleMode,
use_private_desktop: bool, use_private_desktop: bool,
logs_base_dir: Option<&Path>, logs_base_dir: Option<&Path>,
) -> Result<PipeSpawnHandles> { ) -> Result<PipeSpawnHandles> {
@@ -275,6 +291,7 @@ pub fn spawn_process_with_pipes(
env_map, env_map,
logs_base_dir, logs_base_dir,
stdio, stdio,
console_mode,
use_private_desktop, use_private_desktop,
) )
}; };
@@ -4,6 +4,7 @@ use crate::conpty::spawn_conpty_process_as_user;
use crate::desktop::LaunchDesktop; use crate::desktop::LaunchDesktop;
use crate::logging::log_failure; use crate::logging::log_failure;
use crate::logging::log_success; use crate::logging::log_success;
use crate::process::ConsoleMode;
use crate::process::StderrMode; use crate::process::StderrMode;
use crate::process::StdinMode; use crate::process::StdinMode;
use crate::process::read_handle_loop; use crate::process::read_handle_loop;
@@ -98,6 +99,7 @@ fn spawn_legacy_process(
StdinMode::Closed StdinMode::Closed
}, },
StderrMode::Separate, StderrMode::Separate,
ConsoleMode::Inherit,
use_private_desktop, use_private_desktop,
logs_base_dir, logs_base_dir,
)?; )?;