Migrate the Windows MXC sandbox to split MXC crates (#52707)

## Why

PSEC API symbols can be present on transitional Windows builds where MXC is not enabled. Availability detection needs to verify that a native process security environment can actually be created.

## What changed

- Replace `mxc-sdk` with pinned `appcontainer_common`, `learning_mode_windows`, and `wxc_common` dependencies, and update Cargo and Bazel dependency metadata.
- Use MXC's cached create/close probe for `is_available()` and query native deny-path support through `SecurityEnvironmentApi` before launching requests with denied paths.
- Adapt request construction and the existing policy test to the vector-based environment field, and update the sandbox documentation.

GitOrigin-RevId: e8cc6a35d46884d0a069b747d33e711512ec1596
This commit is contained in:
iceweasel-oai
2026-10-09 22:11:30 +00:00
committed by copyberry
parent 4bc83aa5f7
commit 4aa94dce27
11 changed files with 154 additions and 97 deletions
+17 -18
View File
@@ -491,24 +491,23 @@ crate.annotation(
workspace_cargo_toml = "rust/runfiles/Cargo.toml",
)
# MXC's resource compiler lookup needs an explicit path for Windows GNU targets.
crate.annotation(
build_script_deps = [
"@crates//:cc-1.2.55",
"@crates//:winresource-0.1.31",
],
build_script_env = {
"RC_PATH": "$(execpath @llvm//tools:llvm-rc)",
},
build_script_tools = [
"@llvm//tools:clang",
"@llvm//tools:llvm-rc",
],
crate = "mxc-sdk",
patch_args = ["-p1"],
patches = ["//patches:mxc-sdk_utf8_resources.patch"],
workspace_cargo_toml = "src/Cargo.toml",
)
# MXC keeps its Rust workspace below the repository root.
[
crate.annotation(
crate = name,
workspace_cargo_toml = "src/Cargo.toml",
)
for name in [
"appcontainer_common",
"learning_mode_core",
"learning_mode_windows",
"mxc_config_contract",
"mxc_telemetry",
"process_security_environment_spec",
"sandbox_spec",
"wxc_common",
]
]
http_archive = use_repo_rule("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+8 -3
View File
File diff suppressed because one or more lines are too long
+102 -44
View File
@@ -303,6 +303,27 @@ dependencies = [
"wiremock",
]
[[package]]
name = "appcontainer_common"
version = "0.8.0"
source = "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4"
dependencies = [
"flatbuffers",
"getrandom 0.2.17",
"learning_mode_core",
"learning_mode_windows",
"process_security_environment_spec",
"sandbox_spec",
"serde",
"serde_json",
"thiserror 2.0.18",
"widestring",
"windows 0.62.2",
"windows-core 0.62.2",
"winreg 0.55.0",
"wxc_common",
]
[[package]]
name = "approx"
version = "0.5.1"
@@ -4108,12 +4129,13 @@ name = "codex-mxc-sandbox"
version = "0.0.0"
dependencies = [
"anyhow",
"appcontainer_common",
"codex-network-proxy",
"codex-protocol",
"codex-utils-absolute-path",
"codex-utils-path-uri",
"codex-windows-sandbox",
"mxc-sdk",
"learning_mode_windows",
"pretty_assertions",
"serde",
"serde_json",
@@ -4121,6 +4143,7 @@ dependencies = [
"thiserror 2.0.18",
"tracelogging",
"windows-sys 0.61.2",
"wxc_common",
]
[[package]]
@@ -5481,12 +5504,6 @@ version = "0.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "const_format"
version = "0.2.35"
@@ -6247,7 +6264,7 @@ version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
dependencies = [
"const-oid 0.9.6",
"const-oid",
"pem-rfc7468",
"zeroize",
]
@@ -6391,7 +6408,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer 0.10.4",
"const-oid 0.9.6",
"const-oid",
"crypto-common 0.1.7",
"subtle",
]
@@ -6403,7 +6420,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.0",
"const-oid 0.10.2",
"crypto-common 0.2.2",
"ctutils",
]
@@ -9881,6 +9897,32 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "learning_mode_core"
version = "0.8.0"
source = "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4"
dependencies = [
"same-file",
"serde",
"serde_json",
"sha2 0.10.9",
"tempfile",
"thiserror 2.0.18",
]
[[package]]
name = "learning_mode_windows"
version = "0.8.0"
source = "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4"
dependencies = [
"learning_mode_core",
"sha2 0.10.9",
"thiserror 2.0.18",
"windows 0.62.2",
"windows-core 0.62.2",
"wxc_common",
]
[[package]]
name = "leb128fmt"
version = "0.1.0"
@@ -10349,37 +10391,22 @@ dependencies = [
]
[[package]]
name = "mxc-sdk"
version = "1.0.0"
source = "git+https://github.com/microsoft/mxc?rev=a846837d827b496fc4a08dfe60554515758c6647#a846837d827b496fc4a08dfe60554515758c6647"
name = "mxc_config_contract"
version = "0.8.0"
source = "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4"
dependencies = [
"anyhow",
"base64 0.22.1",
"cc",
"chrono",
"cidr",
"flatbuffers",
"getrandom 0.2.17",
"libc",
"nix 0.29.0",
"same-file",
"semver",
"serde",
"serde_json",
"serde_path_to_error",
"sha2 0.11.0",
"tempfile",
"thiserror 2.0.18",
"tokio",
]
[[package]]
name = "mxc_telemetry"
version = "0.8.0"
source = "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4"
dependencies = [
"tracelogging",
"unicode-general-category",
"url",
"uuid",
"widestring",
"windows 0.62.2",
"windows-core 0.62.2",
"winreg 0.55.0",
"winresource",
]
[[package]]
@@ -11880,6 +11907,14 @@ dependencies = [
"windows 0.62.2",
]
[[package]]
name = "process_security_environment_spec"
version = "0.8.0"
source = "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4"
dependencies = [
"flatbuffers",
]
[[package]]
name = "prodash"
version = "31.0.0"
@@ -13628,6 +13663,14 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "sandbox_spec"
version = "0.8.0"
source = "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4"
dependencies = [
"flatbuffers",
]
[[package]]
name = "sansio"
version = "1.0.1"
@@ -17486,15 +17529,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "winresource"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0986a8b1d586b7d3e4fe3d9ea39fb451ae22869dcea4aa109d287a374d866087"
dependencies = [
"version_check",
]
[[package]]
name = "winsafe"
version = "0.0.19"
@@ -17642,6 +17676,30 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
[[package]]
name = "wxc_common"
version = "0.8.0"
source = "git+https://github.com/microsoft/mxc?rev=6cd3d58f05d3447e67109cfb75e042803b843ca4#6cd3d58f05d3447e67109cfb75e042803b843ca4"
dependencies = [
"base64 0.22.1",
"cidr",
"getrandom 0.2.17",
"libc",
"mxc_config_contract",
"mxc_telemetry",
"semver",
"serde",
"serde_json",
"serde_path_to_error",
"thiserror 2.0.18",
"unicode-general-category",
"url",
"widestring",
"windows 0.62.2",
"windows-core 0.62.2",
"winreg 0.55.0",
]
[[package]]
name = "x11rb"
version = "0.13.2"
+3 -1
View File
@@ -320,9 +320,12 @@ codex-windows-sandbox-test-support = { path = "windows-sandbox-rs/tests/support"
core_test_support = { path = "core/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.12.1"
ansi-to-tui = "8.0.1"
anyhow = "1"
appcontainer_common = { git = "https://github.com/microsoft/mxc", rev = "6cd3d58f05d3447e67109cfb75e042803b843ca4" }
arboard = { version = "3", features = ["wayland-data-control"] }
arc-swap = "1.9.0"
assert_cmd = "2"
@@ -409,7 +412,6 @@ maplit = "1.0.2"
memchr = "2.7.6"
mime_guess = "2.0.5"
multimap = "0.10.0"
mxc-sdk = { git = "https://github.com/microsoft/mxc", rev = "a846837d827b496fc4a08dfe60554515758c6647" }
notify = "8.2.0"
nucleo = { git = "https://github.com/helix-editor/nucleo.git", rev = "4253de9faabb4e5c6d81d946a5e35a90f87347ee" }
once_cell = "1.20.2"
+3 -1
View File
@@ -21,12 +21,14 @@ codex-protocol = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-path-uri = { workspace = true }
codex-windows-sandbox = { workspace = true }
mxc-sdk = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
thiserror = { 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"
windows-sys = { workspace = true, features = [
+10 -15
View File
@@ -10,11 +10,11 @@ Windows executors record `codex.windows_mxc.available` once per process with an
`available=true|false` tag. This measures runtime availability independently of
selection. Unsupported Windows executors reject MXC requests before execution.
`is_available()` uses MXC's cached PSEC API-set check. The SDK validates native
creation and capabilities for each request at launch. Its broad
`platform_support()` result also includes older AppContainer backends and is not
used here. A requested deny path requires native `PSE_SUPPORT_FS_DENY` support;
host-loopback ingress requires PSEC 1.1 and native ingress support.
`is_available()` uses MXC's cached create/close probe, rather than an OS build
number or the SDK's broad `platform_support()` result. The latter also reports
older AppContainer backends as supported. A requested deny path additionally
requires the native `PSE_SUPPORT_FS_DENY` capability; otherwise the command
fails before launch.
The wrapper inherits the command's pipes or ConPTY console. MXC creates its
child suspended, assigns a kill-on-close job before resuming it, and retains
@@ -70,9 +70,8 @@ to 4096 bytes; the helper removes them before native process creation. Use
command are not implicitly granted.
- The native API represents paths and environment values as Unicode strings.
Non-Unicode values fail instead of undergoing lossy conversion.
- An empty child environment uses the Windows user-profile defaults, preserving
the previous SDK behavior. Nonempty environments remain explicit; the SDK now
requires `SystemRoot` and `LOCALAPPDATA` before launch.
- An empty child environment uses the Windows user-profile defaults. Nonempty
environments remain explicit.
- The upstream runner terminates remaining descendants when the foreground
process exits, as well as on cancellation. Both existing Windows backends
preserve descendants after normal exit, so detached servers currently lose
@@ -87,10 +86,6 @@ to 4096 bytes; the helper removes them before native process creation. Use
suite on supported Windows and the corresponding platform hosts.
Test the normal `powershell.exe` and `pwsh.exe` command paths, not only `cmd.exe`.
The MXC 1.0.0 Git revision is pinned with the workspace dependencies.
Native launch errors are returned without dumping the SDK's diagnostic buffer,
which may contain command or environment data.
Cargo builds on Windows GNU targets require `RC_PATH` to point to `rc.exe` or
`llvm-rc.exe`; the SDK's automatic resource compiler lookup supports MSVC only.
Bazel supplies its hermetic LLVM resource compiler.
The MXC git revision is pinned with the workspace dependencies. Native launch
errors are returned without dumping the SDK's diagnostic buffer, which may
contain command or environment data.
+2 -2
View File
@@ -86,12 +86,12 @@ pub fn create_command_args(args: CreateMxcCommandArgsParams<'_>) -> Result<Vec<S
Ok(vec![CODEX_WINDOWS_MXC_ARG1.to_owned()])
}
/// Whether the executor exposes MXC's process security-environment API set.
/// 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)]
{
mxc_sdk::process_container_common::base_container_runner::BaseContainerRunner::is_base_container_api_present()
appcontainer_common::base_container_runner::BaseContainerRunner::is_process_security_environment_usable()
}
#[cfg(not(windows))]
{
+5 -3
View File
@@ -3,8 +3,8 @@
use anyhow::Context;
use anyhow::Result;
use anyhow::ensure;
use mxc_sdk::mxc_common as wxc_common;
use mxc_sdk::process_container_common::base_container_runner::BaseContainerRunner;
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;
@@ -25,9 +25,11 @@ pub(super) fn launch(request: &ExecutionRequest) -> Result<i32> {
.any(|capability| capability.eq_ignore_ascii_case("permissiveLearningMode")),
"MXC native launch does not support permissiveLearningMode"
);
// 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!(
BaseContainerRunner::supports_native_denied_paths(),
SecurityEnvironmentApi::load()?.supports_deny_paths()?,
"this Windows build cannot enforce native MXC deny paths"
);
}
+1 -2
View File
@@ -12,7 +12,6 @@ use codex_protocol::protocol::FileSystemSpecialPath;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use codex_windows_sandbox::resolve_windows_deny_read_paths;
use mxc_sdk::mxc_common as wxc_common;
use thiserror::Error;
use wxc_common::cmdline::CommandLineContext;
use wxc_common::cmdline::CommandLineError;
@@ -275,7 +274,7 @@ pub(super) fn build_request(
.to_str()
.ok_or(PolicyError::NonUnicodeCommandCwd)?
.to_owned(),
env: (!env.is_empty()).then_some(env),
env,
policy: ContainerPolicy {
capabilities: vec!["registryRead".to_owned()],
readwrite_paths: unicode_paths(write)?,
+1 -6
View File
@@ -16,7 +16,6 @@ use codex_protocol::protocol::FileSystemSpecialPath;
use codex_protocol::protocol::NetworkSandboxPolicy;
use codex_utils_absolute_path::AbsolutePathBuf;
use codex_utils_path_uri::PathUri;
use mxc_sdk::mxc_common as wxc_common;
use pretty_assertions::assert_eq;
use wxc_common::models::NetworkAction;
use wxc_common::models::NetworkCidr;
@@ -197,13 +196,9 @@ fn wrapper_preserves_exact_argv_and_separate_command_cwd() -> Result<()> {
(
r#""C:\Program Files\tool.exe" --permissions "" "quotes\" and slash\\""#.to_owned(),
cwd.to_str().unwrap().to_owned(),
Some(vec!["CUSTOM=value".to_owned()]),
vec!["CUSTOM=value".to_owned()],
)
);
assert_eq!(
build_request(&parsed, &cwd, Vec::new(), &[], &[])?.env,
None,
);
Ok(())
}
+2 -2
View File
@@ -7,8 +7,8 @@ use std::sync::atomic::Ordering;
static AVAILABILITY_RECORDED: AtomicBool = AtomicBool::new(false);
pub(super) fn record_availability_once() {
// Record the SDK's cached PSEC API-set check. Creation and request-specific
// capability checks happen when launching the MXC command.
// Probe actual PSEC create/close support; symbol presence alone also
// succeeds on transitional Windows builds where MXC is not enabled.
let available = codex_mxc_sandbox::is_available();
if let Some(metrics) = codex_otel::global()
&& !AVAILABILITY_RECORDED.swap(true, Ordering::Relaxed)