mirror of
https://github.com/openai/codex.git
synced 2026-05-15 00:32:51 +00:00
fix: prevent codex-backend from stealing originator (#22533)
## Why Remote control starts by letting `codex-backend` initialize against the app-server as an infrastructure health/proxy client before the real remote client connects. App-server initialization also sets the process-wide `originator` from `client_info.name`, so `codex-backend` could become the sticky originator for later model/API requests even after the real client initialized. ## What changed - Treat `codex-backend` as a non-originating initialize client, alongside the existing `codex_app_server_daemon` probe client. - Preserve normal per-connection initialize behavior, including session metadata and initialize analytics. - Add regression coverage that verifies `codex-backend` initialize does not replace the default originator. ## Testing - `cargo test -p codex-app-server --test all initialize_codex_backend_does_not_override_originator`
This commit is contained in:
@@ -13,7 +13,7 @@ use super::*;
|
||||
use crate::message_processor::ConnectionSessionState;
|
||||
use crate::message_processor::InitializedConnectionSessionState;
|
||||
|
||||
const DAEMON_PROBE_CLIENT_NAME: &str = "codex_app_server_daemon";
|
||||
const NON_ORIGINATING_CLIENT_NAMES: &[&str] = &["codex_app_server_daemon", "codex-backend"];
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct InitializeRequestProcessor {
|
||||
@@ -92,7 +92,7 @@ impl InitializeRequestProcessor {
|
||||
}
|
||||
let originator = name.clone();
|
||||
let user_agent_suffix = format!("{name}; {version}");
|
||||
let mutates_global_identity = name != DAEMON_PROBE_CLIENT_NAME;
|
||||
let mutates_global_identity = !NON_ORIGINATING_CLIENT_NAMES.contains(&name.as_str());
|
||||
let codex_home = self.config.codex_home.clone();
|
||||
if session
|
||||
.initialize(InitializedConnectionSessionState {
|
||||
|
||||
@@ -89,6 +89,33 @@ async fn initialize_probe_does_not_override_originator() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn initialize_codex_backend_does_not_override_originator() -> Result<()> {
|
||||
let responses = Vec::new();
|
||||
let server = create_mock_responses_server_sequence_unchecked(responses).await;
|
||||
let codex_home = TempDir::new()?;
|
||||
create_config_toml(codex_home.path(), &server.uri(), "never")?;
|
||||
let mut mcp = McpProcess::new(codex_home.path()).await?;
|
||||
|
||||
let message = timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
mcp.initialize_with_client_info(ClientInfo {
|
||||
name: "codex-backend".to_string(),
|
||||
title: Some("Codex Backend".to_string()),
|
||||
version: "0.1.0".to_string(),
|
||||
}),
|
||||
)
|
||||
.await??;
|
||||
|
||||
let JSONRPCMessage::Response(response) = message else {
|
||||
anyhow::bail!("expected initialize response, got {message:?}");
|
||||
};
|
||||
let InitializeResponse { user_agent, .. } = to_response::<InitializeResponse>(response)?;
|
||||
|
||||
assert!(user_agent.starts_with("codex_cli_rs/"));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn initialize_respects_originator_override_env_var() -> Result<()> {
|
||||
let responses = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user