Add a native Windows MXC sandbox adapter (#42841)

## What changed

- Add `codex-mxc-sandbox` with native MXC availability detection and a launcher that inherits standard I/O and waits for the sandboxed process.
- Reject unsupported learning-mode and fallback policies, and verify deny-path support before launch.
- Route the existing Windows MXC availability metric through the new crate and move its Windows linking dependencies alongside the adapter.

GitOrigin-RevId: e2a220b85718e00b0e710c30bbe66b6c56958176
This commit is contained in:
iceweasel-oai
2026-09-04 19:11:41 +00:00
committed by copyberry
parent 773f0b081d
commit 60888d0868
8 changed files with 136 additions and 16 deletions

15
codex-rs/Cargo.lock generated
View File

@@ -3992,6 +3992,18 @@ dependencies = [
"tracing",
]
[[package]]
name = "codex-mxc-sandbox"
version = "0.0.0"
dependencies = [
"anyhow",
"appcontainer_common",
"codex-protocol",
"learning_mode_windows",
"tracelogging",
"wxc_common",
]
[[package]]
name = "codex-network-proxy"
version = "0.0.0"
@@ -4331,7 +4343,7 @@ name = "codex-sandboxing"
version = "0.0.0"
dependencies = [
"anyhow",
"appcontainer_common",
"codex-mxc-sandbox",
"codex-network-proxy",
"codex-otel",
"codex-protocol",
@@ -4347,7 +4359,6 @@ dependencies = [
"serde_json",
"tempfile",
"tokio",
"tracelogging",
"tracing",
"url",
"which 8.0.0",

View File

@@ -83,6 +83,7 @@ members = [
"memories/read",
"memories/write",
"model-provider-info",
"mxc-sandbox",
"models-manager",
"network-proxy",
"ollama",
@@ -240,6 +241,7 @@ codex-mcp-extension = { path = "ext/mcp" }
codex-mcp-server = { path = "mcp-server" }
codex-model-provider-info = { path = "model-provider-info" }
codex-models-manager = { path = "models-manager" }
codex-mxc-sandbox = { path = "mxc-sandbox" }
codex-network-proxy = { path = "network-proxy" }
codex-ollama = { path = "ollama" }
codex-otel = { path = "otel" }
@@ -302,6 +304,8 @@ core_test_support = { path = "core/tests/common" }
mcp_test_support = { path = "mcp-server/tests/common" }
# External
learning_mode_windows = { git = "https://github.com/microsoft/mxc", rev = "6cd3d58f05d3447e67109cfb75e042803b843ca4" }
wxc_common = { git = "https://github.com/microsoft/mxc", rev = "6cd3d58f05d3447e67109cfb75e042803b843ca4" }
age = "0.11.1"
ansi-to-tui = "8.0.1"
anyhow = "1"

View File

@@ -0,0 +1,6 @@
load("//:defs.bzl", "codex_rust_crate")
codex_rust_crate(
name = "mxc-sandbox",
crate_name = "codex_mxc_sandbox",
)

View File

@@ -0,0 +1,29 @@
[package]
name = "codex-mxc-sandbox"
version.workspace = true
edition.workspace = true
license.workspace = true
[lib]
test = false
doctest = false
[lints]
workspace = true
# This direct dependency pins MXC's transitive ETW dependency for Windows GNU linking.
[package.metadata.cargo-shear]
ignored = ["tracelogging"]
[dependencies]
anyhow = { workspace = true }
codex-protocol = { workspace = true }
wxc_common = { workspace = true }
[target.'cfg(windows)'.dependencies]
appcontainer_common = { workspace = true }
learning_mode_windows = { workspace = true }
# 1.2.4 imports OneCore_apiset, which the GNU Windows toolchain does not ship.
tracelogging = "=1.2.3"
[dev-dependencies]

View File

@@ -0,0 +1,27 @@
//! Native Windows process security environment availability and launch.
#[cfg(windows)]
pub mod native;
use codex_protocol::models::PermissionProfile;
use std::path::PathBuf;
/// Typed inputs for the native policy adapter.
pub struct MxcCommand {
pub permissions: PermissionProfile,
pub sandbox_policy_cwd: PathBuf,
pub command: Vec<String>,
}
/// Whether the executor can create a native MXC process security environment.
/// This deliberately excludes MXC's older AppContainer fallback backends.
pub fn is_available() -> bool {
#[cfg(windows)]
{
appcontainer_common::base_container_runner::BaseContainerRunner::is_process_security_environment_usable()
}
#[cfg(not(windows))]
{
false
}
}

View File

@@ -0,0 +1,55 @@
//! Run an already prepared MXC request with inherited stdio and job ownership.
use anyhow::Context;
use anyhow::Result;
use anyhow::ensure;
use appcontainer_common::base_container_runner::BaseContainerRunner;
use learning_mode_windows::SecurityEnvironmentApi;
use wxc_common::logger::Logger;
use wxc_common::logger::Mode;
use wxc_common::models::ExecutionRequest;
use wxc_common::sandbox_process::SandboxBackend;
use wxc_common::sandbox_process::StdioMode;
// The ETW functions used by tracelogging are documented Advapi32 exports on
// both Windows toolchains; 1.2.3 leaves their import library to the application.
#[link(name = "advapi32")]
unsafe extern "system" {}
/// Launch a native-only request and wait for its exit status.
pub fn launch(request: &ExecutionRequest) -> Result<i32> {
ensure!(
!request
.policy
.capabilities
.iter()
.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() {
ensure!(
SecurityEnvironmentApi::load()?.supports_deny_paths()?,
"this Windows build cannot enforce native MXC deny paths"
);
}
let mut logger = Logger::new(Mode::Buffer);
let mut child = BaseContainerRunner::new()
.spawn(request, &mut logger, StdioMode::Inherit)
.map_err(|error| anyhow::anyhow!("{}", error.error_message))?;
// Do not publish the SDK diagnostic buffer: it may contain command or
// environment data. This wrapper emits only the resulting launch error.
child.wait().context("waiting for the MXC command")
}

View File

@@ -12,10 +12,6 @@ doctest = false
[lints]
workspace = true
# This direct dependency pins MXC's transitive ETW dependency for Windows GNU linking.
[package.metadata.cargo-shear]
ignored = ["tracelogging"]
[dependencies]
anyhow = { workspace = true }
codex-network-proxy = { workspace = true }
@@ -34,11 +30,9 @@ url = { workspace = true }
which = { workspace = true }
[target.'cfg(windows)'.dependencies]
appcontainer_common = { workspace = true }
codex-mxc-sandbox = { workspace = true }
codex-otel = { workspace = true }
codex-utils-home-dir = { workspace = true }
# 1.2.4 imports OneCore_apiset, which the GNU Windows toolchain does not ship.
tracelogging = "=1.2.3"
[dev-dependencies]
anyhow = { workspace = true }

View File

@@ -1,21 +1,15 @@
//! Native Windows MXC capability discovery and availability metrics.
//! Probing is cached upstream; reporting happens at most once per process.
use appcontainer_common::base_container_runner::BaseContainerRunner;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
static AVAILABILITY_RECORDED: AtomicBool = AtomicBool::new(false);
// tracelogging 1.2.3 leaves its ETW import library to the application. These
// APIs are exported by Advapi32 in both the MSVC and GNU Windows toolchains.
#[link(name = "advapi32")]
unsafe extern "system" {}
pub(super) fn record_availability_once() {
// Probe actual PSEC create/close support; symbol presence alone also
// succeeds on transitional Windows builds where MXC is not enabled.
let available = BaseContainerRunner::is_process_security_environment_usable();
let available = codex_mxc_sandbox::is_available();
if let Some(metrics) = codex_otel::global()
&& !AVAILABILITY_RECORDED.swap(true, Ordering::Relaxed)
{