mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Plumb explicit application paths through Windows launchers
This commit is contained in:
@@ -473,6 +473,7 @@ async fn run_command_under_windows_session(
|
||||
) -> ! {
|
||||
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_windows_sandbox::WindowsProcessLaunch;
|
||||
use codex_windows_sandbox::WindowsSandboxProxySettingsMode;
|
||||
use codex_windows_sandbox::WindowsSandboxSessionRequest;
|
||||
use codex_windows_sandbox::spawn_windows_sandbox_session_for_level;
|
||||
@@ -482,7 +483,10 @@ async fn run_command_under_windows_session(
|
||||
permission_profile,
|
||||
workspace_roots: workspace_roots.as_slice(),
|
||||
codex_home: config.codex_home.as_path(),
|
||||
command,
|
||||
launch: WindowsProcessLaunch {
|
||||
application_path: None,
|
||||
command,
|
||||
},
|
||||
cwd: cwd.as_path(),
|
||||
env_map: env,
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(config),
|
||||
|
||||
@@ -39,7 +39,7 @@ use codex_windows_sandbox::hide_current_user_profile_dir;
|
||||
use codex_windows_sandbox::log_note;
|
||||
use codex_windows_sandbox::read_frame;
|
||||
use codex_windows_sandbox::read_handle_loop;
|
||||
use codex_windows_sandbox::spawn_process_with_pipes;
|
||||
use codex_windows_sandbox::spawn_process_with_pipes_with_launch;
|
||||
use codex_windows_sandbox::to_wide;
|
||||
use codex_windows_sandbox::token_mode_for_permission_profile;
|
||||
use codex_windows_sandbox::write_frame;
|
||||
@@ -300,9 +300,9 @@ fn spawn_ipc_process(req: &SpawnRequest) -> Result<IpcSpawnedProcess> {
|
||||
let mut hpc_handle: Option<HANDLE> = None;
|
||||
let mut pipe_handles = None;
|
||||
let (pi, stdout_handle, stderr_handle, stdin_handle) = if req.tty {
|
||||
let (pi, mut conpty) = codex_windows_sandbox::spawn_conpty_process_as_user(
|
||||
let (pi, mut conpty) = codex_windows_sandbox::spawn_conpty_process_as_user_with_launch(
|
||||
h_token.raw(),
|
||||
&req.command,
|
||||
&req.launch,
|
||||
&effective_cwd,
|
||||
&req.env,
|
||||
req.use_private_desktop,
|
||||
@@ -332,9 +332,9 @@ fn spawn_ipc_process(req: &SpawnRequest) -> Result<IpcSpawnedProcess> {
|
||||
} else {
|
||||
StdinMode::Closed
|
||||
};
|
||||
let spawned_pipes: PipeSpawnHandles = spawn_process_with_pipes(
|
||||
let spawned_pipes: PipeSpawnHandles = spawn_process_with_pipes_with_launch(
|
||||
h_token.raw(),
|
||||
&req.command,
|
||||
&req.launch,
|
||||
&effective_cwd,
|
||||
&req.env,
|
||||
stdin_mode,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
use crate::desktop::LaunchDesktop;
|
||||
use crate::proc_thread_attr::ProcThreadAttributeList;
|
||||
use crate::process::WindowsProcessLaunch;
|
||||
use crate::winutil::format_last_error;
|
||||
use crate::winutil::quote_windows_arg;
|
||||
use crate::winutil::to_wide;
|
||||
@@ -99,12 +100,41 @@ pub fn spawn_conpty_process_as_user(
|
||||
use_private_desktop: bool,
|
||||
logs_base_dir: Option<&Path>,
|
||||
) -> Result<(PROCESS_INFORMATION, ConptyInstance)> {
|
||||
let cmdline_str = argv
|
||||
let launch = WindowsProcessLaunch {
|
||||
application_path: None,
|
||||
command: argv.to_vec(),
|
||||
};
|
||||
spawn_conpty_process_as_user_with_launch(
|
||||
h_token,
|
||||
&launch,
|
||||
cwd,
|
||||
env_map,
|
||||
use_private_desktop,
|
||||
logs_base_dir,
|
||||
)
|
||||
}
|
||||
|
||||
/// Spawns a ConPTY process with an optional explicit `lpApplicationName`.
|
||||
#[doc(hidden)]
|
||||
pub fn spawn_conpty_process_as_user_with_launch(
|
||||
h_token: HANDLE,
|
||||
launch: &WindowsProcessLaunch,
|
||||
cwd: &Path,
|
||||
env_map: &HashMap<String, String>,
|
||||
use_private_desktop: bool,
|
||||
logs_base_dir: Option<&Path>,
|
||||
) -> Result<(PROCESS_INFORMATION, ConptyInstance)> {
|
||||
let cmdline_str = launch
|
||||
.command
|
||||
.iter()
|
||||
.map(|arg| quote_windows_arg(arg))
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ");
|
||||
let mut cmdline: Vec<u16> = to_wide(&cmdline_str);
|
||||
let application_name = launch.application_path.as_deref().map(to_wide);
|
||||
let application_name_ptr = application_name
|
||||
.as_ref()
|
||||
.map_or(std::ptr::null(), Vec::as_ptr);
|
||||
let env_block = make_env_block(env_map);
|
||||
let mut si: STARTUPINFOEXW = unsafe { std::mem::zeroed() };
|
||||
si.StartupInfo.cb = std::mem::size_of::<STARTUPINFOEXW>() as u32;
|
||||
@@ -132,7 +162,7 @@ pub fn spawn_conpty_process_as_user(
|
||||
let ok = unsafe {
|
||||
CreateProcessAsUserW(
|
||||
h_token,
|
||||
std::ptr::null(),
|
||||
application_name_ptr,
|
||||
cmdline.as_mut_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
//! not use this protocol, and non‑unified exec capture uses it only when running
|
||||
//! through the elevated runner.
|
||||
|
||||
use crate::process::WindowsProcessLaunch;
|
||||
use anyhow::Result;
|
||||
use base64::Engine as _;
|
||||
use base64::engine::general_purpose::STANDARD;
|
||||
@@ -26,7 +27,7 @@ use std::path::PathBuf;
|
||||
const MAX_FRAME_LEN: usize = 8 * 1024 * 1024;
|
||||
|
||||
/// Protocol version shared by the parent process and elevated command runner.
|
||||
pub const IPC_PROTOCOL_VERSION: u8 = 4;
|
||||
pub const IPC_PROTOCOL_VERSION: u8 = 5;
|
||||
|
||||
/// Length-prefixed, JSON-encoded frame.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@@ -57,7 +58,7 @@ pub enum Message {
|
||||
/// Spawn parameters sent from parent to runner.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct SpawnRequest {
|
||||
pub command: Vec<String>,
|
||||
pub launch: WindowsProcessLaunch,
|
||||
pub cwd: PathBuf,
|
||||
pub env: HashMap<String, String>,
|
||||
pub permission_profile: PermissionProfile,
|
||||
@@ -212,11 +213,15 @@ mod tests {
|
||||
AbsolutePathBuf::from_absolute_path(PathBuf::from(r"C:\workspace"))
|
||||
.expect("absolute workspace root"),
|
||||
];
|
||||
let launch = WindowsProcessLaunch {
|
||||
application_path: Some(PathBuf::from(r"C:\Windows\System32\cmd.exe")),
|
||||
command: vec!["cmd.exe".to_string(), "/c".to_string(), "ver".to_string()],
|
||||
};
|
||||
let msg = FramedMessage {
|
||||
version: IPC_PROTOCOL_VERSION,
|
||||
message: Message::SpawnRequest {
|
||||
payload: Box::new(SpawnRequest {
|
||||
command: vec!["cmd.exe".to_string(), "/c".to_string(), "ver".to_string()],
|
||||
launch: launch.clone(),
|
||||
cwd: PathBuf::from(r"C:\workspace"),
|
||||
env: HashMap::new(),
|
||||
permission_profile: PermissionProfile::read_only(),
|
||||
@@ -245,6 +250,7 @@ mod tests {
|
||||
};
|
||||
assert_eq!(PermissionProfile::read_only(), payload.permission_profile);
|
||||
assert_eq!(workspace_roots, payload.workspace_roots);
|
||||
assert_eq!(launch, payload.launch);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -43,6 +43,7 @@ mod windows_impl {
|
||||
use crate::logging::log_failure;
|
||||
use crate::logging::log_start;
|
||||
use crate::logging::log_success;
|
||||
use crate::process::WindowsProcessLaunch;
|
||||
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
|
||||
use crate::runner_client::retry_runner_spawn_once;
|
||||
use crate::runner_client::spawn_runner_transport;
|
||||
@@ -182,7 +183,10 @@ mod windows_impl {
|
||||
|
||||
(|| -> Result<CaptureResult> {
|
||||
let spawn_request = SpawnRequest {
|
||||
command: command.clone(),
|
||||
launch: WindowsProcessLaunch {
|
||||
application_path: None,
|
||||
command: command.clone(),
|
||||
},
|
||||
cwd: cwd.to_path_buf(),
|
||||
env: env_map.clone(),
|
||||
permission_profile: permission_profile.clone(),
|
||||
@@ -197,7 +201,7 @@ mod windows_impl {
|
||||
};
|
||||
let transport = retry_runner_spawn_once(
|
||||
sandbox_creds,
|
||||
&spawn_request.command,
|
||||
&spawn_request.launch.command,
|
||||
|sandbox_creds| {
|
||||
spawn_runner_transport(
|
||||
codex_home,
|
||||
|
||||
@@ -164,6 +164,8 @@ pub use conpty::ConptyInstance;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use conpty::spawn_conpty_process_as_user;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use conpty::spawn_conpty_process_as_user_with_launch;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use deny_read_acl::apply_deny_read_acls;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use deny_read_acl::plan_deny_read_acl_paths;
|
||||
@@ -241,12 +243,17 @@ pub use process::StderrMode;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use process::StdinMode;
|
||||
#[cfg(target_os = "windows")]
|
||||
#[doc(hidden)]
|
||||
pub use process::WindowsProcessLaunch;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use process::create_process_as_user;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use process::read_handle_loop;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use process::spawn_process_with_pipes;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use process::spawn_process_with_pipes_with_launch;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use resolved_permissions::ResolvedWindowsSandboxPermissions;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use resolved_permissions::WindowsSandboxTokenMode;
|
||||
@@ -352,7 +359,9 @@ mod windows_impl {
|
||||
use super::WindowsSandboxCancellationToken;
|
||||
use super::logging::log_failure;
|
||||
use super::logging::log_success;
|
||||
use super::process::create_process_as_user;
|
||||
use super::process::CreateProcessAsUserRequest;
|
||||
use super::process::WindowsProcessLaunch;
|
||||
use super::process::create_process_as_user_with_launch;
|
||||
use super::sandbox_utils::ensure_codex_home_exists;
|
||||
use super::spawn_prep::LegacyAclSids;
|
||||
use super::spawn_prep::SpawnPrepOptions;
|
||||
@@ -561,15 +570,21 @@ mod windows_impl {
|
||||
)?;
|
||||
let (stdin_pair, stdout_pair, stderr_pair) = unsafe { setup_stdio_pipes()? };
|
||||
let ((in_r, in_w), (out_r, out_w), (err_r, err_w)) = (stdin_pair, stdout_pair, stderr_pair);
|
||||
let launch = WindowsProcessLaunch {
|
||||
application_path: None,
|
||||
command: command.clone(),
|
||||
};
|
||||
let spawn_res = unsafe {
|
||||
create_process_as_user(
|
||||
create_process_as_user_with_launch(
|
||||
security.h_token,
|
||||
&command,
|
||||
cwd,
|
||||
&env_map,
|
||||
logs_base_dir,
|
||||
Some((in_r, out_w, err_w)),
|
||||
use_private_desktop,
|
||||
CreateProcessAsUserRequest {
|
||||
launch: &launch,
|
||||
cwd,
|
||||
env_map: &env_map,
|
||||
logs_base_dir,
|
||||
stdio: Some((in_r, out_w, err_w)),
|
||||
use_private_desktop,
|
||||
},
|
||||
)
|
||||
};
|
||||
let created = match spawn_res {
|
||||
|
||||
@@ -7,9 +7,12 @@ use crate::winutil::to_wide;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::ptr;
|
||||
use windows_sys::Win32::Foundation::CloseHandle;
|
||||
use windows_sys::Win32::Foundation::GetLastError;
|
||||
@@ -37,6 +40,17 @@ pub struct CreatedProcess {
|
||||
_desktop: LaunchDesktop,
|
||||
}
|
||||
|
||||
/// The command line and optional executable path supplied to a Windows process launch.
|
||||
///
|
||||
/// Callers that have already resolved an executable can set `application_path` so it is passed as
|
||||
/// `lpApplicationName`. A value of `None` preserves Windows' command-line lookup behavior.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[doc(hidden)]
|
||||
pub struct WindowsProcessLaunch {
|
||||
pub application_path: Option<PathBuf>,
|
||||
pub command: Vec<String>,
|
||||
}
|
||||
|
||||
pub fn make_env_block(env: &HashMap<String, String>) -> Vec<u16> {
|
||||
let mut items: Vec<(String, String)> =
|
||||
env.iter().map(|(k, v)| (k.clone(), v.clone())).collect();
|
||||
@@ -73,20 +87,34 @@ unsafe fn ensure_inheritable_stdio(si: &mut STARTUPINFOW) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) struct CreateProcessAsUserRequest<'a> {
|
||||
pub(crate) launch: &'a WindowsProcessLaunch,
|
||||
pub(crate) cwd: &'a Path,
|
||||
pub(crate) env_map: &'a HashMap<String, String>,
|
||||
pub(crate) logs_base_dir: Option<&'a Path>,
|
||||
pub(crate) stdio: Option<(HANDLE, HANDLE, HANDLE)>,
|
||||
pub(crate) use_private_desktop: bool,
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// Caller must provide a valid primary token handle (`h_token`) with appropriate access,
|
||||
/// and the `argv`, `cwd`, and `env_map` must remain valid for the duration of the call.
|
||||
pub unsafe fn create_process_as_user(
|
||||
/// Caller must provide a valid primary token handle (`h_token`) with appropriate access, and all
|
||||
/// borrowed request fields must remain valid for the duration of the call.
|
||||
pub(crate) unsafe fn create_process_as_user_with_launch(
|
||||
h_token: HANDLE,
|
||||
argv: &[String],
|
||||
cwd: &Path,
|
||||
env_map: &HashMap<String, String>,
|
||||
logs_base_dir: Option<&Path>,
|
||||
stdio: Option<(HANDLE, HANDLE, HANDLE)>,
|
||||
use_private_desktop: bool,
|
||||
request: CreateProcessAsUserRequest<'_>,
|
||||
) -> Result<CreatedProcess> {
|
||||
let cmdline_str = argv_to_command_line(argv);
|
||||
let CreateProcessAsUserRequest {
|
||||
launch,
|
||||
cwd,
|
||||
env_map,
|
||||
logs_base_dir,
|
||||
stdio,
|
||||
use_private_desktop,
|
||||
} = request;
|
||||
let cmdline_str = argv_to_command_line(&launch.command);
|
||||
let mut cmdline: Vec<u16> = to_wide(&cmdline_str);
|
||||
let application_name = launch.application_path.as_deref().map(to_wide);
|
||||
let application_name_ptr = application_name.as_ref().map_or(ptr::null(), Vec::as_ptr);
|
||||
let env_block = make_env_block(env_map);
|
||||
let desktop = LaunchDesktop::prepare(use_private_desktop, logs_base_dir)?;
|
||||
let mut pi: PROCESS_INFORMATION = std::mem::zeroed();
|
||||
@@ -123,7 +151,7 @@ pub unsafe fn create_process_as_user(
|
||||
let creation_flags = CREATE_UNICODE_ENVIRONMENT | EXTENDED_STARTUPINFO_PRESENT;
|
||||
let ok = CreateProcessAsUserW(
|
||||
h_token,
|
||||
std::ptr::null(),
|
||||
application_name_ptr,
|
||||
cmdline.as_mut_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
@@ -164,7 +192,7 @@ pub unsafe fn create_process_as_user(
|
||||
let creation_flags = CREATE_UNICODE_ENVIRONMENT;
|
||||
let ok = CreateProcessAsUserW(
|
||||
h_token,
|
||||
std::ptr::null(),
|
||||
application_name_ptr,
|
||||
cmdline.as_mut_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
@@ -199,6 +227,36 @@ pub unsafe fn create_process_as_user(
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// Caller must provide a valid primary token handle (`h_token`) with appropriate access, and the
|
||||
/// borrowed command inputs must remain valid for the duration of the call.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub unsafe fn create_process_as_user(
|
||||
h_token: HANDLE,
|
||||
argv: &[String],
|
||||
cwd: &Path,
|
||||
env_map: &HashMap<String, String>,
|
||||
logs_base_dir: Option<&Path>,
|
||||
stdio: Option<(HANDLE, HANDLE, HANDLE)>,
|
||||
use_private_desktop: bool,
|
||||
) -> Result<CreatedProcess> {
|
||||
let launch = WindowsProcessLaunch {
|
||||
application_path: None,
|
||||
command: argv.to_vec(),
|
||||
};
|
||||
create_process_as_user_with_launch(
|
||||
h_token,
|
||||
CreateProcessAsUserRequest {
|
||||
launch: &launch,
|
||||
cwd,
|
||||
env_map,
|
||||
logs_base_dir,
|
||||
stdio,
|
||||
use_private_desktop,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
/// Controls whether the child's stdin handle is kept open for writing.
|
||||
#[allow(dead_code)]
|
||||
pub enum StdinMode {
|
||||
@@ -234,6 +292,35 @@ pub fn spawn_process_with_pipes(
|
||||
stderr_mode: StderrMode,
|
||||
use_private_desktop: bool,
|
||||
logs_base_dir: Option<&Path>,
|
||||
) -> Result<PipeSpawnHandles> {
|
||||
let launch = WindowsProcessLaunch {
|
||||
application_path: None,
|
||||
command: argv.to_vec(),
|
||||
};
|
||||
spawn_process_with_pipes_with_launch(
|
||||
h_token,
|
||||
&launch,
|
||||
cwd,
|
||||
env_map,
|
||||
stdin_mode,
|
||||
stderr_mode,
|
||||
use_private_desktop,
|
||||
logs_base_dir,
|
||||
)
|
||||
}
|
||||
|
||||
/// Spawns a process with an optional explicit `lpApplicationName`.
|
||||
#[doc(hidden)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn spawn_process_with_pipes_with_launch(
|
||||
h_token: HANDLE,
|
||||
launch: &WindowsProcessLaunch,
|
||||
cwd: &Path,
|
||||
env_map: &HashMap<String, String>,
|
||||
stdin_mode: StdinMode,
|
||||
stderr_mode: StderrMode,
|
||||
use_private_desktop: bool,
|
||||
logs_base_dir: Option<&Path>,
|
||||
) -> Result<PipeSpawnHandles> {
|
||||
let mut in_r: HANDLE = 0;
|
||||
let mut in_w: HANDLE = 0;
|
||||
@@ -266,16 +353,17 @@ pub fn spawn_process_with_pipes(
|
||||
StderrMode::Separate => err_w,
|
||||
};
|
||||
|
||||
let stdio = Some((in_r, out_w, stderr_handle));
|
||||
let spawn_result = unsafe {
|
||||
create_process_as_user(
|
||||
create_process_as_user_with_launch(
|
||||
h_token,
|
||||
argv,
|
||||
cwd,
|
||||
env_map,
|
||||
logs_base_dir,
|
||||
stdio,
|
||||
use_private_desktop,
|
||||
CreateProcessAsUserRequest {
|
||||
launch,
|
||||
cwd,
|
||||
env_map,
|
||||
logs_base_dir,
|
||||
stdio: Some((in_r, out_w, stderr_handle)),
|
||||
use_private_desktop,
|
||||
},
|
||||
)
|
||||
};
|
||||
let created = match spawn_result {
|
||||
|
||||
@@ -10,6 +10,7 @@ use crate::ipc_framed::FramedMessage;
|
||||
use crate::ipc_framed::IPC_PROTOCOL_VERSION;
|
||||
use crate::ipc_framed::Message;
|
||||
use crate::ipc_framed::SpawnRequest;
|
||||
use crate::process::WindowsProcessLaunch;
|
||||
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
|
||||
use crate::runner_client::RunnerTransport;
|
||||
use crate::runner_client::retry_runner_spawn_once;
|
||||
@@ -63,7 +64,7 @@ fn spawn_runner_transport_with_retry<T>(
|
||||
) -> Result<T> {
|
||||
retry_runner_spawn_once(
|
||||
sandbox_creds,
|
||||
&request.spawn_request.command,
|
||||
&request.spawn_request.launch.command,
|
||||
|sandbox_creds| {
|
||||
spawn(
|
||||
&request.codex_home,
|
||||
@@ -112,7 +113,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil
|
||||
permission_profile: &PermissionProfile,
|
||||
workspace_roots: &[AbsolutePathBuf],
|
||||
codex_home: &Path,
|
||||
command: Vec<String>,
|
||||
launch: WindowsProcessLaunch,
|
||||
cwd: &Path,
|
||||
mut env_map: HashMap<String, String>,
|
||||
proxy_enforced: bool,
|
||||
@@ -145,7 +146,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil
|
||||
codex_home,
|
||||
cwd,
|
||||
&mut env_map,
|
||||
&command,
|
||||
&launch.command,
|
||||
read_roots_override,
|
||||
read_roots_include_platform_defaults,
|
||||
write_roots_override,
|
||||
@@ -163,7 +164,7 @@ pub(crate) async fn spawn_windows_sandbox_session_elevated_for_permission_profil
|
||||
env_map: env_map.clone(),
|
||||
logs_base_dir: elevated.logs_base_dir,
|
||||
spawn_request: SpawnRequest {
|
||||
command,
|
||||
launch,
|
||||
cwd: cwd.to_path_buf(),
|
||||
env: env_map,
|
||||
permission_profile: permission_profile.clone(),
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::identity::SandboxCreds;
|
||||
use crate::ipc_framed::ErrorPayload;
|
||||
use crate::ipc_framed::ErrorStage;
|
||||
use crate::ipc_framed::SpawnRequest;
|
||||
use crate::process::WindowsProcessLaunch;
|
||||
use crate::resolved_permissions::ResolvedWindowsSandboxPermissions;
|
||||
use crate::runner_client::RunnerStartupError;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
@@ -68,7 +69,10 @@ fn retry_uses_original_unified_exec_request_and_stops_after_second_failure() {
|
||||
env_map: env_map.clone(),
|
||||
logs_base_dir: Some(PathBuf::from(r"C:\Users\codex\.sandbox")),
|
||||
spawn_request: SpawnRequest {
|
||||
command: vec!["pwsh.exe".to_string(), "-NoProfile".to_string()],
|
||||
launch: WindowsProcessLaunch {
|
||||
application_path: Some(PathBuf::from(r"C:\Program Files\PowerShell\7\pwsh.exe")),
|
||||
command: vec!["pwsh.exe".to_string(), "-NoProfile".to_string()],
|
||||
},
|
||||
cwd: PathBuf::from(r"C:\workspace"),
|
||||
env: env_map.clone(),
|
||||
permission_profile,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use super::windows_common::finish_driver_spawn;
|
||||
use crate::conpty::ConptyInstance;
|
||||
use crate::conpty::spawn_conpty_process_as_user;
|
||||
use crate::conpty::spawn_conpty_process_as_user_with_launch;
|
||||
use crate::desktop::LaunchDesktop;
|
||||
use crate::logging::log_failure;
|
||||
use crate::logging::log_success;
|
||||
use crate::process::StderrMode;
|
||||
use crate::process::StdinMode;
|
||||
use crate::process::WindowsProcessLaunch;
|
||||
use crate::process::read_handle_loop;
|
||||
use crate::process::spawn_process_with_pipes;
|
||||
use crate::process::spawn_process_with_pipes_with_launch;
|
||||
use crate::spawn_prep::LegacyAclSids;
|
||||
use crate::spawn_prep::SpawnPrepOptions;
|
||||
use crate::spawn_prep::allow_null_device_for_workspace_write;
|
||||
@@ -58,7 +59,7 @@ struct LegacyProcessHandles {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn spawn_legacy_process(
|
||||
h_token: HANDLE,
|
||||
command: &[String],
|
||||
launch: &WindowsProcessLaunch,
|
||||
cwd: &Path,
|
||||
env_map: &HashMap<String, String>,
|
||||
use_private_desktop: bool,
|
||||
@@ -70,9 +71,9 @@ fn spawn_legacy_process(
|
||||
logs_base_dir: Option<&Path>,
|
||||
) -> Result<LegacyProcessHandles> {
|
||||
let (pi, output_join, writer_handle, hpc, conpty_owner, desktop) = if tty {
|
||||
let (pi, mut conpty) = spawn_conpty_process_as_user(
|
||||
let (pi, mut conpty) = spawn_conpty_process_as_user_with_launch(
|
||||
h_token,
|
||||
command,
|
||||
launch,
|
||||
cwd,
|
||||
env_map,
|
||||
use_private_desktop,
|
||||
@@ -87,9 +88,9 @@ fn spawn_legacy_process(
|
||||
);
|
||||
(pi, output_join, writer_handle, hpc, Some(conpty), None)
|
||||
} else {
|
||||
let pipe_handles = spawn_process_with_pipes(
|
||||
let pipe_handles = spawn_process_with_pipes_with_launch(
|
||||
h_token,
|
||||
command,
|
||||
launch,
|
||||
cwd,
|
||||
env_map,
|
||||
if stdin_open {
|
||||
@@ -273,7 +274,7 @@ pub(crate) async fn spawn_windows_sandbox_session_legacy(
|
||||
permission_profile: &PermissionProfile,
|
||||
workspace_roots: &[AbsolutePathBuf],
|
||||
codex_home: &Path,
|
||||
command: Vec<String>,
|
||||
launch: WindowsProcessLaunch,
|
||||
cwd: &Path,
|
||||
mut env_map: HashMap<String, String>,
|
||||
timeout_ms: Option<u64>,
|
||||
@@ -289,7 +290,7 @@ pub(crate) async fn spawn_windows_sandbox_session_legacy(
|
||||
codex_home,
|
||||
cwd,
|
||||
&mut env_map,
|
||||
&command,
|
||||
&launch.command,
|
||||
SpawnPrepOptions {
|
||||
inherit_path: false,
|
||||
add_git_safe_directory: false,
|
||||
@@ -354,7 +355,7 @@ pub(crate) async fn spawn_windows_sandbox_session_legacy(
|
||||
desktop,
|
||||
} = match spawn_legacy_process(
|
||||
security.h_token,
|
||||
&command,
|
||||
&launch,
|
||||
cwd,
|
||||
&env_map,
|
||||
use_private_desktop,
|
||||
@@ -377,7 +378,7 @@ pub(crate) async fn spawn_windows_sandbox_session_legacy(
|
||||
|
||||
let process_handle = Arc::new(StdMutex::new(Some(pi.hProcess)));
|
||||
let wait_handle = Arc::clone(&process_handle);
|
||||
let command_for_wait = command.clone();
|
||||
let command_for_wait = launch.command.clone();
|
||||
let hpc_for_wait = hpc_handle.clone();
|
||||
std::thread::spawn(move || {
|
||||
let _desktop = desktop;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
mod backends;
|
||||
|
||||
use crate::process::WindowsProcessLaunch;
|
||||
use anyhow::Result;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
@@ -26,7 +27,7 @@ pub struct WindowsSandboxSessionRequest<'a> {
|
||||
pub permission_profile: &'a PermissionProfile,
|
||||
pub workspace_roots: &'a [AbsolutePathBuf],
|
||||
pub codex_home: &'a Path,
|
||||
pub command: Vec<String>,
|
||||
pub launch: WindowsProcessLaunch,
|
||||
pub cwd: &'a Path,
|
||||
pub env_map: HashMap<String, String>,
|
||||
pub windows_sandbox_level: WindowsSandboxLevel,
|
||||
@@ -53,7 +54,7 @@ pub async fn spawn_windows_sandbox_session_for_level(
|
||||
request.permission_profile,
|
||||
request.workspace_roots,
|
||||
request.codex_home,
|
||||
request.command,
|
||||
request.launch,
|
||||
request.cwd,
|
||||
request.env_map,
|
||||
request.proxy_enforced,
|
||||
@@ -74,7 +75,7 @@ pub async fn spawn_windows_sandbox_session_for_level(
|
||||
request.permission_profile,
|
||||
request.workspace_roots,
|
||||
request.codex_home,
|
||||
request.command,
|
||||
request.launch,
|
||||
request.cwd,
|
||||
request.env_map,
|
||||
request.timeout_ms,
|
||||
@@ -103,11 +104,15 @@ pub async fn spawn_windows_sandbox_session_legacy(
|
||||
stdin_open: bool,
|
||||
use_private_desktop: bool,
|
||||
) -> Result<SpawnedProcess> {
|
||||
let launch = WindowsProcessLaunch {
|
||||
application_path: None,
|
||||
command,
|
||||
};
|
||||
backends::legacy::spawn_windows_sandbox_session_legacy(
|
||||
permission_profile,
|
||||
workspace_roots,
|
||||
codex_home,
|
||||
command,
|
||||
launch,
|
||||
cwd,
|
||||
env_map,
|
||||
timeout_ms,
|
||||
@@ -139,11 +144,15 @@ pub async fn spawn_windows_sandbox_session_elevated_for_permission_profile(
|
||||
stdin_open: bool,
|
||||
use_private_desktop: bool,
|
||||
) -> Result<SpawnedProcess> {
|
||||
let launch = WindowsProcessLaunch {
|
||||
application_path: None,
|
||||
command,
|
||||
};
|
||||
backends::elevated::spawn_windows_sandbox_session_elevated_for_permission_profile(
|
||||
permission_profile,
|
||||
workspace_roots,
|
||||
codex_home,
|
||||
command,
|
||||
launch,
|
||||
cwd,
|
||||
env_map,
|
||||
proxy_enforced,
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#![cfg(target_os = "windows")]
|
||||
|
||||
use super::WindowsSandboxSessionRequest;
|
||||
use super::spawn_windows_sandbox_session_for_level;
|
||||
use super::spawn_windows_sandbox_session_legacy;
|
||||
use crate::WindowsProcessLaunch;
|
||||
use crate::WindowsSandboxCancellationToken;
|
||||
use crate::ipc_framed::Message;
|
||||
use crate::ipc_framed::decode_bytes;
|
||||
use crate::ipc_framed::read_frame;
|
||||
use crate::run_windows_sandbox_capture;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_pty::ProcessDriver;
|
||||
@@ -188,6 +192,56 @@ fn legacy_non_tty_cmd_emits_output() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_non_tty_uses_explicit_application_path() {
|
||||
let _guard = legacy_process_test_guard();
|
||||
let runtime = current_thread_runtime();
|
||||
runtime.block_on(async move {
|
||||
let cwd = sandbox_cwd();
|
||||
let codex_home = sandbox_home("legacy-explicit-application-path");
|
||||
let permission_profile = PermissionProfile::workspace_write();
|
||||
let workspace_roots = workspace_roots_for(cwd.as_path());
|
||||
let spawned = spawn_windows_sandbox_session_for_level(WindowsSandboxSessionRequest {
|
||||
permission_profile: &permission_profile,
|
||||
workspace_roots: workspace_roots.as_slice(),
|
||||
codex_home: codex_home.path(),
|
||||
launch: WindowsProcessLaunch {
|
||||
application_path: Some(PathBuf::from(r"C:\Windows\System32\cmd.exe")),
|
||||
command: vec![
|
||||
"not-the-real-executable.exe".to_string(),
|
||||
"/c".to_string(),
|
||||
"echo EXPLICIT-APPLICATION-PATH".to_string(),
|
||||
],
|
||||
},
|
||||
cwd: cwd.as_path(),
|
||||
env_map: HashMap::new(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
|
||||
proxy_enforced: false,
|
||||
proxy_settings_mode: crate::WindowsSandboxProxySettingsMode::Reconcile,
|
||||
timeout_ms: Some(5_000),
|
||||
read_roots_override: None,
|
||||
read_roots_include_platform_defaults: false,
|
||||
write_roots_override: None,
|
||||
deny_read_paths_override: &[],
|
||||
deny_write_paths_override: &[],
|
||||
tty: false,
|
||||
stdin_open: false,
|
||||
use_private_desktop: true,
|
||||
})
|
||||
.await
|
||||
.expect("spawn with explicit application path");
|
||||
|
||||
let (stdout, exit_code) =
|
||||
collect_stdout_and_exit(spawned, codex_home.path(), Duration::from_secs(10)).await;
|
||||
let stdout = String::from_utf8_lossy(&stdout);
|
||||
assert_eq!(exit_code, 0, "stdout={stdout:?}");
|
||||
assert!(
|
||||
stdout.contains("EXPLICIT-APPLICATION-PATH"),
|
||||
"stdout={stdout:?}"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_non_tty_cmd_rejects_deny_read_overrides() {
|
||||
let _guard = legacy_process_test_guard();
|
||||
|
||||
@@ -177,7 +177,10 @@ async fn run_windows_sandbox_wrapper_request(request: WindowsSandboxWrapperReque
|
||||
permission_profile: &request.permission_profile,
|
||||
workspace_roots: request.workspace_roots.as_slice(),
|
||||
codex_home: request.codex_home.as_path(),
|
||||
command: request.command,
|
||||
launch: crate::WindowsProcessLaunch {
|
||||
application_path: None,
|
||||
command: request.command,
|
||||
},
|
||||
cwd: request.command_cwd.as_path(),
|
||||
env_map: request.env_map,
|
||||
windows_sandbox_level: request.windows_sandbox_level,
|
||||
|
||||
Reference in New Issue
Block a user