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

@@ -1,3 +1,4 @@
use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBuf;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::Path; use std::path::Path;
@@ -20,8 +21,26 @@ pub struct ElevatedSandboxCaptureRequest<'a> {
pub deny_write_paths_override: &'a [AbsolutePathBuf], pub deny_write_paths_override: &'a [AbsolutePathBuf],
} }
pub struct ElevatedSandboxProfileCaptureRequest<'a> {
pub permission_profile: &'a PermissionProfile,
pub permission_profile_cwd: &'a Path,
pub codex_home: &'a Path,
pub command: Vec<String>,
pub cwd: &'a Path,
pub env_map: HashMap<String, String>,
pub timeout_ms: Option<u64>,
pub use_private_desktop: bool,
pub proxy_enforced: bool,
pub read_roots_override: Option<&'a [PathBuf]>,
pub read_roots_include_platform_defaults: bool,
pub write_roots_override: Option<&'a [PathBuf]>,
pub deny_read_paths_override: &'a [AbsolutePathBuf],
pub deny_write_paths_override: &'a [AbsolutePathBuf],
}
mod windows_impl { mod windows_impl {
use super::ElevatedSandboxCaptureRequest; use super::ElevatedSandboxCaptureRequest;
use super::ElevatedSandboxProfileCaptureRequest;
use crate::acl::allow_null_device; use crate::acl::allow_null_device;
use crate::cap::load_or_create_cap_sids; use crate::cap::load_or_create_cap_sids;
use crate::cap::workspace_write_cap_sid_for_root; use crate::cap::workspace_write_cap_sid_for_root;
@@ -37,7 +56,6 @@ mod windows_impl {
use crate::logging::log_failure; use crate::logging::log_failure;
use crate::logging::log_start; use crate::logging::log_start;
use crate::logging::log_success; use crate::logging::log_success;
use crate::policy::SandboxPolicy;
use crate::policy::parse_policy; use crate::policy::parse_policy;
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions; use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
use crate::runner_client::spawn_runner_transport; use crate::runner_client::spawn_runner_transport;
@@ -54,12 +72,12 @@ mod windows_impl {
/// Launches the command runner under the sandbox user and captures its output. /// Launches the command runner under the sandbox user and captures its output.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn run_windows_sandbox_capture( pub fn run_windows_sandbox_capture_for_permission_profile(
request: ElevatedSandboxCaptureRequest<'_>, request: ElevatedSandboxProfileCaptureRequest<'_>,
) -> Result<CaptureResult> { ) -> Result<CaptureResult> {
let ElevatedSandboxCaptureRequest { let ElevatedSandboxProfileCaptureRequest {
policy_json_or_preset, permission_profile,
sandbox_policy_cwd, permission_profile_cwd,
codex_home, codex_home,
command, command,
cwd, cwd,
@@ -73,6 +91,10 @@ mod windows_impl {
deny_read_paths_override, deny_read_paths_override,
deny_write_paths_override, deny_write_paths_override,
} = request; } = request;
let permissions = ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_cwd(
permission_profile,
permission_profile_cwd,
)?;
let deny_read_paths_override = deny_read_paths_override let deny_read_paths_override = deny_read_paths_override
.iter() .iter()
.map(AbsolutePathBuf::to_path_buf) .map(AbsolutePathBuf::to_path_buf)
@@ -81,11 +103,6 @@ mod windows_impl {
.iter() .iter()
.map(AbsolutePathBuf::to_path_buf) .map(AbsolutePathBuf::to_path_buf)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let policy = parse_policy(policy_json_or_preset)?;
let permissions = ResolvedWindowsSandboxPermissions::from_legacy_policy_for_cwd(
&policy,
sandbox_policy_cwd,
);
normalize_null_device_env(&mut env_map); normalize_null_device_env(&mut env_map);
ensure_non_interactive_pager(&mut env_map); ensure_non_interactive_pager(&mut env_map);
inherit_path_env(&mut env_map); inherit_path_env(&mut env_map);
@@ -109,12 +126,6 @@ mod windows_impl {
proxy_enforced, proxy_enforced,
)?; )?;
// Build capability SID for ACL grants. // Build capability SID for ACL grants.
if matches!(
&policy,
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. }
) {
anyhow::bail!("DangerFullAccess and ExternalSandbox are not supported for sandboxing")
}
let caps = load_or_create_cap_sids(codex_home)?; let caps = load_or_create_cap_sids(codex_home)?;
let (sid_for_null, cap_sids) = if permissions.uses_write_capabilities_for_cwd(cwd, &env_map) let (sid_for_null, cap_sids) = if permissions.uses_write_capabilities_for_cwd(cwd, &env_map)
{ {
@@ -143,14 +154,12 @@ mod windows_impl {
} }
(|| -> Result<CaptureResult> { (|| -> Result<CaptureResult> {
let permission_profile =
PermissionProfile::from_legacy_sandbox_policy_for_cwd(&policy, sandbox_policy_cwd);
let spawn_request = SpawnRequest { let spawn_request = SpawnRequest {
command: command.clone(), command: command.clone(),
cwd: cwd.to_path_buf(), cwd: cwd.to_path_buf(),
env: env_map.clone(), env: env_map.clone(),
permission_profile, permission_profile: permission_profile.clone(),
permission_profile_cwd: sandbox_policy_cwd.to_path_buf(), permission_profile_cwd: permission_profile_cwd.to_path_buf(),
codex_home: sandbox_base.clone(), codex_home: sandbox_base.clone(),
real_codex_home: codex_home.to_path_buf(), real_codex_home: codex_home.to_path_buf(),
cap_sids, cap_sids,
@@ -210,6 +219,48 @@ mod windows_impl {
})() })()
} }
/// Legacy policy-string adapter for callers that have not moved to permission profiles yet.
#[allow(clippy::too_many_arguments)]
pub fn run_windows_sandbox_capture(
request: ElevatedSandboxCaptureRequest<'_>,
) -> Result<CaptureResult> {
let ElevatedSandboxCaptureRequest {
policy_json_or_preset,
sandbox_policy_cwd,
codex_home,
command,
cwd,
env_map,
timeout_ms,
use_private_desktop,
proxy_enforced,
read_roots_override,
read_roots_include_platform_defaults,
write_roots_override,
deny_read_paths_override,
deny_write_paths_override,
} = request;
let policy = parse_policy(policy_json_or_preset)?;
let permission_profile =
PermissionProfile::from_legacy_sandbox_policy_for_cwd(&policy, sandbox_policy_cwd);
run_windows_sandbox_capture_for_permission_profile(ElevatedSandboxProfileCaptureRequest {
permission_profile: &permission_profile,
permission_profile_cwd: sandbox_policy_cwd,
codex_home,
command,
cwd,
env_map,
timeout_ms,
use_private_desktop,
proxy_enforced,
read_roots_override,
read_roots_include_platform_defaults,
write_roots_override,
deny_read_paths_override,
deny_write_paths_override,
})
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::policy::SandboxPolicy; use crate::policy::SandboxPolicy;
@@ -242,10 +293,13 @@ mod windows_impl {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use windows_impl::run_windows_sandbox_capture; pub use windows_impl::run_windows_sandbox_capture;
#[cfg(target_os = "windows")]
pub use windows_impl::run_windows_sandbox_capture_for_permission_profile;
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
mod stub { mod stub {
use super::ElevatedSandboxCaptureRequest; use super::ElevatedSandboxCaptureRequest;
use super::ElevatedSandboxProfileCaptureRequest;
use anyhow::Result; use anyhow::Result;
use anyhow::bail; use anyhow::bail;
@@ -264,7 +318,17 @@ mod stub {
) -> Result<CaptureResult> { ) -> Result<CaptureResult> {
bail!("Windows sandbox is only available on Windows") bail!("Windows sandbox is only available on Windows")
} }
/// Stub implementation for non-Windows targets; sandboxing only works on Windows.
#[allow(clippy::too_many_arguments)]
pub fn run_windows_sandbox_capture_for_permission_profile(
_request: ElevatedSandboxProfileCaptureRequest<'_>,
) -> Result<CaptureResult> {
bail!("Windows sandbox is only available on Windows")
}
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
pub use stub::run_windows_sandbox_capture; pub use stub::run_windows_sandbox_capture;
#[cfg(not(target_os = "windows"))]
pub use stub::run_windows_sandbox_capture_for_permission_profile;
+6
View File
@@ -139,8 +139,12 @@ pub use dpapi::unprotect as dpapi_unprotect;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use elevated_impl::ElevatedSandboxCaptureRequest; pub use elevated_impl::ElevatedSandboxCaptureRequest;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use elevated_impl::ElevatedSandboxProfileCaptureRequest;
#[cfg(target_os = "windows")]
pub use elevated_impl::run_windows_sandbox_capture as run_windows_sandbox_capture_elevated; pub use elevated_impl::run_windows_sandbox_capture as run_windows_sandbox_capture_elevated;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use elevated_impl::run_windows_sandbox_capture_for_permission_profile as run_windows_sandbox_capture_for_permission_profile_elevated;
#[cfg(target_os = "windows")]
pub use helper_materialization::resolve_current_exe_for_launch; pub use helper_materialization::resolve_current_exe_for_launch;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use hide_users::hide_current_user_profile_dir; pub use hide_users::hide_current_user_profile_dir;
@@ -258,6 +262,8 @@ pub use token::get_current_token_for_restriction;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use unified_exec::spawn_windows_sandbox_session_elevated; pub use unified_exec::spawn_windows_sandbox_session_elevated;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use unified_exec::spawn_windows_sandbox_session_elevated_for_permission_profile;
#[cfg(target_os = "windows")]
pub use unified_exec::spawn_windows_sandbox_session_legacy; pub use unified_exec::spawn_windows_sandbox_session_legacy;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
pub use wfp::install_wfp_filters_for_account; pub use wfp::install_wfp_filters_for_account;
@@ -41,8 +41,10 @@ pub fn token_mode_for_permission_profile(
cwd: &Path, cwd: &Path,
env_map: &HashMap<String, String>, env_map: &HashMap<String, String>,
) -> Result<WindowsSandboxTokenMode> { ) -> Result<WindowsSandboxTokenMode> {
let permissions = let permissions = ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_cwd(
ResolvedWindowsSandboxPermissions::try_from_permission_profile(permission_profile)?; permission_profile,
cwd,
)?;
if permissions.file_system.has_full_disk_write_access() { if permissions.file_system.has_full_disk_write_access() {
anyhow::bail!( anyhow::bail!(
"permission profile requests full-disk filesystem writes, which cannot be enforced by the Windows sandbox" "permission profile requests full-disk filesystem writes, which cannot be enforced by the Windows sandbox"
@@ -82,6 +84,19 @@ impl ResolvedWindowsSandboxPermissions {
}) })
} }
/// Resolves a managed permission profile and binds symbolic `:workspace_roots`
/// entries to the permission root supplied by the caller.
pub fn try_from_permission_profile_for_cwd(
permission_profile: &PermissionProfile,
cwd: &Path,
) -> Result<Self> {
let mut permissions = Self::try_from_permission_profile(permission_profile)?;
permissions.file_system = permissions
.file_system
.materialize_project_roots_with_cwd(cwd);
Ok(permissions)
}
pub(crate) fn should_apply_network_block(&self) -> bool { pub(crate) fn should_apply_network_block(&self) -> bool {
!self.network.is_enabled() !self.network.is_enabled()
} }
@@ -259,6 +274,43 @@ mod tests {
); );
} }
#[test]
fn permission_profile_workspace_root_stays_bound_to_profile_cwd() {
let tmp = TempDir::new().expect("tempdir");
let profile_cwd = tmp.path().join("workspace");
let command_cwd = profile_cwd.join("subdir");
std::fs::create_dir_all(&command_cwd).expect("create command cwd");
let permission_profile = PermissionProfile::Managed {
file_system: ManagedFileSystemPermissions::Restricted {
entries: vec![FileSystemSandboxEntry {
path: FileSystemPath::Special {
value: FileSystemSpecialPath::project_roots(/*subpath*/ None),
},
access: FileSystemAccessMode::Write,
}],
glob_scan_max_depth: None,
},
network: NetworkSandboxPolicy::Restricted,
};
let permissions = ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_cwd(
&permission_profile,
&profile_cwd,
)
.expect("managed permission profile");
let roots = permissions
.writable_roots_for_cwd(&command_cwd, &HashMap::new())
.into_iter()
.map(|root| root.root)
.collect::<Vec<_>>();
assert_eq!(
roots,
vec![dunce::canonicalize(&profile_cwd).expect("canonical profile cwd")]
);
}
#[test] #[test]
fn token_mode_for_profile_without_writable_roots_uses_readonly_capability() { fn token_mode_for_profile_without_writable_roots_uses_readonly_capability() {
let tmp = TempDir::new().expect("tempdir"); let tmp = TempDir::new().expect("tempdir");
+27 -28
View File
@@ -44,13 +44,13 @@ pub(crate) struct SpawnContext {
pub(crate) policy: SandboxPolicy, pub(crate) policy: SandboxPolicy,
pub(crate) permissions: ResolvedWindowsSandboxPermissions, pub(crate) permissions: ResolvedWindowsSandboxPermissions,
pub(crate) current_dir: PathBuf, pub(crate) current_dir: PathBuf,
pub(crate) sandbox_base: PathBuf,
pub(crate) logs_base_dir: Option<PathBuf>, pub(crate) logs_base_dir: Option<PathBuf>,
pub(crate) uses_write_capabilities: bool, pub(crate) uses_write_capabilities: bool,
} }
pub(crate) struct ElevatedSpawnContext { pub(crate) struct ElevatedSpawnContext {
pub(crate) common: SpawnContext, pub(crate) sandbox_base: PathBuf,
pub(crate) logs_base_dir: Option<PathBuf>,
pub(crate) sandbox_creds: SandboxCreds, pub(crate) sandbox_creds: SandboxCreds,
pub(crate) cap_sids: Vec<String>, pub(crate) cap_sids: Vec<String>,
} }
@@ -109,7 +109,7 @@ fn prepare_spawn_context_common(
ensure_codex_home_exists(codex_home)?; ensure_codex_home_exists(codex_home)?;
let sandbox_base = codex_home.join(".sandbox"); let sandbox_base = codex_home.join(".sandbox");
std::fs::create_dir_all(&sandbox_base)?; std::fs::create_dir_all(&sandbox_base)?;
let logs_base_dir = Some(sandbox_base.clone()); let logs_base_dir = Some(sandbox_base);
log_start(command, logs_base_dir.as_deref()); log_start(command, logs_base_dir.as_deref());
let permissions = let permissions =
@@ -120,7 +120,6 @@ fn prepare_spawn_context_common(
policy, policy,
permissions, permissions,
current_dir: cwd.to_path_buf(), current_dir: cwd.to_path_buf(),
sandbox_base,
logs_base_dir, logs_base_dir,
uses_write_capabilities, uses_write_capabilities,
}) })
@@ -362,9 +361,8 @@ pub(crate) fn apply_legacy_session_acl_rules(
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) fn prepare_elevated_spawn_context( pub(crate) fn prepare_elevated_spawn_context_for_permissions(
policy_json_or_preset: &str, permissions: ResolvedWindowsSandboxPermissions,
sandbox_policy_cwd: &Path,
codex_home: &Path, codex_home: &Path,
cwd: &Path, cwd: &Path,
env_map: &mut HashMap<String, String>, env_map: &mut HashMap<String, String>,
@@ -375,33 +373,33 @@ pub(crate) fn prepare_elevated_spawn_context(
deny_read_paths_override: &[PathBuf], deny_read_paths_override: &[PathBuf],
deny_write_paths_override: &[PathBuf], deny_write_paths_override: &[PathBuf],
) -> Result<ElevatedSpawnContext> { ) -> Result<ElevatedSpawnContext> {
let common = prepare_spawn_context_common( normalize_null_device_env(env_map);
policy_json_or_preset, ensure_non_interactive_pager(env_map);
sandbox_policy_cwd, inherit_path_env(env_map);
codex_home, inject_git_safe_directory(env_map, cwd);
cwd,
env_map, // Use a temp-based log dir that the sandbox user can write.
command, let sandbox_base = codex_home.join(".sandbox");
SpawnPrepOptions { ensure_codex_home_exists(&sandbox_base)?;
inherit_path: true, let logs_base_dir = Some(sandbox_base.clone());
add_git_safe_directory: true, log_start(command, logs_base_dir.as_deref());
},
)?; let uses_write_capabilities = permissions.uses_write_capabilities_for_cwd(cwd, env_map);
let AllowDenyPaths { allow, deny } = let AllowDenyPaths { allow, deny } =
compute_allow_paths_for_permissions(&common.permissions, &common.current_dir, env_map); compute_allow_paths_for_permissions(&permissions, cwd, env_map);
let write_roots: Vec<PathBuf> = allow.into_iter().collect(); let write_roots: Vec<PathBuf> = allow.into_iter().collect();
let deny_write_paths: Vec<PathBuf> = deny.into_iter().collect(); let deny_write_paths: Vec<PathBuf> = deny.into_iter().collect();
let computed_write_roots_override = if common.uses_write_capabilities { let computed_write_roots_override = if uses_write_capabilities {
Some(write_roots.as_slice()) Some(write_roots.as_slice())
} else { } else {
None None
}; };
let write_roots_for_setup = write_roots_override.or(computed_write_roots_override); let write_roots_for_setup = write_roots_override.or(computed_write_roots_override);
let effective_write_roots = if common.uses_write_capabilities { let effective_write_roots = if uses_write_capabilities {
effective_write_roots_for_permissions( effective_write_roots_for_permissions(
&common.permissions, &permissions,
&common.current_dir, cwd,
env_map, env_map,
codex_home, codex_home,
write_roots_for_setup, write_roots_for_setup,
@@ -409,13 +407,13 @@ pub(crate) fn prepare_elevated_spawn_context(
} else { } else {
Vec::new() Vec::new()
}; };
let setup_write_roots_override = if common.uses_write_capabilities { let setup_write_roots_override = if uses_write_capabilities {
Some(effective_write_roots.as_slice()) Some(effective_write_roots.as_slice())
} else { } else {
write_roots_override write_roots_override
}; };
let sandbox_creds = require_logon_sandbox_creds( let sandbox_creds = require_logon_sandbox_creds(
&common.permissions, &permissions,
cwd, cwd,
env_map, env_map,
codex_home, codex_home,
@@ -431,7 +429,7 @@ pub(crate) fn prepare_elevated_spawn_context(
/*proxy_enforced*/ false, /*proxy_enforced*/ false,
)?; )?;
let caps = load_or_create_cap_sids(codex_home)?; let caps = load_or_create_cap_sids(codex_home)?;
let (psid_to_use, cap_sids) = if common.uses_write_capabilities { let (psid_to_use, cap_sids) = if uses_write_capabilities {
let cap_sids = root_capability_sids(codex_home, cwd, effective_write_roots)? let cap_sids = root_capability_sids(codex_home, cwd, effective_write_roots)?
.into_iter() .into_iter()
.map(|root_sid| root_sid.sid_str) .map(|root_sid| root_sid.sid_str)
@@ -452,7 +450,8 @@ pub(crate) fn prepare_elevated_spawn_context(
} }
Ok(ElevatedSpawnContext { Ok(ElevatedSpawnContext {
common, sandbox_base,
logs_base_dir,
sandbox_creds, sandbox_creds,
cap_sids, cap_sids,
}) })
@@ -8,8 +8,10 @@ use crate::ipc_framed::FramedMessage;
use crate::ipc_framed::IPC_PROTOCOL_VERSION; use crate::ipc_framed::IPC_PROTOCOL_VERSION;
use crate::ipc_framed::Message; use crate::ipc_framed::Message;
use crate::ipc_framed::SpawnRequest; use crate::ipc_framed::SpawnRequest;
use crate::policy::parse_policy;
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
use crate::runner_client::spawn_runner_transport; 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 anyhow::Result;
use codex_protocol::models::PermissionProfile; use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBuf;
@@ -23,9 +25,9 @@ use tokio::sync::mpsc;
use tokio::sync::oneshot; use tokio::sync::oneshot;
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) async fn spawn_windows_sandbox_session_elevated( pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profile(
policy_json_or_preset: &str, permission_profile: &PermissionProfile,
sandbox_policy_cwd: &Path, permission_profile_cwd: &Path,
codex_home: &Path, codex_home: &Path,
command: Vec<String>, command: Vec<String>,
cwd: &Path, cwd: &Path,
@@ -48,9 +50,12 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
.iter() .iter()
.map(AbsolutePathBuf::to_path_buf) .map(AbsolutePathBuf::to_path_buf)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let elevated = prepare_elevated_spawn_context( let permissions = ResolvedWindowsSandboxPermissions::try_from_permission_profile_for_cwd(
policy_json_or_preset, permission_profile,
sandbox_policy_cwd, permission_profile_cwd,
)?;
let elevated = prepare_elevated_spawn_context_for_permissions(
permissions,
codex_home, codex_home,
cwd, cwd,
&mut env_map, &mut env_map,
@@ -62,17 +67,13 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
&deny_write_paths_override, &deny_write_paths_override,
)?; )?;
let permission_profile = PermissionProfile::from_legacy_sandbox_policy_for_cwd(
&elevated.common.policy,
sandbox_policy_cwd,
);
let spawn_request = SpawnRequest { let spawn_request = SpawnRequest {
command: command.clone(), command: command.clone(),
cwd: cwd.to_path_buf(), cwd: cwd.to_path_buf(),
env: env_map.clone(), env: env_map.clone(),
permission_profile, permission_profile: permission_profile.clone(),
permission_profile_cwd: sandbox_policy_cwd.to_path_buf(), permission_profile_cwd: permission_profile_cwd.to_path_buf(),
codex_home: elevated.common.sandbox_base.clone(), codex_home: elevated.sandbox_base.clone(),
real_codex_home: codex_home.to_path_buf(), real_codex_home: codex_home.to_path_buf(),
cap_sids: elevated.cap_sids.clone(), cap_sids: elevated.cap_sids.clone(),
timeout_ms, timeout_ms,
@@ -83,7 +84,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
let codex_home = codex_home.to_path_buf(); let codex_home = codex_home.to_path_buf();
let cwd = cwd.to_path_buf(); let cwd = cwd.to_path_buf();
let sandbox_creds = elevated.sandbox_creds.clone(); 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<_> { let transport = tokio::task::spawn_blocking(move || -> Result<_> {
spawn_runner_transport( spawn_runner_transport(
&codex_home, &codex_home,
@@ -144,3 +145,44 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated(
stdin_open, 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; mod backends;
use anyhow::Result; use anyhow::Result;
use codex_protocol::models::PermissionProfile;
use codex_utils_absolute_path::AbsolutePathBuf; use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_pty::SpawnedProcess; use codex_utils_pty::SpawnedProcess;
use std::collections::HashMap; use std::collections::HashMap;
@@ -48,6 +49,44 @@ pub async fn spawn_windows_sandbox_session_legacy(
.await .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)] #[allow(clippy::too_many_arguments)]
pub async fn spawn_windows_sandbox_session_elevated( pub async fn spawn_windows_sandbox_session_elevated(
policy_json_or_preset: &str, policy_json_or_preset: &str,