Wire up the native Windows MXC helper entry point (#44903)

## What changed

- Dispatch `--__codex-windows-mxc` before normal CLI parsing, launch the decoded request, and propagate the child exit code.
- Build the native request from the helper's environment and working directory, discovered Windows platform directories, and volume roots, including UNC working-directory roots. Reject non-Unicode environment values and empty child environments.
- Allow Win32k calls and desktop handles needed for PowerShell startup while retaining clipboard, input-injection, and desktop/system-control restrictions.
- Keep policy, transport, and native launch internals private, and report an explicit error when the helper is invoked outside Windows.

## Testing

Add a wrapper regression test covering exact argument preservation, Windows quoting, separate policy and command working directories, and explicit environment forwarding.

GitOrigin-RevId: 674d0d61a3265bd517b77da9f9de2cd9fad62917
This commit is contained in:
iceweasel-oai
2026-09-11 20:00:37 +00:00
committed by copyberry
parent e3a52b87b2
commit 42cd1ec497
10 changed files with 232 additions and 33 deletions

View File

@@ -100,6 +100,9 @@ pub fn arg0_dispatch() -> Option<Arg0PathEntryGuard> {
}
let argv1 = args.next().unwrap_or_default();
if argv1 == codex_sandboxing::CODEX_WINDOWS_MXC_ARG1 {
codex_sandboxing::run_windows_mxc_main();
}
#[cfg(unix)]
if argv1 == CODEX_ARG0_EXEC_HELPER_ARG1 {
codex_exec_server::run_arg0_exec_helper_main();

View File

@@ -24,6 +24,8 @@ from the canonical Codex permission profile, including protected metadata
carveouts. Supported managed network access allows IPv4 and IPv6 loopback
clients and servers, including the dedicated proxy listeners, while denying
direct non-loopback egress and general inbound network access.
Win32k calls and desktop handles remain available for PowerShell startup;
clipboard, input-injection, and desktop/system-control restrictions remain.
## Launch contract

View File

@@ -1,24 +1,27 @@
//! Native Windows process security environment availability and launch.
//! Native Windows MXC helper. Policy conversion is portable; execution requires
//! a usable process security environment and never enters MXC's ACL fallbacks.
#[cfg(windows)]
pub mod native;
mod native;
#[cfg(any(windows, test))]
mod policy;
mod transport;
#[cfg(windows)]
mod windows;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
use anyhow::Context;
use anyhow::Result;
use anyhow::ensure;
use codex_network_proxy::ManagedNetworkSandboxContext;
use codex_protocol::models::PermissionProfile;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
/// Typed inputs for the native policy adapter.
pub mod transport;
use serde::Deserialize;
use serde::Serialize;
pub const CODEX_WINDOWS_MXC_ARG1: &str = "--__codex-windows-mxc";
const CLIENT_ONLY_LOOPBACK_UNSUPPORTED: &str = "MXC cannot enforce managed networking with allow_local_binding=false: native host-loopback access is bidirectional";
fn validate_managed_network(network: &ManagedNetworkSandboxContext) -> Result<()> {
@@ -38,15 +41,13 @@ fn validate_managed_network(network: &ManagedNetworkSandboxContext) -> Result<()
}
#[derive(Serialize, Deserialize)]
pub struct MxcCommand {
pub permissions: PermissionProfile,
pub sandbox_policy_cwd: PathBuf,
pub managed_network: Option<ManagedNetworkSandboxContext>,
pub command: Vec<String>,
struct MxcCommand {
permissions: PermissionProfile,
sandbox_policy_cwd: PathBuf,
managed_network: Option<ManagedNetworkSandboxContext>,
command: Vec<String>,
}
pub mod policy;
/// Inputs used to build an MXC helper invocation.
#[derive(Debug)]
pub struct CreateMxcCommandArgsParams<'a> {
@@ -98,6 +99,25 @@ pub fn is_available() -> bool {
}
}
/// Entry point dispatched before ordinary Codex CLI parsing.
pub fn run_main() -> ! {
#[cfg(windows)]
{
match windows::run() {
Ok(exit_code) => std::process::exit(exit_code),
Err(error) => {
eprintln!("MXC sandbox: {error:#}");
std::process::exit(1);
}
}
}
#[cfg(not(windows))]
{
eprintln!("MXC sandbox execution requires Windows");
std::process::exit(1);
}
}
#[cfg(test)]
#[path = "policy_tests.rs"]
mod tests;

View File

@@ -16,8 +16,7 @@ use wxc_common::sandbox_process::StdioMode;
#[link(name = "advapi32")]
unsafe extern "system" {}
/// Launch a native-only request and wait for its exit status.
pub fn launch(request: &ExecutionRequest) -> Result<i32> {
pub(super) fn launch(request: &ExecutionRequest) -> Result<i32> {
ensure!(
!request
.policy
@@ -26,17 +25,6 @@ pub fn launch(request: &ExecutionRequest) -> Result<i32> {
.any(|capability| capability.eq_ignore_ascii_case("permissiveLearningMode")),
"MXC native launch does not support permissiveLearningMode"
);
ensure!(
crate::is_available(),
"native MXC is unavailable on this Windows build"
);
ensure!(
!request.policy.least_privilege_mode
&& !request.policy.network_proxy.is_enabled()
&& request.policy.capture_denials.is_none()
&& !request.policy.fallback.allow_dacl_mutation,
"MXC native launch does not support fallback policies"
);
// With no least-privilege mode, legacy proxy, or capture enabled, this
// probe guarantees BaseContainerRunner chooses PSEC rather than SBOX.
if !request.policy.denied_paths.is_empty() {

View File

@@ -21,6 +21,7 @@ use wxc_common::filesystem_object::compare_existing_filesystem_objects;
use wxc_common::filesystem_object::normalize_object_conflicts;
use wxc_common::logger::Logger;
use wxc_common::logger::Mode;
use wxc_common::models::BaseProcessUiConfig;
use wxc_common::models::ContainerPolicy;
use wxc_common::models::ExecutionRequest;
use wxc_common::models::FallbackPolicy;
@@ -31,6 +32,7 @@ use wxc_common::models::NetworkIngressPolicy;
use wxc_common::models::NetworkPeer;
use wxc_common::models::NetworkPolicy;
use wxc_common::models::NetworkRule;
use wxc_common::models::UiPolicy;
use crate::MxcCommand;
@@ -67,7 +69,7 @@ pub enum PolicyError {
CommandLine(#[from] CommandLineError),
}
pub fn build_request(
pub(super) fn build_request(
command: &MxcCommand,
command_cwd: &Path,
env: Vec<String>,
@@ -290,6 +292,16 @@ pub fn build_request(
}),
network_specified: true,
network_mode_specified: true,
// PowerShell needs Win32k and desktop handles during DLL startup.
// Keep clipboard, input injection, and system-control restrictions.
ui: UiPolicy {
disable: false,
..Default::default()
},
base_process_ui: BaseProcessUiConfig {
isolation: "desktop".to_owned(),
..Default::default()
},
..Default::default()
},
..Default::default()

View File

@@ -106,6 +106,50 @@ fn command(permissions: &PermissionProfile, cwd: &Path) -> MxcCommand {
}
}
#[test]
fn wrapper_preserves_exact_argv_and_separate_command_cwd() -> Result<()> {
let root = tempfile::tempdir()?;
let cwd = root.path().join("command-cwd");
let profile = PermissionProfile::from_runtime_permissions(
&FileSystemSandboxPolicy::restricted(Vec::new()),
NetworkSandboxPolicy::Restricted,
);
let argv = vec![
r"C:\Program Files\tool.exe".to_owned(),
"--permissions".to_owned(),
String::new(),
"quotes\" and slash\\".to_owned(),
];
let mut env = HashMap::new();
let wrapped = create_command_args(CreateMxcCommandArgsParams {
command: argv.clone(),
permission_profile: &profile,
sandbox_policy_cwd: root.path(),
managed_network: None,
env: &mut env,
})?;
assert_eq!(wrapped, vec![crate::CODEX_WINDOWS_MXC_ARG1]);
let parsed = crate::transport::decode(&mut env)?;
assert_eq!(
(
&parsed.permissions,
parsed.sandbox_policy_cwd.as_path(),
&parsed.command
),
(&profile, root.path(), &argv)
);
let request = build_request(&parsed, &cwd, vec!["CUSTOM=value".to_owned()], &[], &[])?;
assert_eq!(
(request.script_code, request.working_directory, request.env),
(
r#""C:\Program Files\tool.exe" --permissions "" "quotes\" and slash\\""#.to_owned(),
cwd.to_str().unwrap().to_owned(),
vec!["CUSTOM=value".to_owned()],
)
);
Ok(())
}
#[test]
fn empty_command_is_rejected_before_native_launch() -> Result<()> {
let root = tempfile::tempdir()?;

View File

@@ -23,7 +23,7 @@ fn is_transport_key(key: &str) -> bool {
.is_some_and(|start| start.eq_ignore_ascii_case(PREFIX.as_bytes()))
}
pub fn encode(command: &MxcCommand, env: &mut HashMap<String, String>) -> Result<()> {
pub(super) fn encode(command: &MxcCommand, env: &mut HashMap<String, String>) -> Result<()> {
let payload = serde_json::to_string(command)?;
ensure!(
payload.len() <= MAX_BYTES,
@@ -53,7 +53,7 @@ pub fn encode(command: &MxcCommand, env: &mut HashMap<String, String>) -> Result
}
#[cfg(any(windows, test))]
pub fn decode(env: &mut HashMap<String, String>) -> Result<MxcCommand> {
pub(super) fn decode(env: &mut HashMap<String, String>) -> Result<MxcCommand> {
let mut encoded = HashMap::new();
let mut duplicate = false;
let mut oversized = false;

View File

@@ -0,0 +1,128 @@
//! Resolve executor-local state and launch the decoded MXC helper request.
use std::collections::HashMap;
use std::path::PathBuf;
use anyhow::Result;
use anyhow::ensure;
use windows_sys::Win32::Foundation::RPC_E_CHANGED_MODE;
use windows_sys::Win32::Storage::FileSystem::GetLogicalDrives;
use windows_sys::Win32::System::Com::COINIT_MULTITHREADED;
use windows_sys::Win32::System::Com::CoInitializeEx;
use windows_sys::Win32::System::Com::CoTaskMemFree;
use windows_sys::Win32::System::Com::CoUninitialize;
use windows_sys::Win32::System::SystemInformation::GetWindowsDirectoryW;
use windows_sys::Win32::UI::Shell::FOLDERID_ProgramData;
use windows_sys::Win32::UI::Shell::FOLDERID_ProgramFiles;
use windows_sys::Win32::UI::Shell::FOLDERID_ProgramFilesX86;
use windows_sys::Win32::UI::Shell::SHGetKnownFolderPath;
struct ComUninitializeGuard;
impl Drop for ComUninitializeGuard {
fn drop(&mut self) {
unsafe { CoUninitialize() };
}
}
pub(super) fn run() -> Result<i32> {
ensure!(
crate::is_available(),
"native MXC is unavailable on this Windows build"
);
// Read the already-filtered wrapper environment, rejecting lossy values
// instead of inheriting a different environment inside the sandbox.
let mut env = std::env::vars_os()
.map(|(key, value)| {
let key = key
.into_string()
.map_err(|_| anyhow::anyhow!("MXC requires Unicode environment variable names"))?;
let value = value
.into_string()
.map_err(|_| anyhow::anyhow!("MXC requires Unicode environment variable values"))?;
Ok((key, value))
})
.collect::<Result<HashMap<_, _>>>()?;
let command = crate::transport::decode(&mut env)?;
ensure!(
!env.is_empty(),
"MXC requires an explicit child environment"
);
let mask = unsafe { GetLogicalDrives() };
ensure!(
mask != 0,
"cannot enumerate Windows volumes: {}",
std::io::Error::last_os_error()
);
let mut volumes: Vec<PathBuf> = (0..26)
.filter(|index| mask & (1 << index) != 0)
.map(|index| PathBuf::from(format!("{}:\\", (b'A' + index as u8) as char)))
.collect();
let command_cwd = std::env::current_dir()?;
// An explicit UNC cwd has no drive letter and is absent from GetLogicalDrives.
for cwd in [&command.sandbox_policy_cwd, &command_cwd] {
if let Some(root) = cwd.ancestors().last() {
volumes.push(root.to_path_buf());
}
}
let request = crate::policy::build_request(
&command,
&command_cwd,
env.into_iter()
.map(|(key, value)| format!("{key}={value}"))
.collect(),
&volumes,
&platform_read_roots()?,
)?;
crate::native::launch(&request)
}
fn platform_read_roots() -> Result<Vec<PathBuf>> {
use std::os::windows::ffi::OsStringExt;
let mut buffer = vec![0u16; 32768];
let length = unsafe { GetWindowsDirectoryW(buffer.as_mut_ptr(), buffer.len() as u32) };
ensure!(
length > 0 && (length as usize) < buffer.len(),
"cannot locate the Windows directory"
);
let com_status = unsafe { CoInitializeEx(std::ptr::null(), COINIT_MULTITHREADED as u32) };
ensure!(
com_status >= 0 || com_status == RPC_E_CHANGED_MODE,
"cannot initialize COM: HRESULT {com_status:#010x}"
);
let _com = (com_status >= 0).then(|| ComUninitializeGuard);
let mut roots = vec![PathBuf::from(std::ffi::OsString::from_wide(
&buffer[..length as usize],
))];
for folder in [
FOLDERID_ProgramFiles,
FOLDERID_ProgramFilesX86,
FOLDERID_ProgramData,
] {
let mut path = std::ptr::null_mut();
let status = unsafe {
SHGetKnownFolderPath(&folder, /*dwflags*/ 0, std::ptr::null_mut(), &mut path)
};
if status >= 0 && !path.is_null() {
let mut length = 0;
unsafe {
while *path.add(length) != 0 {
length += 1;
}
roots.push(PathBuf::from(std::ffi::OsString::from_wide(
std::slice::from_raw_parts(path, length),
)));
CoTaskMemFree(path.cast());
}
} else {
if !path.is_null() {
unsafe { CoTaskMemFree(path.cast()) };
}
anyhow::bail!(
"cannot locate required Windows platform directory: HRESULT {status:#010x}"
);
}
}
Ok(roots)
}

View File

@@ -14,6 +14,7 @@ workspace = true
[dependencies]
anyhow = { workspace = true }
codex-mxc-sandbox = { workspace = true }
codex-network-proxy = { workspace = true }
codex-protocol = { workspace = true }
codex-utils-absolute-path = { workspace = true }
@@ -30,7 +31,6 @@ url = { workspace = true }
which = { workspace = true }
[target.'cfg(windows)'.dependencies]
codex-mxc-sandbox = { workspace = true }
codex-otel = { workspace = true }
codex-utils-home-dir = { workspace = true }

View File

@@ -17,6 +17,8 @@ mod windows_mxc;
pub use bwrap::find_system_bwrap_in_path;
#[cfg(target_os = "linux")]
pub use bwrap::system_bwrap_warning;
pub use codex_mxc_sandbox::CODEX_WINDOWS_MXC_ARG1;
pub use codex_mxc_sandbox::run_main as run_windows_mxc_main;
pub use codex_windows_sandbox::WindowsSandboxProxySettingsMode;
pub use denial::is_likely_executor_managed_sandbox_denied;
pub use denial::is_likely_sandbox_denied;