From 3b59ead38ca719bfd52f016be9eb27897c3d33ae Mon Sep 17 00:00:00 2001 From: David Wiesen Date: Tue, 5 May 2026 09:57:53 -0700 Subject: [PATCH] fix(windows): avoid WindowsApps shell aliases in sandbox --- codex-rs/core/src/shell.rs | 42 ++++++++++++++++++++++++-------- codex-rs/core/src/shell_tests.rs | 34 ++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 10 deletions(-) diff --git a/codex-rs/core/src/shell.rs b/codex-rs/core/src/shell.rs index 48c760d667..7093b769d5 100644 --- a/codex-rs/core/src/shell.rs +++ b/codex-rs/core/src/shell.rs @@ -162,6 +162,32 @@ fn file_exists(path: &PathBuf) -> Option { } } +fn is_windowsapps_app_execution_alias(path: &std::path::Path) -> bool { + path.to_string_lossy() + .to_ascii_lowercase() + .contains(r#"\appdata\local\microsoft\windowsapps\"#) +} + +fn first_existing_fallback_path(fallback_paths: &[&str]) -> Option { + for path in fallback_paths { + if let Some(path) = file_exists(&PathBuf::from(path)) { + return Some(path); + } + } + + None +} + +fn prefer_real_windows_shell_path(path: PathBuf, fallback_paths: &[&str]) -> PathBuf { + if is_windowsapps_app_execution_alias(&path) + && let Some(fallback) = first_existing_fallback_path(fallback_paths) + { + return fallback; + } + + path +} + fn get_shell_path( shell_type: ShellType, provided_path: Option<&PathBuf>, @@ -180,21 +206,17 @@ fn get_shell_path( && detect_shell_type(&default_shell_path) == Some(shell_type) && file_exists(&default_shell_path).is_some() { - return Some(default_shell_path); + return Some(prefer_real_windows_shell_path( + default_shell_path, + fallback_paths, + )); } if let Ok(path) = which::which(binary_name) { - return Some(path); + return Some(prefer_real_windows_shell_path(path, fallback_paths)); } - for path in fallback_paths { - //check exists - if let Some(path) = file_exists(&PathBuf::from(path)) { - return Some(path); - } - } - - None + first_existing_fallback_path(fallback_paths) } const ZSH_FALLBACK_PATHS: &[&str] = &["/bin/zsh"]; diff --git a/codex-rs/core/src/shell_tests.rs b/codex-rs/core/src/shell_tests.rs index f0974900b2..2438f16bd7 100644 --- a/codex-rs/core/src/shell_tests.rs +++ b/codex-rs/core/src/shell_tests.rs @@ -1,6 +1,7 @@ use super::*; use std::path::PathBuf; use std::process::Command; +use tempfile::tempdir; #[test] #[cfg(target_os = "macos")] @@ -190,3 +191,36 @@ fn finds_powershell() { assert!(shell_path.ends_with("pwsh.exe") || shell_path.ends_with("powershell.exe")); } + +#[test] +fn detects_windowsapps_app_execution_alias() { + assert!(is_windowsapps_app_execution_alias(&PathBuf::from( + r#"C:\Users\alice\AppData\Local\Microsoft\WindowsApps\pwsh.exe"# + ))); + assert!(!is_windowsapps_app_execution_alias(&PathBuf::from( + r#"C:\Program Files\PowerShell\7\pwsh.exe"# + ))); +} + +#[test] +fn prefers_real_shell_binary_over_windowsapps_alias() { + let temp = tempdir().unwrap(); + let fallback = temp.path().join("pwsh.exe"); + std::fs::write(&fallback, b"").unwrap(); + + let selected = prefer_real_windows_shell_path( + PathBuf::from(r#"C:\Users\alice\AppData\Local\Microsoft\WindowsApps\pwsh.exe"#), + &[fallback.to_str().unwrap()], + ); + + assert_eq!(selected, fallback); +} + +#[test] +fn keeps_windowsapps_alias_when_no_real_shell_exists() { + let alias = PathBuf::from(r#"C:\Users\alice\AppData\Local\Microsoft\WindowsApps\pwsh.exe"#); + + let selected = prefer_real_windows_shell_path(alias.clone(), &[]); + + assert_eq!(selected, alias); +}