mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Resolve Windows sandbox PowerShell fallbacks on the exec server (#49098)
## Why Remote controllers cannot resolve a sandbox-compatible PowerShell executable on the execution host. ## What changed Apply the existing PowerShell fallback during exec-server request preparation for the `Elevated` and `Mxc` Windows sandbox backends. Match `pwsh` and `powershell` executable names case-insensitively and replace inaccessible Store PowerShell paths when a compatible executable is available. ## Testing Add Windows RPC regression tests for lowercase and mixed-case PowerShell aliases with a restricted server `PATH`, verifying successful command execution in the `Mxc` sandbox. GitOrigin-RevId: 365541af2f026a662b1988429f5d182c98e4a236
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
#[cfg(windows)]
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::process_telemetry::ProcessTelemetry;
|
||||
@@ -30,6 +32,8 @@ use codex_sandboxing::resolve_windows_elevated_filesystem_overrides;
|
||||
use codex_sandboxing::resolve_windows_restricted_token_filesystem_overrides;
|
||||
use codex_sandboxing::windows_sandbox_uses_elevated_backend;
|
||||
use codex_sandboxing::with_managed_mitm_ca_readable_root;
|
||||
#[cfg(windows)]
|
||||
use codex_shell_command::shell_detect::fallback_powershell_shell_for_windows_sandbox;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
|
||||
@@ -245,6 +249,20 @@ pub(crate) async fn prepare_exec_request_with_telemetry(
|
||||
);
|
||||
#[cfg(not(unix))]
|
||||
let (program, args) = (program.into(), args.to_vec());
|
||||
#[cfg(windows)]
|
||||
let program = if matches!(
|
||||
sandbox_context.windows_sandbox_selection,
|
||||
WindowsSandboxSelection::Elevated | WindowsSandboxSelection::Mxc
|
||||
) && Path::new(&program).file_stem().is_some_and(|name| {
|
||||
name.eq_ignore_ascii_case("pwsh") || name.eq_ignore_ascii_case("powershell")
|
||||
}) && let Some(fallback) =
|
||||
fallback_powershell_shell_for_windows_sandbox(Path::new(&program))
|
||||
{
|
||||
// Remote controllers cannot resolve a sandbox-compatible shell on this host.
|
||||
fallback.shell_path.into_os_string()
|
||||
} else {
|
||||
program
|
||||
};
|
||||
let transform_request = SandboxDirectSpawnTransformRequest {
|
||||
workspace_roots,
|
||||
windows_sandbox_proxy_settings_mode,
|
||||
|
||||
@@ -10,6 +10,73 @@ use codex_protocol::permissions::FileSystemSpecialPath;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
/// Windows alias casing must not prevent fallback when the primary runtime is absent from PATH.
|
||||
#[test_case::test_case("pwsh.exe"; "lowercase_pwsh")]
|
||||
#[test_case::test_case("PWSH.exe"; "uppercase_pwsh")]
|
||||
#[test_case::test_case("powershell.exe"; "lowercase_powershell")]
|
||||
#[test_case::test_case("PowerShell.exe"; "mixed_case_powershell")]
|
||||
#[cfg_attr(not(windows), ignore = "requires a native Windows sandbox")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
#[serial_test::serial(remote_exec_server)]
|
||||
async fn powershell_alias_falls_back_without_primary_runtime_over_rpc(
|
||||
shell_name: &str,
|
||||
) -> Result<()> {
|
||||
crate::skip_if_mxc_unavailable!(Ok(()));
|
||||
let root = TempDir::new()?;
|
||||
let system32 = std::path::PathBuf::from(std::env::var("SystemRoot")?).join("System32");
|
||||
let server =
|
||||
common::exec_server::exec_server_with_env([("PATH", system32.as_os_str())], &[]).await?;
|
||||
let environment = Environment::create_for_tests(Some(server.websocket_url().to_owned()))?;
|
||||
let cwd = PathUri::from_host_native_path(root.path())?;
|
||||
let mut sandbox = FileSystemSandboxContext::from_permission_profile(
|
||||
PermissionProfile::read_only(),
|
||||
cwd.clone(),
|
||||
);
|
||||
sandbox.windows_sandbox_selection = codex_exec_server::WindowsSandboxSelection::Mxc;
|
||||
let started = environment
|
||||
.get_exec_backend()
|
||||
.start(ExecParams {
|
||||
process_id: ProcessId::from("windows-powershell-fallback"),
|
||||
metadata: None,
|
||||
argv: vec![
|
||||
root.path()
|
||||
.join("WindowsApps")
|
||||
.join(shell_name)
|
||||
.to_string_lossy()
|
||||
.into_owned(),
|
||||
"-NoProfile".to_owned(),
|
||||
"-Command".to_owned(),
|
||||
"Write-Output 'fallback-ok'".to_owned(),
|
||||
],
|
||||
cwd,
|
||||
env_policy: None,
|
||||
shell_snapshot: None,
|
||||
env: HashMap::new(),
|
||||
tty: false,
|
||||
pipe_stdin: false,
|
||||
arg0: None,
|
||||
sandbox: Some(sandbox),
|
||||
enforce_managed_network: false,
|
||||
managed_network: None,
|
||||
network_proxy: None,
|
||||
})
|
||||
.await?;
|
||||
assert_eq!(
|
||||
started.sandbox_type,
|
||||
Some(codex_sandboxing::SandboxType::WindowsMxc)
|
||||
);
|
||||
let (stdout, stderr, exit_code, exited) = collect_process_output_from_events_with_timeout(
|
||||
started.process,
|
||||
Duration::from_secs(/*secs*/ 30),
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(
|
||||
(stdout.trim(), stderr, exit_code, exited),
|
||||
("fallback-ok", String::new(), Some(0), true)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg_attr(not(windows), ignore = "requires a native Windows sandbox")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
#[serial_test::serial(remote_exec_server)]
|
||||
|
||||
Reference in New Issue
Block a user