From ec019930672f9b03b0b4608199b1e3e5827a18a6 Mon Sep 17 00:00:00 2001 From: David Wiesen Date: Thu, 9 Apr 2026 09:13:25 -0700 Subject: [PATCH] Avoid huge apply_patch argv on Windows sandbox --- codex-rs/apply-patch/src/lib.rs | 4 ++ codex-rs/arg0/src/lib.rs | 20 +++++- .../core/src/tools/runtimes/apply_patch.rs | 65 +++++++++++++++---- 3 files changed, 74 insertions(+), 15 deletions(-) diff --git a/codex-rs/apply-patch/src/lib.rs b/codex-rs/apply-patch/src/lib.rs index 3041c4b2cd..ba48907679 100644 --- a/codex-rs/apply-patch/src/lib.rs +++ b/codex-rs/apply-patch/src/lib.rs @@ -34,6 +34,10 @@ pub const APPLY_PATCH_TOOL_INSTRUCTIONS: &str = include_str!("../apply_patch_too /// dispatcher. pub const CODEX_CORE_APPLY_PATCH_ARG1: &str = "--codex-run-as-apply-patch"; +/// Like [`CODEX_CORE_APPLY_PATCH_ARG1`], but argv[2] is a UTF-8 file path +/// containing the patch body instead of the patch body itself. +pub const CODEX_CORE_APPLY_PATCH_FILE_ARG1: &str = "--codex-run-as-apply-patch-file"; + #[derive(Debug, Error, PartialEq)] pub enum ApplyPatchError { #[error(transparent)] diff --git a/codex-rs/arg0/src/lib.rs b/codex-rs/arg0/src/lib.rs index efad6c2481..4dc35c39cb 100644 --- a/codex-rs/arg0/src/lib.rs +++ b/codex-rs/arg0/src/lib.rs @@ -4,6 +4,7 @@ use std::path::Path; use std::path::PathBuf; use codex_apply_patch::CODEX_CORE_APPLY_PATCH_ARG1; +use codex_apply_patch::CODEX_CORE_APPLY_PATCH_FILE_ARG1; use codex_sandboxing::landlock::CODEX_LINUX_SANDBOX_ARG0; use codex_utils_home_dir::find_codex_home; #[cfg(unix)] @@ -93,19 +94,32 @@ pub fn arg0_dispatch() -> Option { } let argv1 = args.next().unwrap_or_default(); - if argv1 == CODEX_CORE_APPLY_PATCH_ARG1 { + if argv1 == CODEX_CORE_APPLY_PATCH_ARG1 || argv1 == CODEX_CORE_APPLY_PATCH_FILE_ARG1 { let patch_arg = args.next().and_then(|s| s.to_str().map(str::to_owned)); let exit_code = match patch_arg { Some(patch_arg) => { + let patch = if argv1 == CODEX_CORE_APPLY_PATCH_FILE_ARG1 { + match std::fs::read_to_string(&patch_arg) { + Ok(patch) => patch, + Err(err) => { + eprintln!( + "Error: failed to read apply_patch file {patch_arg:?}: {err}" + ); + std::process::exit(1); + } + } + } else { + patch_arg + }; let mut stdout = std::io::stdout(); let mut stderr = std::io::stderr(); - match codex_apply_patch::apply_patch(&patch_arg, &mut stdout, &mut stderr) { + match codex_apply_patch::apply_patch(&patch, &mut stdout, &mut stderr) { Ok(()) => 0, Err(_) => 1, } } None => { - eprintln!("Error: {CODEX_CORE_APPLY_PATCH_ARG1} requires a UTF-8 PATCH argument."); + eprintln!("Error: {argv1:?} requires a UTF-8 argument."); 1 } }; diff --git a/codex-rs/core/src/tools/runtimes/apply_patch.rs b/codex-rs/core/src/tools/runtimes/apply_patch.rs index 65a3981cca..04e3f04482 100644 --- a/codex-rs/core/src/tools/runtimes/apply_patch.rs +++ b/codex-rs/core/src/tools/runtimes/apply_patch.rs @@ -22,6 +22,8 @@ use crate::tools::sandboxing::ToolRuntime; use crate::tools::sandboxing::with_cached_approval; use codex_apply_patch::ApplyPatchAction; use codex_apply_patch::CODEX_CORE_APPLY_PATCH_ARG1; +#[cfg(target_os = "windows")] +use codex_apply_patch::CODEX_CORE_APPLY_PATCH_FILE_ARG1; use codex_protocol::models::PermissionProfile; use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::FileChange; @@ -32,6 +34,8 @@ use codex_utils_absolute_path::AbsolutePathBuf; use futures::future::BoxFuture; use std::collections::HashMap; use std::path::PathBuf; +#[cfg(target_os = "windows")] +use uuid::Uuid; #[derive(Debug)] pub struct ApplyPatchRequest { @@ -69,11 +73,15 @@ impl ApplyPatchRuntime { fn build_sandbox_command( req: &ApplyPatchRequest, codex_home: &std::path::Path, - ) -> Result { - Ok(Self::build_sandbox_command_with_program( + ) -> Result<(SandboxCommand, PathBuf), ToolError> { + let patch_file = Self::write_patch_file(req, codex_home)?; + let command = Self::build_sandbox_command_with_program( req, codex_windows_sandbox::resolve_current_exe_for_launch(codex_home, "codex.exe"), - )) + CODEX_CORE_APPLY_PATCH_FILE_ARG1.to_string(), + patch_file.to_string_lossy().to_string(), + ); + Ok((command, patch_file)) } #[cfg(not(target_os = "windows"))] @@ -82,7 +90,12 @@ impl ApplyPatchRuntime { codex_self_exe: Option<&PathBuf>, ) -> Result { let exe = Self::resolve_apply_patch_program(codex_self_exe)?; - Ok(Self::build_sandbox_command_with_program(req, exe)) + Ok(Self::build_sandbox_command_with_program( + req, + exe, + CODEX_CORE_APPLY_PATCH_ARG1.to_string(), + req.action.patch.clone(), + )) } #[cfg(not(target_os = "windows"))] @@ -95,13 +108,32 @@ impl ApplyPatchRuntime { .map_err(|e| ToolError::Rejected(format!("failed to determine codex exe: {e}"))) } - fn build_sandbox_command_with_program(req: &ApplyPatchRequest, exe: PathBuf) -> SandboxCommand { + #[cfg(target_os = "windows")] + fn write_patch_file( + req: &ApplyPatchRequest, + codex_home: &std::path::Path, + ) -> Result { + let dir = codex_home.join("tmp").join("apply-patch"); + std::fs::create_dir_all(&dir).map_err(|err| { + ToolError::Rejected(format!("failed to create apply_patch temp dir: {err}")) + })?; + + let path = dir.join(format!("patch-{}.txt", Uuid::new_v4())); + std::fs::write(&path, req.action.patch.as_bytes()).map_err(|err| { + ToolError::Rejected(format!("failed to write apply_patch temp file: {err}")) + })?; + Ok(path) + } + + fn build_sandbox_command_with_program( + req: &ApplyPatchRequest, + exe: PathBuf, + dispatch_arg: String, + patch_arg: String, + ) -> SandboxCommand { SandboxCommand { program: exe.into_os_string(), - args: vec![ - CODEX_CORE_APPLY_PATCH_ARG1.to_string(), - req.action.patch.clone(), - ], + args: vec![dispatch_arg, patch_arg], cwd: req.action.cwd.clone(), // Run apply_patch with a minimal environment for determinism and to avoid leaks. env: HashMap::new(), @@ -213,9 +245,15 @@ impl ToolRuntime for ApplyPatchRuntime { ctx: &ToolCtx, ) -> Result { #[cfg(target_os = "windows")] - let command = Self::build_sandbox_command(req, &ctx.turn.config.codex_home)?; + let (command, patch_file) = { + let (command, patch_file) = + Self::build_sandbox_command(req, &ctx.turn.config.codex_home)?; + (command, Some(patch_file)) + }; #[cfg(not(target_os = "windows"))] let command = Self::build_sandbox_command(req, ctx.turn.codex_self_exe.as_ref())?; + #[cfg(not(target_os = "windows"))] + let patch_file: Option = None; let options = ExecOptions { expiration: req.timeout_ms.into(), capture_policy: ExecCapturePolicy::ShellTool, @@ -225,8 +263,11 @@ impl ToolRuntime for ApplyPatchRuntime { .map_err(|err| ToolError::Codex(err.into()))?; let out = execute_env(env, Self::stdout_stream(ctx)) .await - .map_err(ToolError::Codex)?; - Ok(out) + .map_err(ToolError::Codex); + if let Some(patch_file) = patch_file { + let _ = std::fs::remove_file(patch_file); + } + Ok(out?) } }