Files
codex/codex-rs/core/tests/common/exec_server.rs
T
viyatb-oai 7d33d0d93f Enable credential masking for remote exec-server commands (#54309)
## What changed

Negotiate executor support for `credential_masking` and enable built-in credential brokerage in executor-local network proxies. Supported non-login Bash commands on Linux and macOS receive credential placeholders, with real credentials injected by the proxy for matching destinations.

Retain credential mappings across process launches within the same executor session, scoped by environment, conversation, and broker configuration. Keep credentials and destination context on the executor.

Fall back to unmasked execution for unsupported shells or shell startup, and for configured credential providers. Emit warnings when masking is unavailable, preserving shell profiles and snapshots.

## Testing

Add coverage for capability negotiation, fallback behavior, mapping reuse and isolation, proxy credential injection, and keeping executor credentials out of model-visible request history. Test warning, removal, and rejection modes for credentials left unmasked during preparation.

GitOrigin-RevId: f64e916a5de8350fa0d289d3440ec29761accd75
2026-10-15 14:41:16 +00:00

79 lines
2.4 KiB
Rust

//! Shared native exec-server process fixture; dropping it stops the child process.
use anyhow::Context;
use anyhow::Result;
use anyhow::bail;
use std::process::Stdio;
use std::time::Duration;
use tempfile::TempDir;
use tokio::io::AsyncBufReadExt;
use tokio::io::BufReader;
use tokio::process::Child;
use tokio::process::ChildStdout;
use tokio::process::Command;
use tokio::time::Instant;
use tokio::time::timeout;
const EXEC_SERVER_START_TIMEOUT: Duration = Duration::from_secs(30);
pub struct ExecServerProcess {
_codex_home: TempDir,
child: Child,
_stdout: BufReader<ChildStdout>,
pub websocket_url: String,
}
impl ExecServerProcess {
pub async fn start() -> Result<Self> {
Self::start_with_env(&[]).await
}
pub async fn start_with_env(env: &[(&str, &str)]) -> Result<Self> {
let codex_home = TempDir::new()?;
let mut child = Command::new(codex_utils_cargo_bin::cargo_bin("codex")?)
.args(["exec-server", "--listen", "ws://127.0.0.1:0"])
.env("CODEX_HOME", codex_home.path())
.envs(env.iter().copied())
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.kill_on_drop(true)
.spawn()?;
let stdout = child
.stdout
.take()
.context("exec-server stdout should be piped")?;
let mut stdout = BufReader::new(stdout);
let deadline = Instant::now() + EXEC_SERVER_START_TIMEOUT;
let websocket_url = loop {
let remaining = deadline
.checked_duration_since(Instant::now())
.context("timed out waiting for exec-server listen URL")?;
let mut line = String::new();
let bytes_read = timeout(remaining, stdout.read_line(&mut line))
.await
.context("timed out reading exec-server listen URL")??;
if bytes_read == 0 {
bail!("exec-server exited before printing its listen URL");
}
let line = line.trim();
if line.starts_with("ws://") {
break line.to_string();
}
};
Ok(Self {
_codex_home: codex_home,
child,
_stdout: stdout,
websocket_url,
})
}
}
impl Drop for ExecServerProcess {
fn drop(&mut self) {
let _ = self.child.start_kill();
}
}