windows-sandbox: add profile-native elevated APIs (#23714)

## Why

This is the next step after #23167 in the Windows sandbox
`PermissionProfile` migration. The elevated Windows backend still
exposed policy-string entry points, which forced callers to pass a
compatibility `SandboxPolicy` before the command-runner IPC could
receive a profile.

Adding profile-native APIs first keeps the core switch in the next PR
small: reviewers can see that the Windows crate can prepare elevated
setup, capability SIDs, and runner IPC from a resolved
`PermissionProfile` without changing core behavior yet.

## What

- Adds `ElevatedSandboxProfileCaptureRequest` and
`run_windows_sandbox_capture_for_permission_profile_elevated` for
one-shot elevated capture.
- Adds `spawn_windows_sandbox_session_elevated_for_permission_profile`
for unified exec sessions.
- Factors elevated spawn prep through
`prepare_elevated_spawn_context_for_permissions`, so both new APIs
operate from `ResolvedWindowsSandboxPermissions` directly.
- Keeps the existing legacy policy-string APIs as adapters for callers
that have not moved yet.

## Verification

- `cargo test -p codex-windows-sandbox`












---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/23714).
* #23715
* __->__ #23714
This commit is contained in:
Michael Bolin authored and GitHub committed 2026-05-21 00:25:31 +00:00
1 parent a27d3847b5
commit c9ff067e31
6 files changed
+268 -66

No files matched your search

@@ -8,8 +8,10 @@ use crate::ipc_framed::FramedMessage;
use crate::ipc_framed::IPC_PROTOCOL_VERSION;
use crate::ipc_framed::Message;
use crate::ipc_framed::SpawnRequest;
use crate::policy::parse_policy;
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
use crate::runner_client::spawn_runner_transport;
use crate::spawn_prep::prepare_elevated_spawn_context;
use crate::spawn_prep::prepare_elevated_spawn_context_for_permissions;
use anyhow::Result;
use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
@@ -23,9 +25,9 @@ use tokio::sync::mpsc;
use tokio::sync::oneshot;
#[allow(clippy::too_many_arguments)]
pub(crate) async fn spawn_windows_sandbox_session_elevated(
policy_json_or_preset: &str,
sandbox_policy_cwd: &Path,
pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profile(
permission_profile: &PermissionProfile,
permission_profile_cwd: &Path,
codex_home: &Path,
command: Vec<String>,
cwd: &Path,
@@ -48,9 +50,12 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
.iter()
.map(AbsolutePathBuf::to_path_buf)
.collect::<Vec<_>>();
let elevated = prepare_elevated_spawn_context(
policy_json_or_preset,
sandbox_policy_cwd,
let permissions = ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_cwd(
permission_profile,
permission_profile_cwd,
)?;
let elevated = prepare_elevated_spawn_context_for_permissions(
permissions,
codex_home,
cwd,
&mut env_map,
@@ -62,17 +67,13 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
&deny_write_paths_override,
)?;
let permission_profile = PermissionProfile::from_legacy_sandbox_policy_for_cwd(
&elevated.common.policy,
sandbox_policy_cwd,
);
let spawn_request = SpawnRequest {
command: command.clone(),
cwd: cwd.to_path_buf(),
env: env_map.clone(),
permission_profile,
permission_profile_cwd: sandbox_policy_cwd.to_path_buf(),
codex_home: elevated.common.sandbox_base.clone(),
permission_profile: permission_profile.clone(),
permission_profile_cwd: permission_profile_cwd.to_path_buf(),
codex_home: elevated.sandbox_base.clone(),
real_codex_home: codex_home.to_path_buf(),
cap_sids: elevated.cap_sids.clone(),
timeout_ms,
@@ -83,7 +84,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
let codex_home = codex_home.to_path_buf();
let cwd = cwd.to_path_buf();
let sandbox_creds = elevated.sandbox_creds.clone();
let logs_base_dir = elevated.common.logs_base_dir.clone();
let logs_base_dir = elevated.logs_base_dir.clone();
let transport = tokio::task::spawn_blocking(move || -> Result<_> {
spawn_runner_transport(
&codex_home,
@@ -144,3 +145,44 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
stdin_open,
))
}
#[allow(clippy::too_many_arguments)]
pub(crate) async fn spawn_windows_sandbox_session_elevated(
policy_json_or_preset: &str,
sandbox_policy_cwd: &Path,
codex_home: &Path,
command: Vec<String>,
cwd: &Path,
env_map: HashMap<String, String>,
timeout_ms: Option<u64>,
read_roots_override: Option<&[PathBuf]>,
read_roots_include_platform_defaults: bool,
write_roots_override: Option<&[PathBuf]>,
deny_read_paths_override: &[AbsolutePathBuf],
deny_write_paths_override: &[AbsolutePathBuf],
tty: bool,
stdin_open: bool,
use_private_desktop: bool,
) -> Result<SpawnedProcess> {
let policy = parse_policy(policy_json_or_preset)?;
let permission_profile =
PermissionProfile::from_legacy_sandbox_policy_for_cwd(&policy, sandbox_policy_cwd);
spawn_windows_sandbox_session_elevated_for_permission_profile(
&permission_profile,
sandbox_policy_cwd,
codex_home,
command,
cwd,
env_map,
timeout_ms,
read_roots_override,
read_roots_include_platform_defaults,
write_roots_override,
deny_read_paths_override,
deny_write_paths_override,
tty,
stdin_open,
use_private_desktop,
)
.await
}
@@ -10,6 +10,7 @@
mod backends;
use anyhow::Result;
use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_pty::SpawnedProcess;
use std::collections::HashMap;
@@ -48,6 +49,44 @@ pub async fn spawn_windows_sandbox_session_legacy(
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn spawn_windows_sandbox_session_elevated_for_permission_profile(
permission_profile: &PermissionProfile,
permission_profile_cwd: &Path,
codex_home: &Path,
command: Vec<String>,
cwd: &Path,
env_map: HashMap<String, String>,
timeout_ms: Option<u64>,
read_roots_override: Option<&[PathBuf]>,
read_roots_include_platform_defaults: bool,
write_roots_override: Option<&[PathBuf]>,
deny_read_paths_override: &[AbsolutePathBuf],
deny_write_paths_override: &[AbsolutePathBuf],
tty: bool,
stdin_open: bool,
use_private_desktop: bool,
) -> Result<SpawnedProcess> {
backends::elevated::spawn_windows_sandbox_session_elevated_for_permission_profile(
permission_profile,
permission_profile_cwd,
codex_home,
command,
cwd,
env_map,
timeout_ms,
read_roots_override,
read_roots_include_platform_defaults,
write_roots_override,
deny_read_paths_override,
deny_write_paths_override,
tty,
stdin_open,
use_private_desktop,
)
.await
}
#[allow(clippy::too_many_arguments)]
pub async fn spawn_windows_sandbox_session_elevated(
policy_json_or_preset: &str,