mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Use compatible PowerShell for elevated Windows sandbox commands (#41227)
## Why Microsoft Store PowerShell executables under `WindowsApps` can be inaccessible to the dedicated elevated sandbox account. ## What changed - For local elevated sandbox commands that target Store PowerShell, select the first compatible `pwsh.exe` or `powershell.exe` from `PATH` or the standard fallback locations. - Continue adding `-NoProfile`, and preserve the selected shell for remote execution and other sandbox modes. ## Testing Added unit coverage for Store-path filtering, fallback ordering, local replacement, and remote-path preservation. GitOrigin-RevId: 8ab3d0db925e886879701445040f13902fb1ad24
This commit is contained in:
1 parent
dd453867fa
commit
426fa8cdab
3 files changed
+254
-15
No files matched your search
@@ -27,7 +27,6 @@ use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::shell_environment::is_non_inheritable_env_var;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::HashMap;
|
||||
#[cfg(unix)]
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) mod apply_patch;
|
||||
@@ -144,11 +143,34 @@ pub(crate) fn apply_zsh_fork_path_prepend(
|
||||
runtime_path_prepends.prepend(env, zsh_bin_dir);
|
||||
}
|
||||
|
||||
pub(crate) fn disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
pub(crate) fn prepare_powershell_command_for_elevated_windows_sandbox(
|
||||
command: &[String],
|
||||
shell_type: Option<&ShellType>,
|
||||
sandbox_requested: bool,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
environment_is_remote: bool,
|
||||
) -> Vec<String> {
|
||||
prepare_powershell_command_for_elevated_windows_sandbox_with_fallback(
|
||||
command,
|
||||
shell_type,
|
||||
sandbox_requested,
|
||||
windows_sandbox_level,
|
||||
environment_is_remote,
|
||||
|path| {
|
||||
codex_shell_command::shell_detect::fallback_powershell_shell_for_elevated_windows_sandbox(
|
||||
path,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn prepare_powershell_command_for_elevated_windows_sandbox_with_fallback(
|
||||
command: &[String],
|
||||
shell_type: Option<&ShellType>,
|
||||
sandbox_requested: bool,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
environment_is_remote: bool,
|
||||
find_fallback: impl FnOnce(&Path) -> Option<codex_shell_command::shell_detect::DetectedShell>,
|
||||
) -> Vec<String> {
|
||||
if shell_type != Some(&ShellType::PowerShell)
|
||||
|| !sandbox_requested
|
||||
@@ -158,17 +180,21 @@ pub(crate) fn disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
return command.to_vec();
|
||||
}
|
||||
|
||||
let mut command = command.to_vec();
|
||||
if !environment_is_remote && let Some(fallback) = find_fallback(Path::new(&command[0])) {
|
||||
command[0] = fallback.shell_path.to_string_lossy().to_string();
|
||||
}
|
||||
|
||||
if command[1..]
|
||||
.iter()
|
||||
.any(|arg| arg.eq_ignore_ascii_case("-NoProfile"))
|
||||
{
|
||||
return command.to_vec();
|
||||
return command;
|
||||
}
|
||||
|
||||
// The elevated Windows sandbox runs as a dedicated sandbox account while
|
||||
// HOME/USERPROFILE may still point at the real user profile. Loading
|
||||
// PowerShell profiles in that mixed context is not a valid login shell.
|
||||
let mut command = command.to_vec();
|
||||
command.insert(1, "-NoProfile".to_string());
|
||||
command
|
||||
}
|
||||
@@ -395,7 +421,7 @@ fn shell_single_quote(input: &str) -> String {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod disable_powershell_profile_tests {
|
||||
mod prepare_powershell_command_tests {
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
@@ -407,11 +433,12 @@ mod disable_powershell_profile_tests {
|
||||
"Write-Output ok".to_string(),
|
||||
];
|
||||
|
||||
let rewritten = disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
let rewritten = prepare_powershell_command_for_elevated_windows_sandbox(
|
||||
&command,
|
||||
Some(&ShellType::PowerShell),
|
||||
/*sandbox_requested*/ true,
|
||||
WindowsSandboxLevel::Elevated,
|
||||
/*environment_is_remote*/ false,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -433,11 +460,12 @@ mod disable_powershell_profile_tests {
|
||||
"VwByAGkAdABlAC0ATwB1AHQAcAB1AHQAIABvAGsA".to_string(),
|
||||
];
|
||||
|
||||
let rewritten = disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
let rewritten = prepare_powershell_command_for_elevated_windows_sandbox(
|
||||
&command,
|
||||
Some(&ShellType::PowerShell),
|
||||
/*sandbox_requested*/ true,
|
||||
WindowsSandboxLevel::Elevated,
|
||||
/*environment_is_remote*/ false,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -460,11 +488,12 @@ mod disable_powershell_profile_tests {
|
||||
"Write-Output ok".to_string(),
|
||||
];
|
||||
|
||||
let rewritten = disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
let rewritten = prepare_powershell_command_for_elevated_windows_sandbox(
|
||||
&command,
|
||||
Some(&ShellType::PowerShell),
|
||||
/*sandbox_requested*/ true,
|
||||
WindowsSandboxLevel::Elevated,
|
||||
/*environment_is_remote*/ false,
|
||||
);
|
||||
|
||||
assert_eq!(rewritten, command);
|
||||
@@ -473,16 +502,18 @@ mod disable_powershell_profile_tests {
|
||||
#[test]
|
||||
fn leaves_legacy_restricted_token_backend_alone() {
|
||||
let command = vec![
|
||||
"powershell.exe".to_string(),
|
||||
r"C:\Program Files\WindowsApps\Microsoft.PowerShell_7.6.4.0_x64__8wekyb3d8bbwe\pwsh.exe"
|
||||
.to_string(),
|
||||
"-Command".to_string(),
|
||||
"Write-Output ok".to_string(),
|
||||
];
|
||||
|
||||
let rewritten = disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
let rewritten = prepare_powershell_command_for_elevated_windows_sandbox(
|
||||
&command,
|
||||
Some(&ShellType::PowerShell),
|
||||
/*sandbox_requested*/ true,
|
||||
WindowsSandboxLevel::RestrictedToken,
|
||||
/*environment_is_remote*/ false,
|
||||
);
|
||||
|
||||
assert_eq!(rewritten, command);
|
||||
@@ -491,16 +522,18 @@ mod disable_powershell_profile_tests {
|
||||
#[test]
|
||||
fn leaves_unsandboxed_attempts_alone() {
|
||||
let command = vec![
|
||||
"powershell.exe".to_string(),
|
||||
r"C:\Program Files\WindowsApps\Microsoft.PowerShell_7.6.4.0_x64__8wekyb3d8bbwe\pwsh.exe"
|
||||
.to_string(),
|
||||
"-Command".to_string(),
|
||||
"Write-Output ok".to_string(),
|
||||
];
|
||||
|
||||
let rewritten = disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
let rewritten = prepare_powershell_command_for_elevated_windows_sandbox(
|
||||
&command,
|
||||
Some(&ShellType::PowerShell),
|
||||
/*sandbox_requested*/ false,
|
||||
WindowsSandboxLevel::Elevated,
|
||||
/*environment_is_remote*/ false,
|
||||
);
|
||||
|
||||
assert_eq!(rewritten, command);
|
||||
@@ -514,15 +547,88 @@ mod disable_powershell_profile_tests {
|
||||
"echo ok".to_string(),
|
||||
];
|
||||
|
||||
let rewritten = disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
let rewritten = prepare_powershell_command_for_elevated_windows_sandbox(
|
||||
&command,
|
||||
Some(&ShellType::Bash),
|
||||
/*sandbox_requested*/ true,
|
||||
WindowsSandboxLevel::Elevated,
|
||||
/*environment_is_remote*/ false,
|
||||
);
|
||||
|
||||
assert_eq!(rewritten, command);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_elevated_powershell_uses_discovered_fallback() {
|
||||
let command = vec![
|
||||
r"C:\Program Files\WindowsApps\Microsoft.PowerShell_7.6.4.0_x64__8wekyb3d8bbwe\pwsh.exe"
|
||||
.to_string(),
|
||||
"-Command".to_string(),
|
||||
"Write-Output ok".to_string(),
|
||||
];
|
||||
let fallback_path = std::path::PathBuf::from(r"C:\Program Files\PowerShell\7\pwsh.exe");
|
||||
|
||||
let rewritten = prepare_powershell_command_for_elevated_windows_sandbox_with_fallback(
|
||||
&command,
|
||||
Some(&ShellType::PowerShell),
|
||||
/*sandbox_requested*/ true,
|
||||
WindowsSandboxLevel::Elevated,
|
||||
/*environment_is_remote*/ false,
|
||||
|_| {
|
||||
Some(codex_shell_command::shell_detect::DetectedShell {
|
||||
shell_type: ShellType::PowerShell,
|
||||
shell_path: fallback_path.clone(),
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
rewritten,
|
||||
vec![
|
||||
fallback_path.to_string_lossy().to_string(),
|
||||
"-NoProfile".to_string(),
|
||||
"-Command".to_string(),
|
||||
"Write-Output ok".to_string(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remote_elevated_powershell_keeps_remote_store_path_and_no_profile() {
|
||||
let command = vec![
|
||||
r"C:\Program Files\WindowsApps\Microsoft.PowerShell_7.6.4.0_x64__8wekyb3d8bbwe\pwsh.exe"
|
||||
.to_string(),
|
||||
"-Command".to_string(),
|
||||
"Write-Output ok".to_string(),
|
||||
];
|
||||
|
||||
let mut discovery_called = false;
|
||||
let rewritten = prepare_powershell_command_for_elevated_windows_sandbox_with_fallback(
|
||||
&command,
|
||||
Some(&ShellType::PowerShell),
|
||||
/*sandbox_requested*/ true,
|
||||
WindowsSandboxLevel::Elevated,
|
||||
/*environment_is_remote*/ true,
|
||||
|_| {
|
||||
discovery_called = true;
|
||||
Some(codex_shell_command::shell_detect::DetectedShell {
|
||||
shell_type: ShellType::PowerShell,
|
||||
shell_path: std::path::PathBuf::from(r"C:\Program Files\PowerShell\7\pwsh.exe"),
|
||||
})
|
||||
},
|
||||
);
|
||||
|
||||
assert!(!discovery_called);
|
||||
assert_eq!(
|
||||
rewritten,
|
||||
vec![
|
||||
command[0].clone(),
|
||||
"-NoProfile".to_string(),
|
||||
"-Command".to_string(),
|
||||
"Write-Output ok".to_string(),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, unix))]
|
||||
|
||||
@@ -20,9 +20,9 @@ use crate::tools::network_approval::NetworkApprovalSpec;
|
||||
use crate::tools::runtimes::RuntimePathPrepends;
|
||||
#[cfg(unix)]
|
||||
use crate::tools::runtimes::apply_zsh_fork_path_prepend;
|
||||
use crate::tools::runtimes::disable_powershell_profile_for_elevated_windows_sandbox;
|
||||
use crate::tools::runtimes::exec_env_for_sandbox_permissions;
|
||||
use crate::tools::runtimes::maybe_wrap_shell_lc_with_snapshot;
|
||||
use crate::tools::runtimes::prepare_powershell_command_for_elevated_windows_sandbox;
|
||||
use crate::tools::runtimes::zsh_fork;
|
||||
use crate::tools::sandboxing::Approvable;
|
||||
use crate::tools::sandboxing::ApprovalAction;
|
||||
@@ -399,11 +399,12 @@ impl<'a> ToolRuntime<UnifiedExecRequest, UnifiedExecAttempt> for UnifiedExecRunt
|
||||
*script = format!("{exports}\n{script}");
|
||||
}
|
||||
}
|
||||
let command = disable_powershell_profile_for_elevated_windows_sandbox(
|
||||
let command = prepare_powershell_command_for_elevated_windows_sandbox(
|
||||
&command,
|
||||
Some(&req.shell_type),
|
||||
attempt.sandbox_requested,
|
||||
attempt.windows_sandbox_level,
|
||||
environment_is_remote,
|
||||
);
|
||||
let command = if matches!(req.shell_type, ShellType::PowerShell) {
|
||||
prefix_powershell_script_with_utf8(&command)
|
||||
|
||||
Reference in new issue
Block a user