feat: include NetworkConfig through ExecParams (#11105)

This PR adds the following field to `Config`:

```rust
pub network: Option<NetworkProxy>,
```

Though for the moment, it will always be initialized as `None` (this
will be addressed in a subsequent PR).

This PR does the work to thread `network` through to `execute_exec_env()`, `process_exec_tool_call()`, and `UnifiedExecRuntime.run()` to ensure it is available whenever we span a process.
This commit is contained in:
Michael Bolin
2026-02-09 03:32:17 +00:00
committed by GitHub
parent ff74aaae21
commit 383b45279e
18 changed files with 116 additions and 45 deletions
+2
View File
@@ -6658,6 +6658,7 @@ mod tests {
cwd: turn_context.cwd.clone(),
expiration: timeout_ms.into(),
env: HashMap::new(),
network: None,
sandbox_permissions,
windows_sandbox_level: turn_context.windows_sandbox_level,
justification: Some("test".to_string()),
@@ -6670,6 +6671,7 @@ mod tests {
cwd: params.cwd.clone(),
expiration: timeout_ms.into(),
env: HashMap::new(),
network: None,
windows_sandbox_level: turn_context.windows_sandbox_level,
justification: params.justification.clone(),
arg0: None,
+9
View File
@@ -47,6 +47,7 @@ use crate::protocol::SandboxPolicy;
use crate::windows_sandbox::WindowsSandboxLevelExt;
use codex_app_server_protocol::Tools;
use codex_app_server_protocol::UserSavedConfig;
use codex_network_proxy::NetworkProxy;
use codex_protocol::config_types::AltScreenMode;
use codex_protocol::config_types::ForcedLoginMethod;
use codex_protocol::config_types::ModeKind;
@@ -152,6 +153,9 @@ pub struct Config {
/// using backend-specific headers or URLs to enforce this.
pub enforce_residency: Constrained<Option<ResidencyRequirement>>,
/// Effective network configuration applied to all spawned processes.
pub network: Option<NetworkProxy>,
/// True if the user passed in an override or set a value in config.toml
/// for either of approval_policy or sandbox_mode.
pub did_user_set_custom_approval_policy_or_sandbox_mode: bool,
@@ -1690,6 +1694,7 @@ impl Config {
approval_policy: constrained_approval_policy.value,
sandbox_policy: constrained_sandbox_policy.value,
enforce_residency: enforce_residency.value,
network: None,
did_user_set_custom_approval_policy_or_sandbox_mode,
forced_auto_mode_downgraded_on_windows,
shell_environment_policy,
@@ -4003,6 +4008,7 @@ model_verbosity = "high"
approval_policy: Constrained::allow_any(AskForApproval::Never),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
enforce_residency: Constrained::allow_any(None),
network: None,
did_user_set_custom_approval_policy_or_sandbox_mode: true,
forced_auto_mode_downgraded_on_windows: false,
shell_environment_policy: ShellEnvironmentPolicy::default(),
@@ -4091,6 +4097,7 @@ model_verbosity = "high"
approval_policy: Constrained::allow_any(AskForApproval::UnlessTrusted),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
enforce_residency: Constrained::allow_any(None),
network: None,
did_user_set_custom_approval_policy_or_sandbox_mode: true,
forced_auto_mode_downgraded_on_windows: false,
shell_environment_policy: ShellEnvironmentPolicy::default(),
@@ -4194,6 +4201,7 @@ model_verbosity = "high"
approval_policy: Constrained::allow_any(AskForApproval::OnFailure),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
enforce_residency: Constrained::allow_any(None),
network: None,
did_user_set_custom_approval_policy_or_sandbox_mode: true,
forced_auto_mode_downgraded_on_windows: false,
shell_environment_policy: ShellEnvironmentPolicy::default(),
@@ -4283,6 +4291,7 @@ model_verbosity = "high"
approval_policy: Constrained::allow_any(AskForApproval::OnFailure),
sandbox_policy: Constrained::allow_any(SandboxPolicy::new_read_only_policy()),
enforce_residency: Constrained::allow_any(None),
network: None,
did_user_set_custom_approval_policy_or_sandbox_mode: true,
forced_auto_mode_downgraded_on_windows: false,
shell_environment_policy: ShellEnvironmentPolicy::default(),
+24 -5
View File
@@ -32,6 +32,7 @@ use crate::sandboxing::SandboxPermissions;
use crate::spawn::StdioPolicy;
use crate::spawn::spawn_child_async;
use crate::text_encoding::bytes_to_string_smart;
use codex_network_proxy::NetworkProxy;
use codex_utils_pty::process_group::kill_child_process_group;
pub const DEFAULT_EXEC_COMMAND_TIMEOUT_MS: u64 = 10_000;
@@ -63,6 +64,7 @@ pub struct ExecParams {
pub cwd: PathBuf,
pub expiration: ExecExpiration,
pub env: HashMap<String, String>,
pub network: Option<NetworkProxy>,
pub sandbox_permissions: SandboxPermissions,
pub windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel,
pub justification: Option<String>,
@@ -169,14 +171,17 @@ pub async fn process_exec_tool_call(
let ExecParams {
command,
cwd,
mut env,
expiration,
env,
network,
sandbox_permissions,
windows_sandbox_level,
justification,
arg0: _,
} = params;
if let Some(network) = network.as_ref() {
network.apply_to_env(&mut env);
}
let (program, args) = command.split_first().ok_or_else(|| {
CodexErr::Io(io::Error::new(
io::ErrorKind::InvalidInput,
@@ -208,18 +213,19 @@ pub async fn process_exec_tool_call(
.map_err(CodexErr::from)?;
// Route through the sandboxing module for a single, unified execution path.
crate::sandboxing::execute_env(exec_env, sandbox_policy, stdout_stream).await
crate::sandboxing::execute_env(exec_env, sandbox_policy, network, stdout_stream).await
}
pub(crate) async fn execute_exec_env(
env: ExecEnv,
sandbox_policy: &SandboxPolicy,
stdout_stream: Option<StdoutStream>,
network: Option<NetworkProxy>,
) -> Result<ExecToolCallOutput> {
let ExecEnv {
command,
cwd,
env,
mut env,
expiration,
sandbox,
windows_sandbox_level,
@@ -228,11 +234,16 @@ pub(crate) async fn execute_exec_env(
arg0,
} = env;
if let Some(network) = network.as_ref() {
network.apply_to_env(&mut env);
}
let params = ExecParams {
command,
cwd,
expiration,
env,
network,
sandbox_permissions,
windows_sandbox_level,
justification,
@@ -324,11 +335,16 @@ async fn exec_windows_sandbox(
let ExecParams {
command,
cwd,
env,
mut env,
network,
expiration,
windows_sandbox_level,
..
} = params;
if let Some(network) = network.as_ref() {
network.apply_to_env(&mut env);
}
// TODO(iceweasel-oai): run_windows_sandbox_capture should support all
// variants of ExecExpiration, not just timeout.
let timeout_ms = expiration.timeout_ms();
@@ -678,6 +694,7 @@ async fn exec(
command,
cwd,
env,
network: _,
arg0,
expiration,
windows_sandbox_level: _,
@@ -1061,6 +1078,7 @@ mod tests {
cwd: std::env::current_dir()?,
expiration: 500.into(),
env,
network: None,
sandbox_permissions: SandboxPermissions::UseDefault,
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
justification: None,
@@ -1107,6 +1125,7 @@ mod tests {
cwd: cwd.clone(),
expiration: ExecExpiration::Cancellation(cancel_token),
env,
network: None,
sandbox_permissions: SandboxPermissions::UseDefault,
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
justification: None,
+3 -1
View File
@@ -21,6 +21,7 @@ use crate::seatbelt::create_seatbelt_command_args;
use crate::spawn::CODEX_SANDBOX_ENV_VAR;
use crate::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
use crate::tools::sandboxing::SandboxablePreference;
use codex_network_proxy::NetworkProxy;
use codex_protocol::config_types::WindowsSandboxLevel;
pub use codex_protocol::models::SandboxPermissions;
use std::collections::HashMap;
@@ -206,7 +207,8 @@ impl SandboxManager {
pub async fn execute_env(
env: ExecEnv,
policy: &SandboxPolicy,
network: Option<NetworkProxy>,
stdout_stream: Option<StdoutStream>,
) -> crate::error::Result<ExecToolCallOutput> {
execute_exec_env(env, policy, stdout_stream).await
execute_exec_env(env, policy, stdout_stream, network).await
}
+8 -3
View File
@@ -160,9 +160,14 @@ pub(crate) async fn execute_user_shell_command(
});
let sandbox_policy = SandboxPolicy::DangerFullAccess;
let exec_result = execute_exec_env(exec_env, &sandbox_policy, stdout_stream)
.or_cancel(&cancellation_token)
.await;
let exec_result = execute_exec_env(
exec_env,
&sandbox_policy,
stdout_stream,
turn_context.config.network.clone(),
)
.or_cancel(&cancellation_token)
.await;
match exec_result {
Err(CancelErr::Cancelled) => {
@@ -53,6 +53,7 @@ impl ShellHandler {
cwd: turn_context.resolve_path(params.workdir.clone()),
expiration: params.timeout_ms.into(),
env: create_env(&turn_context.shell_environment_policy, Some(thread_id)),
network: turn_context.config.network.clone(),
sandbox_permissions: params.sandbox_permissions.unwrap_or_default(),
windows_sandbox_level: turn_context.windows_sandbox_level,
justification: params.justification.clone(),
@@ -81,6 +82,7 @@ impl ShellCommandHandler {
cwd: turn_context.resolve_path(params.workdir.clone()),
expiration: params.timeout_ms.into(),
env: create_env(&turn_context.shell_environment_policy, Some(thread_id)),
network: turn_context.config.network.clone(),
sandbox_permissions: params.sandbox_permissions.unwrap_or_default(),
windows_sandbox_level: turn_context.windows_sandbox_level,
justification: params.justification.clone(),
@@ -311,6 +313,7 @@ impl ShellHandler {
cwd: exec_params.cwd.clone(),
timeout_ms: exec_params.expiration.timeout_ms(),
env: exec_params.env.clone(),
network: exec_params.network.clone(),
sandbox_permissions: exec_params.sandbox_permissions,
justification: exec_params.justification.clone(),
exec_approval_requirement,
@@ -441,6 +444,7 @@ mod tests {
assert_eq!(exec_params.command, expected_command);
assert_eq!(exec_params.cwd, expected_cwd);
assert_eq!(exec_params.env, expected_env);
assert_eq!(exec_params.network, turn_context.config.network);
assert_eq!(exec_params.expiration.timeout_ms(), timeout_ms);
assert_eq!(exec_params.sandbox_permissions, sandbox_permissions);
assert_eq!(exec_params.justification, justification);
@@ -153,7 +153,7 @@ impl ToolRuntime<ApplyPatchRequest, ExecToolCallOutput> for ApplyPatchRuntime {
let env = attempt
.env_for(spec)
.map_err(|err| ToolError::Codex(err.into()))?;
let out = execute_env(env, attempt.policy, Self::stdout_stream(ctx))
let out = execute_env(env, attempt.policy, None, Self::stdout_stream(ctx))
.await
.map_err(ToolError::Codex)?;
Ok(out)
+10 -3
View File
@@ -23,6 +23,7 @@ use crate::tools::sandboxing::ToolCtx;
use crate::tools::sandboxing::ToolError;
use crate::tools::sandboxing::ToolRuntime;
use crate::tools::sandboxing::with_cached_approval;
use codex_network_proxy::NetworkProxy;
use codex_protocol::protocol::ReviewDecision;
use futures::future::BoxFuture;
use std::path::PathBuf;
@@ -33,6 +34,7 @@ pub struct ShellRequest {
pub cwd: PathBuf,
pub timeout_ms: Option<u64>,
pub env: std::collections::HashMap<String, String>,
pub network: Option<NetworkProxy>,
pub sandbox_permissions: SandboxPermissions,
pub justification: Option<String>,
pub exec_approval_requirement: ExecApprovalRequirement,
@@ -166,9 +168,14 @@ impl ToolRuntime<ShellRequest, ExecToolCallOutput> for ShellRuntime {
let env = attempt
.env_for(spec)
.map_err(|err| ToolError::Codex(err.into()))?;
let out = execute_env(env, attempt.policy, Self::stdout_stream(ctx))
.await
.map_err(ToolError::Codex)?;
let out = execute_env(
env,
attempt.policy,
req.network.clone(),
Self::stdout_stream(ctx),
)
.await
.map_err(ToolError::Codex)?;
Ok(out)
}
}
@@ -27,6 +27,7 @@ use crate::tools::sandboxing::with_cached_approval;
use crate::unified_exec::UnifiedExecError;
use crate::unified_exec::UnifiedExecProcess;
use crate::unified_exec::UnifiedExecProcessManager;
use codex_network_proxy::NetworkProxy;
use codex_protocol::protocol::ReviewDecision;
use futures::future::BoxFuture;
use std::collections::HashMap;
@@ -37,6 +38,7 @@ pub struct UnifiedExecRequest {
pub command: Vec<String>,
pub cwd: PathBuf,
pub env: HashMap<String, String>,
pub network: Option<NetworkProxy>,
pub tty: bool,
pub sandbox_permissions: SandboxPermissions,
pub justification: Option<String>,
@@ -55,28 +57,6 @@ pub struct UnifiedExecRuntime<'a> {
manager: &'a UnifiedExecProcessManager,
}
impl UnifiedExecRequest {
pub fn new(
command: Vec<String>,
cwd: PathBuf,
env: HashMap<String, String>,
tty: bool,
sandbox_permissions: SandboxPermissions,
justification: Option<String>,
exec_approval_requirement: ExecApprovalRequirement,
) -> Self {
Self {
command,
cwd,
env,
tty,
sandbox_permissions,
justification,
exec_approval_requirement,
}
}
}
impl<'a> UnifiedExecRuntime<'a> {
pub fn new(manager: &'a UnifiedExecProcessManager) -> Self {
Self { manager }
@@ -181,10 +161,14 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
command
};
let mut env = req.env.clone();
if let Some(network) = req.network.as_ref() {
network.apply_to_env(&mut env);
}
let spec = build_command_spec(
&command,
&req.cwd,
&req.env,
&env,
ExecExpiration::DefaultTimeout,
req.sandbox_permissions,
req.justification.clone(),
@@ -501,15 +501,16 @@ impl UnifiedExecProcessManager {
prefix_rule: request.prefix_rule.clone(),
})
.await;
let req = UnifiedExecToolRequest::new(
request.command.clone(),
let req = UnifiedExecToolRequest {
command: request.command.clone(),
cwd,
env,
request.tty,
request.sandbox_permissions,
request.justification.clone(),
network: context.turn.config.network.clone(),
tty: request.tty,
sandbox_permissions: request.sandbox_permissions,
justification: request.justification.clone(),
exec_approval_requirement,
);
};
let tool_ctx = ToolCtx {
session: context.session.as_ref(),
turn: context.turn.as_ref(),