diff --git a/codex-rs/apply-patch/src/lib.rs b/codex-rs/apply-patch/src/lib.rs index 6b6aba2df3..78ae1ecf35 100644 --- a/codex-rs/apply-patch/src/lib.rs +++ b/codex-rs/apply-patch/src/lib.rs @@ -38,6 +38,7 @@ pub const APPLY_PATCH_TOOL_INSTRUCTIONS: &str = include_str!("../apply_patch_too /// process-invocation contract between the apply-patch runtime and the arg0 /// dispatcher. pub const CODEX_CORE_APPLY_PATCH_ARG1: &str = "--codex-run-as-apply-patch"; +pub const CODEX_CORE_APPLY_PATCH_FILE_ARG1: &str = "--codex-run-as-apply-patch-file"; #[derive(Debug, Error, PartialEq)] pub enum ApplyPatchError { diff --git a/codex-rs/arg0/src/lib.rs b/codex-rs/arg0/src/lib.rs index f8b61796b5..37b1c6c49e 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,10 +94,23 @@ 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_arg = if argv1 == CODEX_CORE_APPLY_PATCH_FILE_ARG1 { + match std::fs::read_to_string(&patch_arg) { + Ok(patch_arg) => patch_arg, + Err(err) => { + eprintln!( + "Error: failed to read PATCH file for {CODEX_CORE_APPLY_PATCH_FILE_ARG1}: {err}" + ); + std::process::exit(1); + } + } + } else { + patch_arg + }; let mut stdout = std::io::stdout(); let mut stderr = std::io::stderr(); let cwd = match codex_utils_absolute_path::AbsolutePathBuf::current_dir() { @@ -122,7 +136,7 @@ pub fn arg0_dispatch() -> Option { } } None => { - eprintln!("Error: {CODEX_CORE_APPLY_PATCH_ARG1} requires a UTF-8 PATCH argument."); + eprintln!("Error: {argv1:?} requires a UTF-8 PATCH 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 3104f4eae1..3c62d55a24 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::exec_output::ExecToolCallOutput; use codex_protocol::exec_output::StreamOutput; use codex_protocol::models::PermissionProfile; @@ -33,6 +35,8 @@ use codex_sandboxing::SandboxablePreference; use codex_utils_absolute_path::AbsolutePathBuf; use futures::future::BoxFuture; use std::collections::HashMap; +#[cfg(target_os = "windows")] +use std::io::Write; use std::path::PathBuf; use std::time::Instant; @@ -71,10 +75,13 @@ impl ApplyPatchRuntime { fn build_sandbox_command( req: &ApplyPatchRequest, codex_home: &std::path::Path, + patch_file: &std::path::Path, ) -> Result { Ok(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.display().to_string(), )) } @@ -84,7 +91,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"))] @@ -97,13 +109,15 @@ 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 { + fn build_sandbox_command_with_program( + req: &ApplyPatchRequest, + exe: PathBuf, + mode_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![mode_arg, patch_arg], cwd: req.action.cwd.to_path_buf(), // Run apply_patch with a minimal environment for determinism and to avoid leaks. env: HashMap::new(), @@ -111,6 +125,22 @@ impl ApplyPatchRuntime { } } + #[cfg(target_os = "windows")] + fn write_patch_temp_file( + req: &ApplyPatchRequest, + ) -> Result { + let mut patch_file = tempfile::Builder::new() + .prefix(".codex-apply-patch-") + .suffix(".patch") + .tempfile_in(req.action.cwd.as_path()) + .map_err(|err| ToolError::Rejected(format!("failed to create patch file: {err}")))?; + patch_file + .write_all(req.action.patch.as_bytes()) + .and_then(|_| patch_file.flush()) + .map_err(|err| ToolError::Rejected(format!("failed to write patch file: {err}")))?; + Ok(patch_file) + } + fn stdout_stream(ctx: &ToolCtx) -> Option { Some(crate::exec::StdoutStream { sub_id: ctx.turn.sub_id.clone(), @@ -241,7 +271,10 @@ impl ToolRuntime for ApplyPatchRuntime { } #[cfg(target_os = "windows")] - let command = Self::build_sandbox_command(req, &ctx.turn.config.codex_home)?; + let patch_file = Self::write_patch_temp_file(req)?; + #[cfg(target_os = "windows")] + let command = + Self::build_sandbox_command(req, &ctx.turn.config.codex_home, patch_file.path())?; #[cfg(not(target_os = "windows"))] let command = Self::build_sandbox_command(req, ctx.turn.codex_self_exe.as_ref())?; let options = ExecOptions { diff --git a/codex-rs/windows-sandbox-rs/src/firewall.rs b/codex-rs/windows-sandbox-rs/src/firewall.rs index cfd114f648..d424dc0e30 100644 --- a/codex-rs/windows-sandbox-rs/src/firewall.rs +++ b/codex-rs/windows-sandbox-rs/src/firewall.rs @@ -39,7 +39,7 @@ const OFFLINE_BLOCK_LOOPBACK_TCP_RULE_FRIENDLY: &str = "Codex Sandbox Offline - Block Loopback TCP (Except Proxy)"; const OFFLINE_BLOCK_LOOPBACK_UDP_RULE_FRIENDLY: &str = "Codex Sandbox Offline - Block Loopback UDP"; const OFFLINE_PROXY_ALLOW_RULE_NAME: &str = "codex_sandbox_offline_allow_loopback_proxy"; -const LOOPBACK_REMOTE_ADDRESSES: &str = "127.0.0.0/8,::/127"; +const LOOPBACK_REMOTE_ADDRESSES: &str = "127.0.0.1-127.255.255.255,::1"; const NON_LOOPBACK_REMOTE_ADDRESSES: &str = "0.0.0.0-126.255.255.255,128.0.0.0-255.255.255.255,::,::2-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; struct BlockRuleSpec<'a> {