Add Windows sandbox unified exec runtime support (#15578)

## Summary

This is the runtime/foundation half of the Windows sandbox unified-exec
work.

- add Windows sandbox `unified_exec` session support in
`windows-sandbox-rs` for both:
  - the legacy restricted-token backend
  - the elevated runner backend
- extend the PTY/process runtime so driver-backed sessions can support:
  - stdin streaming
  - stdout/stderr separation
  - exit propagation
  - PTY resize hooks
- add Windows sandbox runtime coverage in `codex-windows-sandbox` /
`codex-utils-pty`

This PR does **not** enable Windows sandbox `UnifiedExec` for product
callers yet because hooking this up to app-server comes in the next PR.

Windows sandbox advertising is intentionally kept aligned with `main`,
so sandboxed Windows callers still fall back to `ShellCommand`.

This PR isolates the runtime/session layer so it can be reviewed
independently from product-surface enablement.

---------

Co-authored-by: jif-oai <jif@openai.com>
Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
iceweasel-oai
2026-04-21 10:44:49 -07:00
committed by GitHub
Unverified
parent 38ba876ea9
commit 8612714aa6
32 changed files with 2620 additions and 210 deletions
+6 -1
View File
@@ -314,7 +314,11 @@ pub fn build_exec_request(
windows_sandbox_level,
windows_sandbox_private_desktop,
})
.map(|request| ExecRequest::from_sandbox_exec_request(request, options))
.map(|request| {
let windows_sandbox_policy_cwd = AbsolutePathBuf::try_from(sandbox_cwd.to_path_buf())
.unwrap_or_else(|_| request.cwd.clone());
ExecRequest::from_sandbox_exec_request(request, options, windows_sandbox_policy_cwd)
})
.map_err(CodexErr::from)?;
let use_windows_elevated_backend = windows_sandbox_uses_elevated_backend(
exec_req.windows_sandbox_level,
@@ -357,6 +361,7 @@ pub(crate) async fn execute_exec_request(
expiration,
capture_policy,
sandbox,
windows_sandbox_policy_cwd: _,
windows_sandbox_level,
windows_sandbox_private_desktop,
sandbox_policy,
+5
View File
@@ -49,6 +49,7 @@ pub struct ExecRequest {
pub expiration: ExecExpiration,
pub capture_policy: ExecCapturePolicy,
pub sandbox: SandboxType,
pub windows_sandbox_policy_cwd: AbsolutePathBuf,
pub windows_sandbox_level: WindowsSandboxLevel,
pub windows_sandbox_private_desktop: bool,
pub sandbox_policy: SandboxPolicy,
@@ -75,6 +76,7 @@ impl ExecRequest {
network_sandbox_policy: NetworkSandboxPolicy,
arg0: Option<String>,
) -> Self {
let windows_sandbox_policy_cwd = cwd.clone();
Self {
command,
cwd,
@@ -84,6 +86,7 @@ impl ExecRequest {
expiration,
capture_policy,
sandbox,
windows_sandbox_policy_cwd,
windows_sandbox_level,
windows_sandbox_private_desktop,
sandbox_policy,
@@ -97,6 +100,7 @@ impl ExecRequest {
pub(crate) fn from_sandbox_exec_request(
request: SandboxExecRequest,
options: ExecOptions,
windows_sandbox_policy_cwd: AbsolutePathBuf,
) -> Self {
let SandboxExecRequest {
command,
@@ -134,6 +138,7 @@ impl ExecRequest {
expiration,
capture_policy,
sandbox,
windows_sandbox_policy_cwd,
windows_sandbox_level,
windows_sandbox_private_desktop,
sandbox_policy,
+1
View File
@@ -171,6 +171,7 @@ pub(crate) async fn execute_user_shell_command(
expiration: USER_SHELL_TIMEOUT_MS.into(),
capture_policy: ExecCapturePolicy::ShellTool,
sandbox: SandboxType::None,
windows_sandbox_policy_cwd: cwd.clone(),
windows_sandbox_level: turn_context.windows_sandbox_level,
windows_sandbox_private_desktop: turn_context
.config
+5 -1
View File
@@ -1091,7 +1091,11 @@ impl JsReplManager {
.windows_sandbox_private_desktop,
})
.map(|request| {
crate::sandboxing::ExecRequest::from_sandbox_exec_request(request, options)
crate::sandboxing::ExecRequest::from_sandbox_exec_request(
request,
options,
turn.cwd.clone(),
)
})
.map_err(|err| format!("failed to configure sandbox for js_repl: {err}"))?;
@@ -134,6 +134,7 @@ pub(super) async fn try_run_zsh_fork(
expiration: _sandbox_expiration,
capture_policy: _capture_policy,
sandbox,
windows_sandbox_policy_cwd: sandbox_policy_cwd,
windows_sandbox_level,
windows_sandbox_private_desktop: _windows_sandbox_private_desktop,
sandbox_policy,
@@ -161,7 +162,7 @@ pub(super) async fn try_run_zsh_fork(
network: sandbox_network,
windows_sandbox_level,
arg0,
sandbox_policy_cwd: ctx.turn.cwd.clone(),
sandbox_policy_cwd,
codex_linux_sandbox_exe: ctx.turn.codex_linux_sandbox_exe.clone(),
use_legacy_landlock: ctx.turn.features.use_legacy_landlock(),
};
@@ -785,6 +786,7 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
expiration: ExecExpiration::Cancellation(cancel_rx),
capture_policy: ExecCapturePolicy::ShellTool,
sandbox: self.sandbox,
windows_sandbox_policy_cwd: self.sandbox_policy_cwd.clone(),
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: false,
sandbox_policy: self.sandbox_policy.clone(),
@@ -924,8 +926,11 @@ impl CoreShellCommandExecutor {
windows_sandbox_level: self.windows_sandbox_level,
windows_sandbox_private_desktop: false,
})?;
let mut exec_request =
crate::sandboxing::ExecRequest::from_sandbox_exec_request(exec_request, options);
let mut exec_request = crate::sandboxing::ExecRequest::from_sandbox_exec_request(
exec_request,
options,
self.sandbox_policy_cwd.clone(),
);
if let Some(network) = exec_request.network.as_ref() {
network.apply_to_env(&mut exec_request.env);
}
+10 -1
View File
@@ -376,7 +376,16 @@ impl<'a> SandboxAttempt<'a> {
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
})
.map(|request| {
crate::sandboxing::ExecRequest::from_sandbox_exec_request(request, options)
let windows_sandbox_policy_cwd =
codex_utils_absolute_path::AbsolutePathBuf::try_from(
self.sandbox_cwd.to_path_buf(),
)
.unwrap_or_else(|_| request.cwd.clone());
crate::sandboxing::ExecRequest::from_sandbox_exec_request(
request,
options,
windows_sandbox_policy_cwd,
)
})
}
}
@@ -659,6 +659,60 @@ impl UnifiedExecProcessManager {
environment: &codex_exec_server::Environment,
) -> Result<UnifiedExecProcess, UnifiedExecError> {
let inherited_fds = spawn_lifecycle.inherited_fds();
#[cfg(target_os = "windows")]
if request.sandbox == codex_sandboxing::SandboxType::WindowsRestrictedToken {
let policy_json = serde_json::to_string(&request.sandbox_policy).map_err(|err| {
UnifiedExecError::create_process(format!(
"failed to serialize Windows sandbox policy: {err}"
))
})?;
let codex_home = crate::config::find_codex_home().map_err(|err| {
UnifiedExecError::create_process(format!(
"windows sandbox: failed to resolve codex_home: {err}"
))
})?;
let spawned = match request.windows_sandbox_level {
codex_protocol::config_types::WindowsSandboxLevel::Elevated => {
codex_windows_sandbox::spawn_windows_sandbox_session_elevated(
policy_json.as_str(),
request.windows_sandbox_policy_cwd.as_path(),
codex_home.as_ref(),
request.command.clone(),
request.cwd.as_path(),
request.env.clone(),
None,
tty,
tty,
request.windows_sandbox_private_desktop,
)
.await
}
codex_protocol::config_types::WindowsSandboxLevel::RestrictedToken
| codex_protocol::config_types::WindowsSandboxLevel::Disabled => {
codex_windows_sandbox::spawn_windows_sandbox_session_legacy(
policy_json.as_str(),
request.windows_sandbox_policy_cwd.as_path(),
codex_home.as_ref(),
request.command.clone(),
request.cwd.as_path(),
request.env.clone(),
None,
tty,
tty,
request.windows_sandbox_private_desktop,
)
.await
}
};
spawn_lifecycle.after_spawn();
return UnifiedExecProcess::from_spawned(
spawned.map_err(|err| UnifiedExecError::create_process(err.to_string()))?,
request.sandbox,
spawn_lifecycle,
)
.await;
}
if environment.is_remote() {
if !inherited_fds.is_empty() {
return Err(UnifiedExecError::create_process(
@@ -67,12 +67,13 @@ fn env_overlay_for_exec_server_keeps_runtime_changes_only() {
#[test]
fn exec_server_params_use_env_policy_overlay_contract() {
let cwd: codex_utils_absolute_path::AbsolutePathBuf = std::env::current_dir()
.expect("current dir")
.try_into()
.expect("absolute path");
let request = ExecRequest {
command: vec!["bash".to_string(), "-lc".to_string(), "true".to_string()],
cwd: std::env::current_dir()
.expect("current dir")
.try_into()
.expect("absolute path"),
cwd: cwd.clone(),
env: HashMap::from([
("HOME".to_string(), "/client-home".to_string()),
("PATH".to_string(), "/sandbox-path".to_string()),
@@ -95,6 +96,7 @@ fn exec_server_params_use_env_policy_overlay_contract() {
expiration: crate::exec::ExecExpiration::DefaultTimeout,
capture_policy: crate::exec::ExecCapturePolicy::ShellTool,
sandbox: codex_sandboxing::SandboxType::None,
windows_sandbox_policy_cwd: cwd,
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
windows_sandbox_private_desktop: false,
sandbox_policy: codex_protocol::protocol::SandboxPolicy::DangerFullAccess,