mirror of
https://github.com/openai/codex.git
synced 2026-09-05 15:18:41 +00:00
Fix Windows sandbox process application resolution
This commit is contained in:
@@ -92,5 +92,8 @@ features = [
|
||||
]
|
||||
version = "0.52"
|
||||
|
||||
[target.'cfg(windows)'.dependencies.which]
|
||||
workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { workspace = true }
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::logging;
|
||||
use crate::proc_thread_attr::ProcThreadAttributeList;
|
||||
use crate::winutil::argv_to_command_line;
|
||||
use crate::winutil::format_last_error;
|
||||
use crate::winutil::resolve_application_path;
|
||||
use crate::winutil::to_wide;
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
@@ -86,6 +87,8 @@ pub unsafe fn create_process_as_user(
|
||||
) -> Result<CreatedProcess> {
|
||||
let cmdline_str = argv_to_command_line(argv);
|
||||
let mut cmdline: Vec<u16> = to_wide(&cmdline_str);
|
||||
let application_path = resolve_application_path(argv, env_map, cwd);
|
||||
let application_name = application_path.as_deref().map(to_wide);
|
||||
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();
|
||||
@@ -122,7 +125,9 @@ 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
|
||||
.as_ref()
|
||||
.map_or(std::ptr::null(), |wide| wide.as_ptr()),
|
||||
cmdline.as_mut_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
@@ -136,9 +141,12 @@ pub unsafe fn create_process_as_user(
|
||||
if ok == 0 {
|
||||
let err = GetLastError() as i32;
|
||||
let msg = format!(
|
||||
"CreateProcessAsUserW failed: {} ({}) | cwd={} | cmd={} | env_u16_len={} | si_flags={} | creation_flags={}",
|
||||
"CreateProcessAsUserW failed: {} ({}) | app={} | cwd={} | cmd={} | env_u16_len={} | si_flags={} | creation_flags={}",
|
||||
err,
|
||||
format_last_error(err),
|
||||
application_path
|
||||
.as_deref()
|
||||
.map_or("<unresolved>", |path| path.to_string_lossy().as_ref()),
|
||||
cwd.display(),
|
||||
cmdline_str,
|
||||
env_block_len,
|
||||
@@ -163,7 +171,9 @@ pub unsafe fn create_process_as_user(
|
||||
let creation_flags = CREATE_UNICODE_ENVIRONMENT;
|
||||
let ok = CreateProcessAsUserW(
|
||||
h_token,
|
||||
std::ptr::null(),
|
||||
application_name
|
||||
.as_ref()
|
||||
.map_or(std::ptr::null(), |wide| wide.as_ptr()),
|
||||
cmdline.as_mut_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
@@ -177,9 +187,12 @@ pub unsafe fn create_process_as_user(
|
||||
if ok == 0 {
|
||||
let err = GetLastError() as i32;
|
||||
let msg = format!(
|
||||
"CreateProcessAsUserW failed: {} ({}) | cwd={} | cmd={} | env_u16_len={} | si_flags={} | creation_flags={}",
|
||||
"CreateProcessAsUserW failed: {} ({}) | app={} | cwd={} | cmd={} | env_u16_len={} | si_flags={} | creation_flags={}",
|
||||
err,
|
||||
format_last_error(err),
|
||||
application_path
|
||||
.as_deref()
|
||||
.map_or("<unresolved>", |path| path.to_string_lossy().as_ref()),
|
||||
cwd.display(),
|
||||
cmdline_str,
|
||||
env_block_len,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use anyhow::Result;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::ffi::OsString;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use windows_sys::Win32::Foundation::ERROR_INSUFFICIENT_BUFFER;
|
||||
use windows_sys::Win32::Foundation::GetLastError;
|
||||
use windows_sys::Win32::Foundation::HLOCAL;
|
||||
@@ -73,6 +77,25 @@ pub fn argv_to_command_line(argv: &[String]) -> String {
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn resolve_application_path(
|
||||
argv: &[String],
|
||||
env_map: &HashMap<String, String>,
|
||||
cwd: &Path,
|
||||
) -> Option<PathBuf> {
|
||||
let program = argv.first().filter(|program| !program.is_empty())?;
|
||||
let candidate = Path::new(program);
|
||||
if candidate.components().count() > 1 {
|
||||
return Some(candidate.to_path_buf());
|
||||
}
|
||||
|
||||
let search_path = env_map
|
||||
.iter()
|
||||
.find(|(key, _)| key.eq_ignore_ascii_case("PATH"))
|
||||
.map(|(_, value)| OsString::from(value));
|
||||
which::which_in(program, search_path, cwd).ok()
|
||||
}
|
||||
|
||||
// Produce a readable description for a Win32 error code.
|
||||
pub fn format_last_error(err: i32) -> String {
|
||||
unsafe {
|
||||
@@ -208,7 +231,11 @@ fn sid_bytes_from_string(sid_str: &str) -> Result<Vec<u8>> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::argv_to_command_line;
|
||||
use super::resolve_application_path;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn argv_to_command_line_quotes_each_argument_independently() {
|
||||
@@ -238,4 +265,19 @@ mod tests {
|
||||
"pwsh.exe -Command \"Write-Output \\\"hello world\\\"\""
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[test]
|
||||
fn resolve_application_path_preserves_explicit_paths() {
|
||||
let resolved = resolve_application_path(
|
||||
&[r"C:\Program Files\Git\cmd\git.exe".to_string()],
|
||||
&HashMap::new(),
|
||||
Path::new(r"C:\"),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
resolved,
|
||||
Some(PathBuf::from(r"C:\Program Files\Git\cmd\git.exe"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user