mirror of
https://github.com/openai/codex.git
synced 2026-11-06 20:20:34 +00:00
## Why `codex exec --output-last-message` wrote through a pathname after tools finished, allowing sandbox activity to redirect the final write by replacing that path with a symlink. The related risk is described in https://github.com/openai/codex/pull/25956. ## What changed - Open the destination before starting the agent and retain its handle until the final write. - Reject symlinks, Windows reparse points, and multiply linked files; verify that the pathname still identifies the bound file before truncating and after writing. - Propagate output errors so the command fails when the destination cannot be opened or written safely. - Preserve inherited Unix output streams, FIFOs, devices, and Windows `NUL` support, while avoiding duplicate final answers when human output targets stdout. ## Testing Add regression coverage for sandbox-created symlinks across consecutive invocations, redirected stdout, delayed truncation, hard links, replaced paths, and Windows stream binding and filesystem identity checks. GitOrigin-RevId: 4a31bd28a5eb2d0a9e200cb658eb7d19dc93e933
48 lines
1.4 KiB
Rust
48 lines
1.4 KiB
Rust
use anyhow::Context;
|
|
use std::path::Path;
|
|
|
|
use codex_app_server_protocol::ServerNotification;
|
|
use codex_core::config::Config;
|
|
use codex_exec_server::BoundOutput;
|
|
use codex_protocol::protocol::SessionConfiguredEvent;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum CodexStatus {
|
|
Running,
|
|
InitiateShutdown,
|
|
}
|
|
|
|
pub(crate) trait EventProcessor {
|
|
/// Print summary of effective configuration and user prompt.
|
|
fn print_config_summary(
|
|
&mut self,
|
|
config: &Config,
|
|
prompt: &str,
|
|
session_configured: &SessionConfiguredEvent,
|
|
);
|
|
|
|
/// Handle a single typed app-server notification emitted by the agent.
|
|
fn process_server_notification(&mut self, notification: ServerNotification) -> CodexStatus;
|
|
|
|
/// Handle a local exec warning that is not represented as an app-server notification.
|
|
fn process_warning(&mut self, message: String) -> CodexStatus;
|
|
|
|
fn print_final_output(&mut self, output: Option<BoundOutput>) -> anyhow::Result<()>;
|
|
}
|
|
|
|
pub(crate) fn handle_last_message(
|
|
last_agent_message: Option<&str>,
|
|
path: &Path,
|
|
file: BoundOutput,
|
|
) -> anyhow::Result<()> {
|
|
file.write(last_agent_message.unwrap_or_default().as_bytes())
|
|
.with_context(|| format!("Failed to write last message file {path:?}"))?;
|
|
if last_agent_message.is_none() {
|
|
eprintln!(
|
|
"Warning: no last agent message; wrote empty content to {}",
|
|
path.display()
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|