mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Preserve Windows sandbox proxy settings in guardian sessions (#35036)
## Why Guardian review commands can run without the proxy-port environment used to configure the parent session. Reconciling persistent Windows sandbox settings for those commands can discard the parent's proxy configuration. ## What changed - Add a session-level Windows sandbox proxy-settings mode and use `Preserve` for guardian review sessions while keeping `Reconcile` as the default. - Carry the mode through unified exec and the exec-server sandbox context to Windows process launches. - In preserve mode, reuse the proxy settings recorded by the existing sandbox setup marker. ## Testing Added coverage for guardian session configuration, exec-server transport, and preserving an existing setup marker when proxy-port environment variables are absent. GitOrigin-RevId: 214655c6e6d97360906431773dc0de0fdda2db6e
This commit is contained in:
@@ -87,6 +87,7 @@ pub(crate) async fn run_codex_thread_interactive(
|
||||
subagent_source: SubAgentSource,
|
||||
initial_history: Option<InitialHistory>,
|
||||
git_enrichment_policy: GitEnrichmentPolicy,
|
||||
windows_sandbox_proxy_settings_mode: codex_sandboxing::WindowsSandboxProxySettingsMode,
|
||||
) -> Result<(Arc<Session>, SessionIo), CodexErr> {
|
||||
let (tx_sub, rx_sub) = async_channel::bounded(SUBMISSION_CHANNEL_CAPACITY);
|
||||
let (tx_ops, rx_ops) = async_channel::bounded(SUBMISSION_CHANNEL_CAPACITY);
|
||||
@@ -139,6 +140,7 @@ pub(crate) async fn run_codex_thread_interactive(
|
||||
external_time_provider: Some(Arc::clone(&parent_session.services.time_provider)),
|
||||
inherited_multi_agent_version: Some(MultiAgentVersion::Disabled),
|
||||
git_enrichment_policy,
|
||||
windows_sandbox_proxy_settings_mode,
|
||||
}))
|
||||
.or_cancel(&cancel_token)
|
||||
.await??;
|
||||
@@ -224,6 +226,7 @@ pub(crate) async fn run_codex_thread_one_shot(
|
||||
subagent_source,
|
||||
initial_history,
|
||||
GitEnrichmentPolicy::Fresh,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
))
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -212,6 +212,7 @@ async fn run_codex_thread_interactive_respects_pre_cancelled_spawn() {
|
||||
SubAgentSource::Review,
|
||||
/*initial_history*/ None,
|
||||
crate::session::GitEnrichmentPolicy::Fresh,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -680,6 +680,7 @@ async fn spawn_guardian_review_session(
|
||||
SubAgentSource::Other(GUARDIAN_REVIEWER_NAME.to_string()),
|
||||
initial_history,
|
||||
GitEnrichmentPolicy::Skip,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Preserve,
|
||||
))
|
||||
.await?;
|
||||
|
||||
@@ -1254,6 +1255,31 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn spawned_guardian_session_preserves_windows_sandbox_proxy_settings() {
|
||||
let params = test_review_params().await;
|
||||
let manager = GuardianReviewSessionManager::default();
|
||||
manager
|
||||
.initialize(params.parent_session, params.parent_turn)
|
||||
.await
|
||||
.expect("initialize Guardian session");
|
||||
let mode = manager
|
||||
.state
|
||||
.lock()
|
||||
.await
|
||||
.trunk
|
||||
.as_ref()
|
||||
.expect("Guardian session")
|
||||
.session
|
||||
.windows_sandbox_proxy_settings_mode;
|
||||
|
||||
assert_eq!(
|
||||
mode,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Preserve
|
||||
);
|
||||
manager.shutdown().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn guardian_review_session_config_change_invalidates_cached_session() {
|
||||
let parent_config = crate::config::test_config().await;
|
||||
|
||||
@@ -455,6 +455,8 @@ pub(crate) struct SessionSpawnArgs {
|
||||
pub(crate) external_time_provider: Option<Arc<dyn TimeProvider>>,
|
||||
pub(crate) inherited_multi_agent_version: Option<MultiAgentVersion>,
|
||||
pub(crate) git_enrichment_policy: GitEnrichmentPolicy,
|
||||
pub(crate) windows_sandbox_proxy_settings_mode:
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode,
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_multi_agent_version(
|
||||
@@ -544,6 +546,7 @@ impl Session {
|
||||
external_time_provider,
|
||||
inherited_multi_agent_version,
|
||||
git_enrichment_policy,
|
||||
windows_sandbox_proxy_settings_mode,
|
||||
} = args;
|
||||
let (tx_sub, rx_sub) = async_channel::bounded(SUBMISSION_CHANNEL_CAPACITY);
|
||||
let (tx_event, rx_event) = async_channel::unbounded();
|
||||
@@ -732,6 +735,7 @@ impl Session {
|
||||
external_time_provider,
|
||||
multi_agent_version,
|
||||
git_enrichment_policy,
|
||||
windows_sandbox_proxy_settings_mode,
|
||||
))
|
||||
.await
|
||||
.map_err(|e| {
|
||||
|
||||
@@ -37,6 +37,8 @@ pub(crate) struct Session {
|
||||
/// The set of enabled features should be invariant for the lifetime of the
|
||||
/// session.
|
||||
pub(super) features: ManagedFeatures,
|
||||
pub(crate) windows_sandbox_proxy_settings_mode:
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode,
|
||||
pub(super) multi_agent_version: OnceLock<MultiAgentVersion>,
|
||||
pub(super) mcp_refresh_pending: std::sync::atomic::AtomicBool,
|
||||
/// Serializes runtime refreshes without blocking calls that own a snapshot.
|
||||
@@ -514,6 +516,7 @@ impl Session {
|
||||
external_time_provider: Option<Arc<dyn TimeProvider>>,
|
||||
multi_agent_version: Option<MultiAgentVersion>,
|
||||
git_enrichment_policy: GitEnrichmentPolicy,
|
||||
windows_sandbox_proxy_settings_mode: codex_sandboxing::WindowsSandboxProxySettingsMode,
|
||||
) -> anyhow::Result<Arc<Self>> {
|
||||
debug!(
|
||||
"Configuring session: model={}; provider={:?}",
|
||||
@@ -1150,6 +1153,7 @@ impl Session {
|
||||
state: Mutex::new(state),
|
||||
managed_network_proxy_refresh_lock: Semaphore::new(/*permits*/ 1),
|
||||
features: config.features.clone(),
|
||||
windows_sandbox_proxy_settings_mode,
|
||||
multi_agent_version,
|
||||
mcp_refresh_pending: std::sync::atomic::AtomicBool::new(false),
|
||||
mcp_refresh_lock: Semaphore::new(/*permits*/ 1),
|
||||
|
||||
@@ -5288,6 +5288,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_packaged_zsh() {
|
||||
/*external_time_provider*/ None,
|
||||
Some(config.multi_agent_version_from_features()),
|
||||
GitEnrichmentPolicy::Fresh,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -5539,6 +5540,8 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
state: Mutex::new(state),
|
||||
managed_network_proxy_refresh_lock: Semaphore::new(/*permits*/ 1),
|
||||
features: config.features.clone(),
|
||||
windows_sandbox_proxy_settings_mode:
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
multi_agent_version: OnceLock::from(config.multi_agent_version_from_features()),
|
||||
mcp_refresh_pending: std::sync::atomic::AtomicBool::new(true),
|
||||
mcp_refresh_lock: Semaphore::new(/*permits*/ 1),
|
||||
@@ -5673,6 +5676,7 @@ async fn make_session_with_config_and_rx(
|
||||
/*external_time_provider*/ None,
|
||||
Some(config.multi_agent_version_from_features()),
|
||||
GitEnrichmentPolicy::Fresh,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -5788,6 +5792,7 @@ async fn make_session_with_history_source_and_agent_control_and_rx(
|
||||
/*external_time_provider*/ None,
|
||||
Some(config.multi_agent_version_from_features()),
|
||||
GitEnrichmentPolicy::Fresh,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -7702,6 +7707,8 @@ where
|
||||
state: Mutex::new(state),
|
||||
managed_network_proxy_refresh_lock: Semaphore::new(/*permits*/ 1),
|
||||
features: config.features.clone(),
|
||||
windows_sandbox_proxy_settings_mode:
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
multi_agent_version: OnceLock::from(config.multi_agent_version_from_features()),
|
||||
mcp_refresh_pending: std::sync::atomic::AtomicBool::new(true),
|
||||
mcp_refresh_lock: Semaphore::new(/*permits*/ 1),
|
||||
|
||||
@@ -766,6 +766,8 @@ async fn guardian_subagent_does_not_inherit_parent_exec_policy_rules() {
|
||||
external_time_provider: None,
|
||||
inherited_multi_agent_version: None,
|
||||
git_enrichment_policy: GitEnrichmentPolicy::Skip,
|
||||
windows_sandbox_proxy_settings_mode:
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Preserve,
|
||||
})
|
||||
.await
|
||||
.expect("spawn guardian subagent");
|
||||
|
||||
@@ -360,6 +360,7 @@ impl TurnContext {
|
||||
.config
|
||||
.permissions
|
||||
.windows_sandbox_private_desktop,
|
||||
windows_sandbox_proxy_settings_mode: None,
|
||||
use_legacy_landlock: self.config.features.use_legacy_landlock(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1676,6 +1676,8 @@ impl ThreadManagerState {
|
||||
external_time_provider: self.external_time_provider.clone(),
|
||||
inherited_multi_agent_version: multi_agent_version,
|
||||
git_enrichment_policy: GitEnrichmentPolicy::Fresh,
|
||||
windows_sandbox_proxy_settings_mode:
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
}))
|
||||
.await?;
|
||||
let new_thread = self
|
||||
|
||||
@@ -99,6 +99,7 @@ impl ApplyPatchRuntime {
|
||||
workspace_roots: attempt.workspace_roots.to_vec(),
|
||||
windows_sandbox_level: attempt.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: attempt.windows_sandbox_private_desktop,
|
||||
windows_sandbox_proxy_settings_mode: None,
|
||||
use_legacy_landlock: attempt.use_legacy_landlock,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -302,6 +302,7 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
|
||||
ctx: &ToolCtx,
|
||||
) -> Result<UnifiedExecProcess, ToolError> {
|
||||
let base_command = &req.command;
|
||||
let windows_sandbox_proxy_settings_mode = ctx.session.windows_sandbox_proxy_settings_mode;
|
||||
let session_shell = ctx.session.user_shell();
|
||||
let shell = req
|
||||
.turn_environment
|
||||
@@ -464,6 +465,7 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
|
||||
.open_session_with_prepared_exec_env(
|
||||
req.process_id,
|
||||
&prepared.exec_request,
|
||||
windows_sandbox_proxy_settings_mode,
|
||||
req.tty,
|
||||
prepared.spawn_lifecycle,
|
||||
req.turn_environment.environment.as_ref(),
|
||||
@@ -510,6 +512,7 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecProcess> for UnifiedExecRunt
|
||||
network_proxy_launch,
|
||||
/*environment_id*/ Some(&req.turn_environment.environment_id),
|
||||
req.exec_server_env_config.clone(),
|
||||
windows_sandbox_proxy_settings_mode,
|
||||
req.tty,
|
||||
Box::new(NoopSpawnLifecycle),
|
||||
req.turn_environment.environment.as_ref(),
|
||||
|
||||
@@ -507,6 +507,7 @@ impl<'a> SandboxAttempt<'a> {
|
||||
workspace_roots: self.workspace_roots.to_vec(),
|
||||
windows_sandbox_level: self.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
||||
windows_sandbox_proxy_settings_mode: None,
|
||||
use_legacy_landlock: self.use_legacy_landlock,
|
||||
});
|
||||
exec_request.exec_server_enforce_managed_network = self.enforce_managed_network;
|
||||
|
||||
@@ -268,6 +268,7 @@ fn exec_server_env_keeps_command_native_and_carries_sandbox_context() {
|
||||
workspace_roots: vec![cwd_uri.clone()],
|
||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
windows_sandbox_proxy_settings_mode: None,
|
||||
use_legacy_landlock: false,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -113,6 +113,7 @@ async fn exec_command_with_tty(
|
||||
.open_session_with_prepared_exec_env(
|
||||
process_id,
|
||||
&request,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
tty,
|
||||
Box::new(NoopSpawnLifecycle),
|
||||
turn.environments
|
||||
@@ -753,6 +754,7 @@ async fn completed_pipe_commands_preserve_exit_code() -> anyhow::Result<()> {
|
||||
.open_session_with_prepared_exec_env(
|
||||
/*process_id*/ 1234,
|
||||
&request,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
/*tty*/ false,
|
||||
Box::new(NoopSpawnLifecycle),
|
||||
&environment,
|
||||
@@ -793,6 +795,7 @@ async fn unified_exec_uses_remote_exec_server_when_configured() -> anyhow::Resul
|
||||
.open_session_with_prepared_exec_env(
|
||||
/*process_id*/ 1234,
|
||||
&request,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
/*tty*/ true,
|
||||
Box::new(NoopSpawnLifecycle),
|
||||
remote_test_env.environment(),
|
||||
@@ -851,6 +854,7 @@ async fn remote_exec_server_rejects_inherited_fd_launches() -> anyhow::Result<()
|
||||
.open_session_with_prepared_exec_env(
|
||||
/*process_id*/ 1234,
|
||||
&request,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
/*tty*/ true,
|
||||
Box::new(TestSpawnLifecycle {
|
||||
inherited_fds: vec![42],
|
||||
|
||||
@@ -177,9 +177,14 @@ fn exec_server_env_for_request(
|
||||
fn exec_server_params_for_request(
|
||||
process_id: i32,
|
||||
request: &ExecRequest,
|
||||
windows_sandbox_proxy_settings_mode: codex_sandboxing::WindowsSandboxProxySettingsMode,
|
||||
tty: bool,
|
||||
) -> codex_exec_server::ExecParams {
|
||||
let (env_policy, env) = exec_server_env_for_request(request);
|
||||
let sandbox = request.exec_server_sandbox.clone().map(|mut sandbox| {
|
||||
sandbox.windows_sandbox_proxy_settings_mode = Some(windows_sandbox_proxy_settings_mode);
|
||||
sandbox
|
||||
});
|
||||
// Sandbox retries reuse the unified-exec ID but start a distinct executor process.
|
||||
let exec_server_process_id = if request.exec_server_sandbox.is_some() {
|
||||
format!("{process_id}-{}", Uuid::new_v4())
|
||||
@@ -195,7 +200,7 @@ fn exec_server_params_for_request(
|
||||
tty,
|
||||
pipe_stdin: false,
|
||||
arg0: request.arg0.clone(),
|
||||
sandbox: request.exec_server_sandbox.clone(),
|
||||
sandbox,
|
||||
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(),
|
||||
@@ -1003,6 +1008,7 @@ impl UnifiedExecProcessManager {
|
||||
network_proxy_launch: Option<codex_network_proxy::RemoteNetworkProxyLaunchConfig>,
|
||||
environment_id: Option<&str>,
|
||||
exec_server_env_config: Option<ExecServerEnvConfig>,
|
||||
windows_sandbox_proxy_settings_mode: codex_sandboxing::WindowsSandboxProxySettingsMode,
|
||||
tty: bool,
|
||||
spawn_lifecycle: SpawnLifecycleHandle,
|
||||
environment: &codex_exec_server::Environment,
|
||||
@@ -1018,6 +1024,7 @@ impl UnifiedExecProcessManager {
|
||||
self.open_session_with_prepared_exec_env(
|
||||
process_id,
|
||||
&request,
|
||||
windows_sandbox_proxy_settings_mode,
|
||||
tty,
|
||||
spawn_lifecycle,
|
||||
environment,
|
||||
@@ -1038,6 +1045,7 @@ impl UnifiedExecProcessManager {
|
||||
&self,
|
||||
process_id: i32,
|
||||
request: &ExecRequest,
|
||||
windows_sandbox_proxy_settings_mode: codex_sandboxing::WindowsSandboxProxySettingsMode,
|
||||
tty: bool,
|
||||
mut spawn_lifecycle: SpawnLifecycleHandle,
|
||||
environment: &codex_exec_server::Environment,
|
||||
@@ -1053,7 +1061,12 @@ impl UnifiedExecProcessManager {
|
||||
|
||||
let started = environment
|
||||
.get_exec_backend()
|
||||
.start(exec_server_params_for_request(process_id, request, tty))
|
||||
.start(exec_server_params_for_request(
|
||||
process_id,
|
||||
request,
|
||||
windows_sandbox_proxy_settings_mode,
|
||||
tty,
|
||||
))
|
||||
.await
|
||||
.map_err(|err| UnifiedExecError::create_process(err.to_string()))?;
|
||||
spawn_lifecycle.after_spawn();
|
||||
@@ -1100,22 +1113,21 @@ impl UnifiedExecProcessManager {
|
||||
None::<String>
|
||||
}
|
||||
};
|
||||
let windows_sandbox = if request.sandbox
|
||||
== codex_sandboxing::SandboxType::WindowsRestrictedToken
|
||||
{
|
||||
Some(codex_sandboxing::WindowsSandboxSpawnRequest {
|
||||
permission_profile: &request.permission_profile,
|
||||
workspace_roots: &request.windows_sandbox_workspace_roots,
|
||||
windows_sandbox_level: request.windows_sandbox_level,
|
||||
proxy_enforced: request.network.is_some(),
|
||||
network_proxy_restricting_sid: network_proxy_restricting_sid.as_deref(),
|
||||
proxy_settings_mode: codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
filesystem_overrides: request.windows_sandbox_filesystem_overrides.as_ref(),
|
||||
use_private_desktop: request.windows_sandbox_private_desktop,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let windows_sandbox =
|
||||
if request.sandbox == codex_sandboxing::SandboxType::WindowsRestrictedToken {
|
||||
Some(codex_sandboxing::WindowsSandboxSpawnRequest {
|
||||
permission_profile: &request.permission_profile,
|
||||
workspace_roots: &request.windows_sandbox_workspace_roots,
|
||||
windows_sandbox_level: request.windows_sandbox_level,
|
||||
proxy_enforced: request.network.is_some(),
|
||||
network_proxy_restricting_sid: network_proxy_restricting_sid.as_deref(),
|
||||
proxy_settings_mode: windows_sandbox_proxy_settings_mode,
|
||||
filesystem_overrides: request.windows_sandbox_filesystem_overrides.as_ref(),
|
||||
use_private_desktop: request.windows_sandbox_private_desktop,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let spawn_result = codex_sandboxing::spawn_process(codex_sandboxing::SpawnRequest {
|
||||
command: &request.command,
|
||||
cwd: native_cwd.as_path(),
|
||||
|
||||
@@ -177,8 +177,16 @@ fn exec_server_params_use_path_uri_and_env_policy_overlay_contract() {
|
||||
exec_server_network_proxy: None,
|
||||
};
|
||||
|
||||
let params =
|
||||
exec_server_params_for_request(/*process_id*/ 123, &request, /*tty*/ true);
|
||||
let proxy_settings_mode = codex_sandboxing::WindowsSandboxProxySettingsMode::Preserve;
|
||||
let params_for_request = |request: &ExecRequest| {
|
||||
exec_server_params_for_request(
|
||||
/*process_id*/ 123,
|
||||
request,
|
||||
proxy_settings_mode,
|
||||
/*tty*/ true,
|
||||
)
|
||||
};
|
||||
let params = params_for_request(&request);
|
||||
|
||||
assert_eq!(params.process_id.as_str(), "123");
|
||||
assert_eq!(params.cwd, request.cwd);
|
||||
@@ -200,10 +208,15 @@ fn exec_server_params_use_path_uri_and_env_policy_overlay_contract() {
|
||||
request.exec_server_sandbox = Some(
|
||||
codex_exec_server::FileSystemSandboxContext::from_permission_profile(permission_profile),
|
||||
);
|
||||
let first =
|
||||
exec_server_params_for_request(/*process_id*/ 123, &request, /*tty*/ true);
|
||||
let second =
|
||||
exec_server_params_for_request(/*process_id*/ 123, &request, /*tty*/ true);
|
||||
let first = params_for_request(&request);
|
||||
let second = params_for_request(&request);
|
||||
assert_eq!(
|
||||
first
|
||||
.sandbox
|
||||
.as_ref()
|
||||
.and_then(|sandbox| sandbox.windows_sandbox_proxy_settings_mode),
|
||||
Some(codex_sandboxing::WindowsSandboxProxySettingsMode::Preserve)
|
||||
);
|
||||
assert!(first.process_id.as_str().starts_with("123-"));
|
||||
assert!(second.process_id.as_str().starts_with("123-"));
|
||||
assert_ne!(first.process_id, second.process_id);
|
||||
|
||||
@@ -743,6 +743,7 @@ mod tests {
|
||||
use codex_network_proxy::NetworkProxyConfig;
|
||||
use codex_network_proxy::RemoteNetworkProxyConfig;
|
||||
use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
|
||||
use codex_protocol::config_types::WindowsSandboxProxySettingsMode;
|
||||
use codex_protocol::models::ManagedFileSystemPermissions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::permissions::FileSystemAccessMode;
|
||||
@@ -938,6 +939,17 @@ mod tests {
|
||||
.expect("deserialize sandbox"),
|
||||
sandbox
|
||||
);
|
||||
let preserve = FileSystemSandboxContext {
|
||||
windows_sandbox_proxy_settings_mode: Some(WindowsSandboxProxySettingsMode::Preserve),
|
||||
..sandbox
|
||||
};
|
||||
let serialized = serde_json::to_value(&preserve).expect("serialize preserve mode");
|
||||
assert_eq!(serialized["windowsSandboxProxySettingsMode"], "preserve");
|
||||
assert_eq!(
|
||||
serde_json::from_value::<FileSystemSandboxContext>(serialized)
|
||||
.expect("deserialize preserve mode"),
|
||||
preserve
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -52,6 +52,7 @@ struct PreparedWindowsSandboxRequest {
|
||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel,
|
||||
proxy_enforced: bool,
|
||||
network_proxy_restricting_sid: Option<String>,
|
||||
proxy_settings_mode: WindowsSandboxProxySettingsMode,
|
||||
filesystem_overrides: Option<WindowsSandboxFilesystemOverrides>,
|
||||
use_private_desktop: bool,
|
||||
}
|
||||
@@ -66,7 +67,7 @@ impl PreparedExecRequest {
|
||||
windows_sandbox_level: request.windows_sandbox_level,
|
||||
proxy_enforced: request.proxy_enforced,
|
||||
network_proxy_restricting_sid: request.network_proxy_restricting_sid.as_deref(),
|
||||
proxy_settings_mode: WindowsSandboxProxySettingsMode::Reconcile,
|
||||
proxy_settings_mode: request.proxy_settings_mode,
|
||||
filesystem_overrides: request.filesystem_overrides.as_ref(),
|
||||
use_private_desktop: request.use_private_desktop,
|
||||
})
|
||||
@@ -114,6 +115,9 @@ pub(crate) async fn prepare_exec_request(
|
||||
windows_sandbox: None,
|
||||
});
|
||||
};
|
||||
let windows_sandbox_proxy_settings_mode = sandbox_context
|
||||
.windows_sandbox_proxy_settings_mode
|
||||
.unwrap_or_default();
|
||||
let runtime_paths = runtime_paths
|
||||
.ok_or_else(|| invalid_params("sandbox runtime paths are not configured".to_string()))?;
|
||||
// TODO(jif): Transport permissions before orchestrator-local paths are materialized,
|
||||
@@ -212,8 +216,7 @@ pub(crate) async fn prepare_exec_request(
|
||||
let (program, args) = (program.into(), args.to_vec());
|
||||
let transform_request = SandboxDirectSpawnTransformRequest {
|
||||
workspace_roots,
|
||||
windows_sandbox_proxy_settings_mode:
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
windows_sandbox_proxy_settings_mode,
|
||||
transform: SandboxTransformRequest {
|
||||
command: SandboxCommand {
|
||||
program,
|
||||
@@ -271,6 +274,7 @@ pub(crate) async fn prepare_exec_request(
|
||||
windows_sandbox_level: sandbox_context.windows_sandbox_level,
|
||||
proxy_enforced,
|
||||
network_proxy_restricting_sid,
|
||||
proxy_settings_mode: windows_sandbox_proxy_settings_mode,
|
||||
filesystem_overrides,
|
||||
use_private_desktop: sandbox_context.windows_sandbox_private_desktop,
|
||||
})
|
||||
|
||||
@@ -394,6 +394,8 @@ async fn managed_network_selects_elevated_windows_spawn() {
|
||||
cwd_uri.clone(),
|
||||
);
|
||||
sandbox.windows_sandbox_level = WindowsSandboxLevel::RestrictedToken;
|
||||
sandbox.windows_sandbox_proxy_settings_mode =
|
||||
Some(codex_sandboxing::WindowsSandboxProxySettingsMode::Preserve);
|
||||
let proxy_config = RemoteNetworkProxyConfig::from_effective_config(&NetworkProxyConfig {
|
||||
enabled: true,
|
||||
enable_socks5: false,
|
||||
@@ -434,6 +436,10 @@ async fn managed_network_selects_elevated_windows_spawn() {
|
||||
);
|
||||
assert!(spawn.proxy_enforced);
|
||||
assert!(spawn.network_proxy_restricting_sid.is_some());
|
||||
assert_eq!(
|
||||
spawn.proxy_settings_mode,
|
||||
codex_sandboxing::WindowsSandboxProxySettingsMode::Preserve
|
||||
);
|
||||
assert_eq!(spawn.permission_profile, &permissions);
|
||||
assert_eq!(spawn.workspace_roots, std::slice::from_ref(&cwd));
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ mod find_up;
|
||||
|
||||
use bytes::Bytes;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::config_types::WindowsSandboxProxySettingsMode;
|
||||
use codex_protocol::models::ManagedFileSystemPermissions;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::models::SandboxEnforcement;
|
||||
@@ -291,6 +292,8 @@ pub struct FileSystemSandboxContext {
|
||||
pub windows_sandbox_level: WindowsSandboxLevel,
|
||||
#[serde(default)]
|
||||
pub windows_sandbox_private_desktop: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub windows_sandbox_proxy_settings_mode: Option<WindowsSandboxProxySettingsMode>,
|
||||
#[serde(default)]
|
||||
pub use_legacy_landlock: bool,
|
||||
}
|
||||
@@ -332,6 +335,7 @@ impl FileSystemSandboxContext {
|
||||
workspace_roots,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
windows_sandbox_proxy_settings_mode: None,
|
||||
use_legacy_landlock: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,6 +283,16 @@ pub enum WindowsSandboxLevel {
|
||||
Elevated,
|
||||
}
|
||||
|
||||
/// Controls whether a Windows sandbox launch reconciles persistent proxy settings or preserves
|
||||
/// the settings established by another launch.
|
||||
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum WindowsSandboxProxySettingsMode {
|
||||
#[default]
|
||||
Reconcile,
|
||||
Preserve,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Serialize,
|
||||
|
||||
@@ -371,4 +371,36 @@ mod tests {
|
||||
vec![8080]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn guardian_preserve_mode_does_not_churn_marker_with_empty_proxy_ports() {
|
||||
let marker = SetupMarker {
|
||||
version: crate::setup::SETUP_VERSION,
|
||||
offline_username: "offline".to_string(),
|
||||
online_username: "online".to_string(),
|
||||
created_at: None,
|
||||
proxy_ports: vec![3128, 8081],
|
||||
allow_local_binding: true,
|
||||
};
|
||||
let env_map = HashMap::new();
|
||||
let reconciled = desired_offline_proxy_settings(
|
||||
Some(&marker),
|
||||
WindowsSandboxProxySettingsMode::Reconcile,
|
||||
&env_map,
|
||||
SandboxNetworkIdentity::Offline,
|
||||
);
|
||||
assert_eq!(reconciled.proxy_ports, Vec::<u16>::new());
|
||||
|
||||
let desired = desired_offline_proxy_settings(
|
||||
Some(&marker),
|
||||
WindowsSandboxProxySettingsMode::Preserve,
|
||||
&env_map,
|
||||
SandboxNetworkIdentity::Offline,
|
||||
);
|
||||
assert_eq!(desired, marker.offline_proxy_settings());
|
||||
assert_eq!(
|
||||
marker.request_mismatch_reason(SandboxNetworkIdentity::Offline, &desired),
|
||||
None
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,14 +35,7 @@ impl fmt::Debug for WindowsSandboxCancellationToken {
|
||||
}
|
||||
}
|
||||
|
||||
/// Controls whether a Windows sandbox launch reconciles persistent proxy
|
||||
/// firewall settings or preserves the settings established by another launch.
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||
pub enum WindowsSandboxProxySettingsMode {
|
||||
#[default]
|
||||
Reconcile,
|
||||
Preserve,
|
||||
}
|
||||
pub use codex_protocol::config_types::WindowsSandboxProxySettingsMode;
|
||||
|
||||
/// Network settings installed by an administrator during managed Windows sandbox setup.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
|
||||
Reference in New Issue
Block a user