diff --git a/codex-rs/cli/src/debug_sandbox.rs b/codex-rs/cli/src/debug_sandbox.rs index 889bc5a6..2b90cb93 100644 --- a/codex-rs/cli/src/debug_sandbox.rs +++ b/codex-rs/cli/src/debug_sandbox.rs @@ -124,6 +124,7 @@ async fn run_command_under_sandbox( let cwd_clone = cwd.clone(); let env_map = env.clone(); let command_vec = command.clone(); + let base_dir = config.codex_home.clone(); let res = tokio::task::spawn_blocking(move || { run_windows_sandbox_capture( policy_str, @@ -132,6 +133,7 @@ async fn run_command_under_sandbox( &cwd_clone, env_map, None, + Some(base_dir.as_path()), ) }) .await; diff --git a/codex-rs/core/src/exec.rs b/codex-rs/core/src/exec.rs index e8eb0644..b4dacd9a 100644 --- a/codex-rs/core/src/exec.rs +++ b/codex-rs/core/src/exec.rs @@ -171,6 +171,7 @@ async fn exec_windows_sandbox( params: ExecParams, sandbox_policy: &SandboxPolicy, ) -> Result { + use crate::config::find_codex_home; use codex_windows_sandbox::run_windows_sandbox_capture; let ExecParams { @@ -188,8 +189,17 @@ async fn exec_windows_sandbox( }; let sandbox_cwd = cwd.clone(); + let logs_base_dir = find_codex_home().ok(); let spawn_res = tokio::task::spawn_blocking(move || { - run_windows_sandbox_capture(policy_str, &sandbox_cwd, command, &cwd, env, timeout_ms) + run_windows_sandbox_capture( + policy_str, + &sandbox_cwd, + command, + &cwd, + env, + timeout_ms, + logs_base_dir.as_deref(), + ) }) .await; diff --git a/codex-rs/windows-sandbox-rs/src/lib.rs b/codex-rs/windows-sandbox-rs/src/lib.rs index d0b9292d..5e165f42 100644 --- a/codex-rs/windows-sandbox-rs/src/lib.rs +++ b/codex-rs/windows-sandbox-rs/src/lib.rs @@ -182,6 +182,7 @@ mod windows_impl { cwd: &Path, mut env_map: HashMap, timeout_ms: Option, + logs_base_dir: Option<&Path>, ) -> Result { let policy = SandboxPolicy::parse(policy_json_or_preset)?; normalize_null_device_env(&mut env_map); @@ -191,7 +192,7 @@ mod windows_impl { let current_dir = cwd.to_path_buf(); // for now, don't fail if we detect world-writable directories // audit::audit_everyone_writable(¤t_dir, &env_map)?; - log_start(&command); + log_start(&command, logs_base_dir); let (h_token, psid_to_use): (HANDLE, *mut c_void) = unsafe { match &policy.0 { SandboxMode::ReadOnly => { @@ -295,7 +296,7 @@ mod windows_impl { env_block.len(), si.dwFlags, ); - debug_log(&dbg); + debug_log(&dbg, logs_base_dir); unsafe { CloseHandle(in_r); CloseHandle(in_w); @@ -395,9 +396,9 @@ mod windows_impl { }; if exit_code == 0 { - log_success(&command); + log_success(&command, logs_base_dir); } else { - log_failure(&command, &format!("exit code {}", exit_code)); + log_failure(&command, &format!("exit code {}", exit_code), logs_base_dir); } if !persist_aces { @@ -446,6 +447,7 @@ mod stub { _cwd: &Path, _env_map: HashMap, _timeout_ms: Option, + _logs_base_dir: Option<&Path>, ) -> Result { bail!("Windows sandbox is only available on Windows") } diff --git a/codex-rs/windows-sandbox-rs/src/logging.rs b/codex-rs/windows-sandbox-rs/src/logging.rs index feb42ece..42d6d807 100644 --- a/codex-rs/windows-sandbox-rs/src/logging.rs +++ b/codex-rs/windows-sandbox-rs/src/logging.rs @@ -1,5 +1,7 @@ use std::fs::OpenOptions; use std::io::Write; +use std::path::Path; +use std::path::PathBuf; const LOG_COMMAND_PREVIEW_LIMIT: usize = 200; pub const LOG_FILE_NAME: &str = "sandbox_commands.rust.log"; @@ -13,35 +15,43 @@ fn preview(command: &[String]) -> String { } } -fn append_line(line: &str) { - if let Ok(mut f) = OpenOptions::new() - .create(true) - .append(true) - .open(LOG_FILE_NAME) - { - let _ = writeln!(f, "{}", line); +fn log_file_path(base_dir: &Path) -> Option { + if base_dir.is_dir() { + Some(base_dir.join(LOG_FILE_NAME)) + } else { + None } } -pub fn log_start(command: &[String]) { - let p = preview(command); - append_line(&format!("START: {}", p)); +fn append_line(line: &str, base_dir: Option<&Path>) { + if let Some(dir) = base_dir { + if let Some(path) = log_file_path(dir) { + if let Ok(mut f) = OpenOptions::new().create(true).append(true).open(path) { + let _ = writeln!(f, "{}", line); + } + } + } } -pub fn log_success(command: &[String]) { +pub fn log_start(command: &[String], base_dir: Option<&Path>) { let p = preview(command); - append_line(&format!("SUCCESS: {}", p)); + append_line(&format!("START: {p}"), base_dir); } -pub fn log_failure(command: &[String], detail: &str) { +pub fn log_success(command: &[String], base_dir: Option<&Path>) { let p = preview(command); - append_line(&format!("FAILURE: {} ({})", p, detail)); + append_line(&format!("SUCCESS: {p}"), base_dir); +} + +pub fn log_failure(command: &[String], detail: &str, base_dir: Option<&Path>) { + let p = preview(command); + append_line(&format!("FAILURE: {p} ({detail})"), base_dir); } // Debug logging helper. Emits only when SBX_DEBUG=1 to avoid noisy logs. -pub fn debug_log(msg: &str) { +pub fn debug_log(msg: &str, base_dir: Option<&Path>) { if std::env::var("SBX_DEBUG").ok().as_deref() == Some("1") { - append_line(&format!("DEBUG: {}", msg)); - eprintln!("{}", msg); + append_line(&format!("DEBUG: {msg}"), base_dir); + eprintln!("{msg}"); } } diff --git a/codex-rs/windows-sandbox-rs/src/process.rs b/codex-rs/windows-sandbox-rs/src/process.rs index 77c4b845..095dcf8b 100644 --- a/codex-rs/windows-sandbox-rs/src/process.rs +++ b/codex-rs/windows-sandbox-rs/src/process.rs @@ -101,6 +101,7 @@ pub unsafe fn create_process_as_user( argv: &[String], cwd: &Path, env_map: &HashMap, + logs_base_dir: Option<&Path>, ) -> Result<(PROCESS_INFORMATION, STARTUPINFOW)> { let cmdline_str = argv .iter() @@ -142,7 +143,7 @@ pub unsafe fn create_process_as_user( env_block.len(), si.dwFlags, ); - logging::debug_log(&msg); + logging::debug_log(&msg, logs_base_dir); return Err(anyhow!("CreateProcessAsUserW failed: {}", err)); } Ok((pi, si))