mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
## 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
189 lines
6.1 KiB
Rust
189 lines
6.1 KiB
Rust
use super::windows_common::finish_driver_spawn;
|
|
use super::windows_common::make_runner_resizer;
|
|
use super::windows_common::start_runner_pipe_writer;
|
|
use super::windows_common::start_runner_stdin_writer;
|
|
use super::windows_common::start_runner_stdout_reader;
|
|
use crate::ipc_framed::EmptyPayload;
|
|
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_for_permissions;
|
|
use anyhow::Result;
|
|
use codex_protocol::models::PermissionProfile;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use codex_utils_pty::ProcessDriver;
|
|
use codex_utils_pty::SpawnedProcess;
|
|
use std::collections::HashMap;
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use tokio::sync::broadcast;
|
|
use tokio::sync::mpsc;
|
|
use tokio::sync::oneshot;
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
|
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,
|
|
mut 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 deny_read_paths_override = deny_read_paths_override
|
|
.iter()
|
|
.map(AbsolutePathBuf::to_path_buf)
|
|
.collect::<Vec<_>>();
|
|
let deny_write_paths_override = deny_write_paths_override
|
|
.iter()
|
|
.map(AbsolutePathBuf::to_path_buf)
|
|
.collect::<Vec<_>>();
|
|
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,
|
|
&command,
|
|
read_roots_override,
|
|
read_roots_include_platform_defaults,
|
|
write_roots_override,
|
|
&deny_read_paths_override,
|
|
&deny_write_paths_override,
|
|
)?;
|
|
|
|
let spawn_request = SpawnRequest {
|
|
command: command.clone(),
|
|
cwd: cwd.to_path_buf(),
|
|
env: env_map.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,
|
|
tty,
|
|
stdin_open,
|
|
use_private_desktop,
|
|
};
|
|
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.logs_base_dir.clone();
|
|
let transport = tokio::task::spawn_blocking(move || -> Result<_> {
|
|
spawn_runner_transport(
|
|
&codex_home,
|
|
&cwd,
|
|
&sandbox_creds,
|
|
logs_base_dir.as_deref(),
|
|
spawn_request,
|
|
)
|
|
})
|
|
.await
|
|
.map_err(|err| anyhow::anyhow!("runner handshake task failed: {err}"))??;
|
|
let (pipe_write, pipe_read) = transport.into_files();
|
|
|
|
let (writer_tx, writer_rx) = mpsc::channel::<Vec<u8>>(128);
|
|
let (stdout_tx, stdout_rx) = broadcast::channel::<Vec<u8>>(256);
|
|
let stderr_rx = if tty {
|
|
None
|
|
} else {
|
|
Some(broadcast::channel::<Vec<u8>>(256))
|
|
};
|
|
let (exit_tx, exit_rx) = oneshot::channel::<i32>();
|
|
|
|
let outbound_tx = start_runner_pipe_writer(pipe_write);
|
|
let writer_handle = start_runner_stdin_writer(writer_rx, outbound_tx.clone(), tty, stdin_open);
|
|
let terminator = {
|
|
let outbound_tx = outbound_tx.clone();
|
|
Some(Box::new(move || {
|
|
let _ = outbound_tx.send(FramedMessage {
|
|
version: IPC_PROTOCOL_VERSION,
|
|
message: Message::Terminate {
|
|
payload: EmptyPayload::default(),
|
|
},
|
|
});
|
|
}) as Box<dyn FnMut() + Send + Sync>)
|
|
};
|
|
|
|
start_runner_stdout_reader(
|
|
pipe_read,
|
|
stdout_tx,
|
|
stderr_rx.as_ref().map(|(tx, _rx)| tx.clone()),
|
|
exit_tx,
|
|
);
|
|
|
|
Ok(finish_driver_spawn(
|
|
ProcessDriver {
|
|
writer_tx,
|
|
stdout_rx,
|
|
stderr_rx: stderr_rx.map(|(_tx, rx)| rx),
|
|
exit_rx,
|
|
terminator,
|
|
writer_handle: Some(writer_handle),
|
|
resizer: if tty {
|
|
Some(make_runner_resizer(outbound_tx))
|
|
} else {
|
|
None
|
|
},
|
|
},
|
|
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
|
|
}
|