Preserve Windows sandbox identity during credential retry (#29624)

## Summary

- recognize stale Windows sandbox credentials from both runner logon and
child startup failures
- refresh credentials once without changing the original command,
permissions, file rules, desktop mode, or managed-network identity
- add a Windows regression test that forces error 1312 and inspects the
real retry arguments

## Why

Elevated unified exec starts commands in two steps:

```text
Codex -> sandbox command runner -> requested command
```

Either process start can fail when Windows invalidates the sandbox logon
session. The child-side failure was previously returned as text, so the
parent could not reliably recognize Windows error 1312.

The existing retry also refreshed credentials with `proxy_enforced =
false`, even when the original request used managed networking. That
could change the selected Windows sandbox identity from offline to
online during the retry.

## How

- carry the failure stage and numeric Windows error code through the
command-runner IPC protocol
- preserve native `CreateProcessAsUserW` error codes instead of parsing
error messages
- keep every retry-sensitive field in one request and use it for both
attempts
- retry exactly once after refreshing credentials, then return the
second failure
- share the retry rule with the elevated capture path

The Windows test injects error 1312 on both attempts and verifies:

- two spawn attempts and one credential refresh
- stale credentials are replaced by refreshed credentials
- both attempts receive the same command, environment, cwd, permissions,
roots, deny paths, TTY settings, and private-desktop mode
- credential refresh receives the original `proxy_enforced` value

## Tests

- `just test -p codex-windows-sandbox`
- the new Windows-only regression test is included in the Windows
nextest CI archive
This commit is contained in:
jif authored and GitHub committed 2026-06-24 20:20:52 +01:00
1 parent 5013d10824
commit 4907f0c2c3
9 files changed
+488 -103

No files matched your search

@@ -44,7 +44,7 @@ mod windows_impl {
use crate::logging::log_start;
use crate::logging::log_success;
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
use crate::runner_client::is_stale_sandbox_creds_error;
use crate::runner_client::retry_runner_spawn_once;
use crate::runner_client::spawn_runner_transport;
use crate::sandbox_utils::ensure_codex_home_exists;
use crate::sandbox_utils::inject_git_safe_directory;
@@ -139,7 +139,7 @@ mod windows_impl {
let logs_base_dir: Option<&Path> = Some(sandbox_base.as_path());
log_start(&command, logs_base_dir);
let mut sandbox_creds = require_logon_sandbox_creds(
let sandbox_creds = require_logon_sandbox_creds(
&permissions,
cwd,
&env_map,
@@ -195,16 +195,19 @@ mod windows_impl {
stdin_open: false,
use_private_desktop,
};
let transport = match spawn_runner_transport(
codex_home,
cwd,
&sandbox_creds,
logs_base_dir,
spawn_request.clone(),
) {
Ok(transport) => transport,
Err(err) if is_stale_sandbox_creds_error(&err) => {
sandbox_creds = refresh_logon_sandbox_creds(
let transport = retry_runner_spawn_once(
sandbox_creds,
|sandbox_creds| {
spawn_runner_transport(
codex_home,
cwd,
&sandbox_creds,
logs_base_dir,
spawn_request.clone(),
)
},
|| {
refresh_logon_sandbox_creds(
&permissions,
cwd,
&env_map,
@@ -216,17 +219,9 @@ mod windows_impl {
&deny_write_paths_override,
proxy_enforced,
crate::WindowsSandboxProxySettingsMode::Reconcile,
)?;
spawn_runner_transport(
codex_home,
cwd,
&sandbox_creds,
logs_base_dir,
spawn_request,
)?
}
Err(err) => return Err(err),
};
)
},
)?;
let (pipe_write, mut pipe_read) = transport.into_files();
let cancel_writer = spawn_cancel_writer(&pipe_write, cancellation)?;