fix(exec-server): separate proxy launch inputs

Co-authored-by: Codex noreply@openai.com
This commit is contained in:
viyatb-oai
2026-07-09 10:04:13 -07:00
co-authored by Codex noreply@openai.com
parent 2af252a7b8
commit 6d48c777bc
26 changed files with 206 additions and 43 deletions
+1
View File
@@ -461,6 +461,7 @@ pub(crate) async fn execute_exec_request(
exec_server_sandbox: _,
exec_server_enforce_managed_network: _,
exec_server_managed_network: _,
exec_server_network_proxy: _,
} = exec_request;
// TODO(anp): Keep PathUri through the local process launch boundary.
+4
View File
@@ -17,6 +17,7 @@ use crate::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
use codex_file_system::FileSystemSandboxContext;
use codex_network_proxy::ManagedNetworkSandboxContext;
use codex_network_proxy::NetworkProxy;
use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::exec_output::ExecToolCallOutput;
use codex_protocol::models::PermissionProfile;
@@ -65,6 +66,7 @@ pub struct ExecRequest {
pub(crate) exec_server_sandbox: Option<FileSystemSandboxContext>,
pub(crate) exec_server_enforce_managed_network: bool,
pub(crate) exec_server_managed_network: Option<ManagedNetworkSandboxContext>,
pub(crate) exec_server_network_proxy: Option<RemoteNetworkProxyLaunchConfig>,
}
impl ExecRequest {
@@ -110,6 +112,7 @@ impl ExecRequest {
exec_server_sandbox: None,
exec_server_enforce_managed_network: false,
exec_server_managed_network: None,
exec_server_network_proxy: None,
}
}
@@ -169,6 +172,7 @@ impl ExecRequest {
exec_server_sandbox: None,
exec_server_enforce_managed_network: false,
exec_server_managed_network: None,
exec_server_network_proxy: None,
}
}
}
+1
View File
@@ -231,6 +231,7 @@ pub(crate) async fn execute_user_shell_command(
exec_server_sandbox: None,
exec_server_enforce_managed_network: false,
exec_server_managed_network: None,
exec_server_network_proxy: None,
};
let stdout_stream = Some(StdoutStream {
@@ -169,6 +169,7 @@ pub(super) async fn try_run_zsh_fork(
exec_server_sandbox: _,
exec_server_enforce_managed_network: _,
exec_server_managed_network: _,
exec_server_network_proxy: _,
} = sandbox_exec_request;
let ParsedShellCommand { script, login, .. } = extract_shell_script(&command)?;
let effective_timeout = Duration::from_millis(
@@ -904,6 +905,7 @@ impl CoreShellCommandExecutor {
exec_server_sandbox: None,
exec_server_enforce_managed_network: false,
exec_server_managed_network: None,
exec_server_network_proxy: None,
},
/*stdout_stream*/ None,
after_spawn,
@@ -42,7 +42,6 @@ use crate::unified_exec::UnifiedExecProcess;
use crate::unified_exec::UnifiedExecProcessManager;
use codex_network_proxy::ManagedNetworkSandboxContext;
use codex_network_proxy::NetworkProxy;
use codex_network_proxy::RemoteNetworkProxyConfig;
use codex_protocol::error::CodexErr;
use codex_protocol::error::SandboxErr;
use codex_protocol::models::AdditionalPermissionProfile;
@@ -324,26 +323,12 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
launch_sandbox_permissions,
));
let env = exec_env_for_sandbox_permissions(&req.env, launch_sandbox_permissions);
let (env, managed_network_context) = match managed_network {
let (env, managed_network_context, network_proxy_launch) = match managed_network {
Some(network) if environment_is_remote => {
let config = network.current_cfg().await.map_err(|err| {
ToolError::Codex(CodexErr::Io(io::Error::other(format!(
"failed to read effective network proxy config: {err}"
))))
let launch = network.remote_launch_config().await.map_err(|err| {
ToolError::Codex(CodexErr::Io(io::Error::other(err.to_string())))
})?;
let proxy_config = RemoteNetworkProxyConfig::from_effective_config(&config)
.map_err(|err| {
ToolError::Codex(CodexErr::Io(io::Error::other(err.to_string())))
})?;
let allow_local_binding = proxy_config.allow_local_binding;
(
env,
Some(codex_network_proxy::ManagedNetworkSandboxContext {
loopback_ports: Vec::new(),
allow_local_binding,
proxy_config: Some(proxy_config),
}),
)
(env, None, Some(launch))
}
Some(network) => {
let prepared = network
@@ -357,9 +342,9 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
req.turn_environment.environment_id
))))
})?;
(prepared.env, Some(prepared.sandbox_context))
(prepared.env, Some(prepared.sandbox_context), None)
}
None => (env, None),
None => (env, None, None),
};
let explicit_env_overrides = req.explicit_env_overrides.clone();
#[cfg(unix)]
@@ -496,6 +481,7 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
options,
attempt,
managed_network,
network_proxy_launch,
/*environment_id*/ Some(&req.turn_environment.environment_id),
req.exec_server_env_config.clone(),
req.tty,
@@ -232,7 +232,6 @@ fn exec_server_env_keeps_command_native_and_carries_sandbox_context() {
let managed_network = ManagedNetworkSandboxContext {
loopback_ports: vec![43123],
allow_local_binding: false,
proxy_config: None,
};
let command = || SandboxCommand {
program: "/bin/bash".into(),
@@ -192,6 +192,7 @@ fn exec_server_params_for_request(
sandbox: request.exec_server_sandbox.clone(),
enforce_managed_network: request.exec_server_enforce_managed_network,
managed_network: request.exec_server_managed_network.clone(),
network_proxy: request.exec_server_network_proxy.clone(),
}
}
@@ -915,6 +916,7 @@ impl UnifiedExecProcessManager {
options: ExecOptions,
attempt: &SandboxAttempt<'_>,
network: Option<&NetworkProxy>,
network_proxy_launch: Option<codex_network_proxy::RemoteNetworkProxyLaunchConfig>,
environment_id: Option<&str>,
exec_server_env_config: Option<ExecServerEnvConfig>,
tty: bool,
@@ -927,6 +929,7 @@ impl UnifiedExecProcessManager {
attempt.env_for(command, options, network, environment_id)
}
.map_err(ToolError::Codex)?;
request.exec_server_network_proxy = network_proxy_launch;
request.exec_server_env_config = exec_server_env_config;
self.open_session_with_prepared_exec_env(
process_id,
@@ -117,7 +117,6 @@ fn exec_server_params_use_path_uri_and_env_policy_overlay_contract() {
let managed_network = ManagedNetworkSandboxContext {
loopback_ports: vec![43123],
allow_local_binding: false,
proxy_config: None,
};
let mut request = ExecRequest {
command: vec!["bash".to_string(), "-lc".to_string(), "true".to_string()],
@@ -175,6 +174,7 @@ fn exec_server_params_use_path_uri_and_env_policy_overlay_contract() {
exec_server_sandbox: None,
exec_server_enforce_managed_network: true,
exec_server_managed_network: Some(managed_network.clone()),
exec_server_network_proxy: None,
};
let params =
+29 -2
View File
@@ -5,6 +5,7 @@ use codex_file_system::FileSystemSandboxContext;
pub use codex_file_system::WalkOptions;
pub use codex_file_system::WalkOutcome;
use codex_network_proxy::ManagedNetworkSandboxContext;
use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
use codex_protocol::config_types::ShellEnvironmentPolicyInherit;
use codex_shell_command::shell_detect::DetectedShell;
use codex_utils_path_uri::PathUri;
@@ -144,6 +145,9 @@ pub struct ExecParams {
/// continue to fail closed. This preserves compatibility with older clients.
#[serde(default)]
pub managed_network: Option<ManagedNetworkSandboxContext>,
/// Optional instructions for starting an executor-local managed-network proxy.
#[serde(default)]
pub network_proxy: Option<RemoteNetworkProxyLaunchConfig>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
@@ -574,13 +578,17 @@ mod tests {
use super::ShellInfo;
use codex_file_system::FileSystemSandboxContext;
use codex_network_proxy::ManagedNetworkSandboxContext;
use codex_network_proxy::NetworkProxyAuditMetadata;
use codex_network_proxy::NetworkProxyConfig;
use codex_network_proxy::RemoteNetworkProxyConfig;
use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
use codex_protocol::models::PermissionProfile;
use codex_utils_path_uri::PathUri;
use pretty_assertions::assert_eq;
use std::collections::HashMap;
#[test]
fn exec_params_managed_network_context_round_trips_and_defaults_for_legacy_peers() {
fn exec_params_keeps_proxy_launch_separate_from_sandbox_facts() {
let cwd =
PathUri::from_host_native_path(std::env::current_dir().expect("current directory"))
.expect("cwd URI");
@@ -598,8 +606,18 @@ mod tests {
managed_network: Some(ManagedNetworkSandboxContext {
loopback_ports: vec![43123, 48081],
allow_local_binding: false,
proxy_config: None,
}),
network_proxy: Some(
RemoteNetworkProxyLaunchConfig::new(
RemoteNetworkProxyConfig::from_effective_config(&NetworkProxyConfig::default())
.expect("supported remote config"),
)
.with_audit_metadata(NetworkProxyAuditMetadata {
conversation_id: Some("conversation-1".to_string()),
..NetworkProxyAuditMetadata::default()
})
.for_execution("remote".to_string(), "execution-1".to_string()),
),
};
let mut serialized = serde_json::to_value(&params).expect("serialize exec params");
@@ -610,6 +628,10 @@ mod tests {
"allowLocalBinding": false,
})
);
assert_eq!(
serialized["networkProxy"]["auditMetadata"]["conversationId"],
"conversation-1"
);
let round_trip: ExecParams =
serde_json::from_value(serialized.clone()).expect("deserialize exec params");
assert_eq!(round_trip, params);
@@ -618,10 +640,15 @@ mod tests {
.as_object_mut()
.expect("exec params object")
.remove("managedNetwork");
serialized
.as_object_mut()
.expect("exec params object")
.remove("networkProxy");
let legacy: ExecParams =
serde_json::from_value(serialized).expect("deserialize legacy exec params");
assert!(legacy.enforce_managed_network);
assert_eq!(legacy.managed_network, None);
assert_eq!(legacy.network_proxy, None);
}
#[test]
+1
View File
@@ -1387,6 +1387,7 @@ mod tests {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.instrument(parent_span)
.await
+2
View File
@@ -1176,6 +1176,7 @@ mod tests {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await
.expect("start process");
@@ -1216,6 +1217,7 @@ mod tests {
sandbox: Some(sandbox),
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await;
let Err(err) = result else {
@@ -1010,6 +1010,7 @@ mod tests {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
}
}
+10 -5
View File
@@ -7,6 +7,7 @@ use codex_network_proxy::ManagedNetworkSandboxContext;
use codex_network_proxy::NetworkProxy;
use codex_network_proxy::NetworkProxyHandle;
use codex_network_proxy::NetworkProxyState;
use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
use codex_network_proxy::is_managed_mitm_ca_trust_bundle_path;
use codex_protocol::models::PermissionProfile;
use codex_sandboxing::SandboxCommand;
@@ -38,8 +39,12 @@ pub(crate) async fn prepare_exec_request(
env: HashMap<String, String>,
runtime_paths: Option<&ExecServerRuntimePaths>,
) -> Result<PreparedExecRequest, JSONRPCErrorError> {
let (env, managed_network, network_proxy_handle) =
prepare_managed_network(params.managed_network.as_ref(), env).await?;
let (env, managed_network, network_proxy_handle) = prepare_managed_network(
params.managed_network.as_ref(),
params.network_proxy.as_ref(),
env,
)
.await?;
let Some(sandbox_context) = params.sandbox.as_ref() else {
return Ok(PreparedExecRequest {
command: params.argv.clone(),
@@ -155,6 +160,7 @@ pub(crate) async fn prepare_exec_request(
async fn prepare_managed_network(
managed_network: Option<&ManagedNetworkSandboxContext>,
network_proxy: Option<&RemoteNetworkProxyLaunchConfig>,
env: HashMap<String, String>,
) -> Result<
(
@@ -164,11 +170,10 @@ async fn prepare_managed_network(
),
JSONRPCErrorError,
> {
let Some(proxy_config) = managed_network.and_then(|context| context.proxy_config.clone())
else {
let Some(network_proxy) = network_proxy.cloned() else {
return Ok((env, managed_network.cloned(), None));
};
let state = NetworkProxyState::from_static_config(proxy_config.into_network_proxy_config())
let state = NetworkProxyState::from_remote_launch_config(network_proxy)
.map_err(|err| invalid_params(format!("invalid network proxy config: {err}")))?;
let proxy = NetworkProxy::builder()
.state(Arc::new(state))
@@ -5,6 +5,7 @@ use std::time::Duration;
use codex_network_proxy::ManagedNetworkSandboxContext;
use codex_network_proxy::NetworkProxyConfig;
use codex_network_proxy::RemoteNetworkProxyConfig;
use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
#[cfg(unix)]
use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
@@ -53,6 +54,7 @@ async fn sandbox_request_wraps_native_argv_on_executor() {
sandbox: Some(sandbox),
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
};
let prepared = prepare_exec_request(&params, HashMap::new(), Some(&runtime_paths))
@@ -117,8 +119,8 @@ async fn sandbox_request_allows_prepared_managed_proxy_port() {
managed_network: Some(ManagedNetworkSandboxContext {
loopback_ports: vec![43123],
allow_local_binding: false,
proxy_config: None,
}),
network_proxy: None,
};
let prepared = prepare_exec_request(&params, HashMap::new(), Some(&runtime_paths))
@@ -153,6 +155,7 @@ async fn native_request_preserves_native_launch_fields() {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
};
let prepared = prepare_exec_request(&params, env.clone(), /*runtime_paths*/ None)
@@ -189,11 +192,11 @@ async fn remote_proxy_config_starts_executor_local_proxy() {
arg0: None,
sandbox: None,
enforce_managed_network: false,
managed_network: Some(ManagedNetworkSandboxContext {
loopback_ports: Vec::new(),
allow_local_binding: false,
proxy_config: Some(proxy_config),
}),
managed_network: None,
network_proxy: Some(
RemoteNetworkProxyLaunchConfig::new(proxy_config)
.for_execution("remote".to_string(), "execution-1".to_string()),
),
};
let stale_proxy = "http://127.0.0.1:9".to_string();
let env = HashMap::from([("HTTP_PROXY".to_string(), stale_proxy.clone())]);
@@ -37,6 +37,7 @@ fn exec_params_with_argv(process_id: &str, argv: Vec<String>) -> ExecParams {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
}
}
@@ -594,6 +594,7 @@ mod tests {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
}
}
@@ -84,6 +84,7 @@ async fn assert_exec_process_starts_and_exits(use_remote: bool) -> Result<()> {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
assert_eq!(session.process.process_id().as_str(), "proc-1");
@@ -229,6 +230,7 @@ async fn assert_exec_process_streams_output(use_remote: bool) -> Result<()> {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
assert_eq!(session.process.process_id().as_str(), process_id);
@@ -263,6 +265,7 @@ async fn assert_exec_process_pushes_events(use_remote: bool) -> Result<()> {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
assert_eq!(session.process.process_id().as_str(), process_id);
@@ -313,6 +316,7 @@ async fn assert_exec_process_replays_events_after_close(use_remote: bool) -> Res
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
assert_eq!(session.process.process_id().as_str(), process_id);
@@ -364,6 +368,7 @@ async fn assert_exec_process_retains_output_after_exit_until_streams_close(
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
assert_eq!(session.process.process_id().as_str(), process_id);
@@ -440,6 +445,7 @@ async fn assert_exec_process_write_then_read(use_remote: bool) -> Result<()> {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
assert_eq!(session.process.process_id().as_str(), process_id);
@@ -480,6 +486,7 @@ async fn assert_exec_process_write_then_read_without_tty(use_remote: bool) -> Re
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
assert_eq!(session.process.process_id().as_str(), process_id);
@@ -516,6 +523,7 @@ async fn assert_exec_process_rejects_write_without_pipe_stdin(use_remote: bool)
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
assert_eq!(session.process.process_id().as_str(), process_id);
@@ -553,6 +561,7 @@ async fn assert_exec_process_signal_interrupts_process(use_remote: bool) -> Resu
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
assert_eq!(session.process.process_id().as_str(), process_id);
@@ -609,6 +618,7 @@ async fn assert_exec_process_signal_reports_unsupported_on_windows(use_remote: b
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
@@ -652,6 +662,7 @@ async fn assert_exec_process_preserves_queued_events_before_subscribe(
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
@@ -713,6 +724,7 @@ async fn remote_exec_process_recovers_after_transport_disconnect() -> Result<()>
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
+1
View File
@@ -153,6 +153,7 @@ async fn remote_environment_routes_encrypted_exec_server_rpc() -> Result<()> {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await?;
assert_eq!(
+1
View File
@@ -69,6 +69,7 @@ pub use proxy::PreparedManagedNetwork;
pub use proxy::has_proxy_url_env_vars;
pub use proxy::proxy_url_env_value;
pub use remote_config::RemoteNetworkProxyConfig;
pub use remote_config::RemoteNetworkProxyLaunchConfig;
pub use runtime::BlockedRequest;
pub use runtime::BlockedRequestArgs;
pub use runtime::BlockedRequestObserver;
+23 -5
View File
@@ -348,9 +348,6 @@ pub struct ManagedNetworkSandboxContext {
/// Whether the command may bind local sockets and exchange loopback traffic.
#[serde(default)]
pub allow_local_binding: bool,
/// Effective proxy settings for an exec-server that must start its own local listeners.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub proxy_config: Option<crate::RemoteNetworkProxyConfig>,
}
/// Environment-specific managed-network settings prepared for one command launch.
@@ -660,6 +657,29 @@ impl NetworkProxy {
self.state.current_cfg().await
}
/// Captures the static inputs needed to launch a matching executor-local proxy.
pub async fn remote_launch_config(&self) -> Result<crate::RemoteNetworkProxyLaunchConfig> {
let proxy = crate::RemoteNetworkProxyConfig::from_effective_config(
&self.state.current_cfg().await?,
)?;
let (environment_id, execution_id) = self
.execution_scope
.as_ref()
.map(|scope| {
(
Some(scope.environment_id.clone()),
Some(scope.execution_id.clone()),
)
})
.unwrap_or_default();
Ok(crate::RemoteNetworkProxyLaunchConfig {
proxy,
audit_metadata: self.state.audit_metadata().clone(),
environment_id,
execution_id,
})
}
pub async fn add_allowed_domain(&self, host: &str) -> Result<()> {
self.state.add_allowed_domain(host).await
}
@@ -730,7 +750,6 @@ impl NetworkProxy {
sandbox_context: ManagedNetworkSandboxContext {
loopback_ports,
allow_local_binding: runtime_settings.allow_local_binding,
proxy_config: None,
},
}
}
@@ -1220,7 +1239,6 @@ mod tests {
ManagedNetworkSandboxContext {
loopback_ports: expected_ports,
allow_local_binding: false,
proxy_config: None,
}
);
}
@@ -2,6 +2,7 @@ use super::*;
pub(super) struct ExecutionScope {
pub(super) environment_id: String,
pub(super) execution_id: String,
pub(super) attribution_token: String,
state: Arc<NetworkProxyState>,
}
@@ -30,6 +31,7 @@ impl NetworkProxy {
let mut proxy = self.clone();
proxy.execution_scope = Some(Arc::new(ExecutionScope {
environment_id: environment_id.to_string(),
execution_id: execution_id.to_string(),
attribution_token,
state: Arc::clone(&self.state),
}));
@@ -5,9 +5,49 @@ use serde::Serialize;
use crate::NetworkDomainPermissions;
use crate::NetworkMode;
use crate::NetworkProxyAuditMetadata;
use crate::NetworkProxyConfig;
use crate::NetworkUnixSocketPermissions;
/// Executor-local proxy launch inputs transported with one process start.
///
/// Unlike [`crate::ManagedNetworkSandboxContext`], this describes how the executor should create
/// proxy listeners. The sandbox context is materialized only after those listeners are running.
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
#[non_exhaustive]
pub struct RemoteNetworkProxyLaunchConfig {
pub proxy: RemoteNetworkProxyConfig,
#[serde(default)]
pub audit_metadata: NetworkProxyAuditMetadata,
#[serde(default)]
pub environment_id: Option<String>,
#[serde(default)]
pub execution_id: Option<String>,
}
impl RemoteNetworkProxyLaunchConfig {
pub fn new(proxy: RemoteNetworkProxyConfig) -> Self {
Self {
proxy,
audit_metadata: NetworkProxyAuditMetadata::default(),
environment_id: None,
execution_id: None,
}
}
pub fn with_audit_metadata(mut self, audit_metadata: NetworkProxyAuditMetadata) -> Self {
self.audit_metadata = audit_metadata;
self
}
pub fn for_execution(mut self, environment_id: String, execution_id: String) -> Self {
self.environment_id = Some(environment_id);
self.execution_id = Some(execution_id);
self
}
}
/// Effective network proxy settings that are safe to send to a remote executor.
///
/// Listener addresses are deliberately omitted because the executor chooses its own loopback
@@ -1,8 +1,11 @@
use pretty_assertions::assert_eq;
use super::RemoteNetworkProxyConfig;
use super::RemoteNetworkProxyLaunchConfig;
use crate::NetworkMode;
use crate::NetworkProxyAuditMetadata;
use crate::NetworkProxyConfig;
use crate::NetworkProxyState;
#[test]
fn round_trip_preserves_supported_effective_settings() {
@@ -44,3 +47,27 @@ fn rejects_mitm_configuration() {
"remote exec-server network proxy does not support MITM, credential injection, or MITM hooks"
);
}
#[test]
fn launch_config_materializes_audit_and_execution_attribution() {
let proxy = RemoteNetworkProxyConfig::from_effective_config(&NetworkProxyConfig::default())
.expect("supported remote config");
let audit_metadata = NetworkProxyAuditMetadata {
conversation_id: Some("conversation-1".to_string()),
user_account_id: Some("account-1".to_string()),
originator: Some("codex_cli_rs".to_string()),
model: Some("model-1".to_string()),
..NetworkProxyAuditMetadata::default()
};
let state = NetworkProxyState::from_remote_launch_config(RemoteNetworkProxyLaunchConfig {
proxy,
audit_metadata: audit_metadata.clone(),
environment_id: Some("remote".to_string()),
execution_id: Some("execution-1".to_string()),
})
.expect("remote launch state");
assert_eq!(state.audit_metadata(), &audit_metadata);
assert_eq!(state.environment_id(), Some("remote"));
assert_eq!(state.execution_id().as_deref(), Some("execution-1"));
}
+26 -1
View File
@@ -23,6 +23,7 @@ use anyhow::Context;
use anyhow::Result;
use codex_utils_absolute_path::AbsolutePathBuf;
use globset::GlobSet;
use serde::Deserialize;
use serde::Serialize;
use std::collections::HashMap;
use std::collections::HashSet;
@@ -47,7 +48,8 @@ const MAX_BLOCKED_EVENTS: usize = 200;
const DNS_LOOKUP_TIMEOUT: Duration = Duration::from_secs(2);
const NETWORK_POLICY_VIOLATION_PREFIX: &str = "CODEX_NETWORK_POLICY_VIOLATION";
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct NetworkProxyAuditMetadata {
pub conversation_id: Option<String>,
pub app_version: Option<String>,
@@ -286,6 +288,29 @@ impl NetworkProxyState {
))
}
/// Builds immutable runtime state for one executor-local proxy launch.
pub fn from_remote_launch_config(
launch: crate::RemoteNetworkProxyLaunchConfig,
) -> Result<Self> {
let crate::RemoteNetworkProxyLaunchConfig {
proxy,
audit_metadata,
environment_id,
execution_id,
} = launch;
let config = proxy.into_network_proxy_config();
let state = build_config_state(config, NetworkProxyConstraints::default())?;
Ok(Self {
environment_id: environment_id.map(Into::into),
execution_id: execution_id.map(Into::into),
..Self::with_reloader_and_audit_metadata(
state.clone(),
Arc::new(StaticConfigReloader { state }),
audit_metadata,
)
})
}
pub fn with_reloader(state: ConfigState, reloader: Arc<dyn ConfigReloader>) -> Self {
Self::with_reloader_and_audit_metadata(
state,
@@ -510,6 +510,7 @@ impl ExecutorStdioServerLauncher {
sandbox: None,
enforce_managed_network: false,
managed_network: None,
network_proxy: None,
})
.await
.map_err(io::Error::other)?;
@@ -269,7 +269,6 @@ fn prepared_managed_network_context_allows_only_its_proxy_ports() {
let managed_network = ManagedNetworkSandboxContext {
loopback_ports: vec![43123, 48081],
allow_local_binding: false,
proxy_config: None,
};
let args = create_seatbelt_command_args(CreateSeatbeltCommandArgsParams {
command: vec!["/bin/true".to_string()],