Allow managed requirements to disable the Windows MXC sandbox (#49642)

## Why

`windows.allowed_sandbox_implementations` permits MXC regardless of the listed implementations. Managed requirements need a separate way to block both explicit MXC configuration and automatic selection through `prefer_mxc`.

## What changed

Add the optional managed requirement `windows.allow_mxc`. Setting it to `false` disables automatic MXC selection and rejects explicit `windows.sandbox = "mxc"` configuration with an error identifying the requirement. Leaving it unset or setting it to `true` preserves existing behavior.

## Testing

Add coverage for the requirement alongside allowed sandbox implementations, preference resolution, and cloud-managed opt-out behavior for implicit, elevated, and explicit MXC configurations.

GitOrigin-RevId: a458a7b4c92cf17e687dbbad8208884d1c097da5
This commit is contained in:
iceweasel-oai
2026-09-30 14:37:56 +00:00
committed by copyberry
parent 7219fd735b
commit 67727e7cf1
7 changed files with 135 additions and 25 deletions
@@ -1218,6 +1218,7 @@ client_id = "mcp-client"
codex_config::WindowsSandboxImplementationToml::Elevated,
codex_config::WindowsSandboxImplementationToml::Unelevated,
]),
allow_mxc: None,
}),
..ConfigRequirementsToml::default()
});
+39 -2
View File
@@ -870,6 +870,8 @@ impl fmt::Display for WebSearchModeRequirement {
#[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)]
pub struct WindowsRequirementsToml {
pub allowed_sandbox_implementations: Option<Vec<WindowsSandboxImplementationToml>>,
/// False blocks both explicit MXC configuration and automatic selection.
pub allow_mxc: Option<bool>,
}
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
@@ -881,7 +883,7 @@ pub enum WindowsSandboxImplementationToml {
impl WindowsRequirementsToml {
pub fn is_empty(&self) -> bool {
self.allowed_sandbox_implementations.is_none()
self.allowed_sandbox_implementations.is_none() && self.allow_mxc.is_none()
}
}
@@ -1915,11 +1917,16 @@ impl TryFrom<ConfigRequirementsWithSources> for ConfigRequirements {
/*source*/ None,
),
};
let windows_sandbox_mode = match windows {
let mxc_requirement_source = windows
.as_ref()
.filter(|windows| windows.value.allow_mxc == Some(false))
.map(|windows| windows.source.clone());
let mut windows_sandbox_mode = match windows {
Some(Sourced {
value:
WindowsRequirementsToml {
allowed_sandbox_implementations: Some(implementations),
allow_mxc: _,
},
source: requirement_source,
}) => {
@@ -1965,6 +1972,7 @@ impl TryFrom<ConfigRequirementsWithSources> for ConfigRequirements {
value:
WindowsRequirementsToml {
allowed_sandbox_implementations: None,
allow_mxc: _,
},
..
})
@@ -1973,6 +1981,18 @@ impl TryFrom<ConfigRequirementsWithSources> for ConfigRequirements {
/*source*/ None,
),
};
if let Some(requirement_source) = mxc_requirement_source {
windows_sandbox_mode.add_validator(move |candidate| match candidate {
Some(WindowsSandboxModeToml::Mxc) => Err(ConstraintError::InvalidValue {
field_name: "windows.sandbox",
candidate: format!("{candidate:?}"),
allowed: "windows.allow_mxc = false".to_string(),
requirement_source: requirement_source.clone(),
}),
Some(WindowsSandboxModeToml::Elevated | WindowsSandboxModeToml::Unelevated)
| None => Ok(()),
})?;
}
let exec_policy = match rules {
Some(Sourced { value, source }) => {
let policy = value.to_requirements_policy().map_err(|err| {
@@ -2827,6 +2847,7 @@ mod tests {
};
let windows = WindowsRequirementsToml {
allowed_sandbox_implementations: Some(vec![WindowsSandboxImplementationToml::Elevated]),
allow_mxc: None,
};
let enforce_residency = ResidencyRequirement::Us;
let enforce_source = source.clone();
@@ -3812,6 +3833,22 @@ allowed_approvals_reviewers = ["user"]
);
assert!(requirements.windows_sandbox_mode.can_set(&None).is_err());
for allow_mxc in [true, false] {
let config = from_str(&format!("{toml_str}\nallow_mxc = {allow_mxc}"))?;
let requirements: ConfigRequirements = with_unknown_source(config).try_into()?;
let constraint = &requirements.windows_sandbox_mode;
assert_eq!(
[
Some(WindowsSandboxModeToml::Elevated),
Some(WindowsSandboxModeToml::Unelevated),
Some(WindowsSandboxModeToml::Mxc),
None,
]
.map(|mode| constraint.can_set(&mode).is_ok()),
[true, false, allow_mxc, false]
);
}
Ok(())
}
+30 -20
View File
@@ -11102,22 +11102,41 @@ async fn explicit_sandbox_mode_falls_back_when_disallowed_by_requirements() -> s
#[tokio::test]
async fn local_mxc_preference_preserves_configured_backend() -> anyhow::Result<()> {
use codex_sandboxing::SandboxType::WindowsMxc;
use codex_sandboxing::SandboxType::WindowsRestrictedToken;
use codex_sandboxing::SandboxType::WindowsRestrictedToken as RestrictedToken;
let codex_home = TempDir::new()?;
for (prefer, resolved_preference, binding, mode, expected) in [
(true, true, true, "unelevated", WindowsMxc),
(true, false, true, "unelevated", WindowsRestrictedToken),
(true, false, false, "unelevated", WindowsRestrictedToken),
(false, false, true, "unelevated", WindowsRestrictedToken),
(false, false, false, "mxc", WindowsMxc),
for (prefer, resolved_preference, binding, allow_mxc, mode, expected) in [
(true, true, true, true, "unelevated", WindowsMxc),
(true, false, true, true, "unelevated", RestrictedToken),
(true, false, true, false, "unelevated", RestrictedToken),
(true, false, false, true, "unelevated", RestrictedToken),
(false, false, true, true, "unelevated", RestrictedToken),
(false, false, false, true, "mxc", WindowsMxc),
] {
let cfg: ConfigToml = toml::from_str(&format!(
"[windows]\nsandbox = {mode:?}\n[features]\nprefer_mxc = {prefer}\n\
[features.network_proxy]\nenabled = true\nallow_local_binding = {binding}\n"
))?;
std::fs::write(
codex_home.path().join(CONFIG_TOML_FILE),
toml::to_string(&cfg)?,
)?;
let mut config = ConfigBuilder::without_managed_config_for_tests()
.codex_home(codex_home.path().to_path_buf())
.fallback_cwd(Some(codex_home.path().to_path_buf()))
.cloud_config_bundle(
CloudConfigBundleFixture::loader_with_enterprise_requirement(format!(
"[windows]\nallow_mxc = {allow_mxc}\n"
)),
)
.build()
.await?;
assert_eq!(
network_config_allows_mxc(
config_allows_mxc(
&config
.config_layer_stack
.requirements()
.windows_sandbox_mode,
&EffectivePermissionSelection {
profiles: None,
selected_profile_id: None,
@@ -11130,20 +11149,11 @@ async fn local_mxc_preference_preserves_configured_backend() -> anyhow::Result<(
cfg.features.as_ref(),
/*enable_network_proxy*/ true,
)?,
binding,
binding && allow_mxc,
);
let mut config = Config::load_from_base_config_with_overrides(
cfg,
ConfigOverrides {
cwd: Some(codex_home.path().to_path_buf()),
..Default::default()
},
codex_home.abs(),
)
.await?;
assert_eq!(
config.prefer_mxc,
prefer && binding && codex_sandboxing::windows_mxc_available(),
prefer && binding && allow_mxc && codex_sandboxing::windows_mxc_available(),
);
// Exercise both resolved decisions independently of the host's native support.
config.prefer_mxc = resolved_preference;
@@ -11156,7 +11166,7 @@ async fn local_mxc_preference_preserves_configured_backend() -> anyhow::Result<(
if mode == "mxc" {
WindowsMxc
} else {
WindowsRestrictedToken
RestrictedToken
},
expected
),
+3 -2
View File
@@ -218,7 +218,7 @@ pub use permissions::resolve_permission_profile;
pub(crate) use resolved_permission_profile::PermissionProfileState;
pub use token_budget_startup::TokenBudgetStartupConfig;
pub use windows_sandbox_config::PreparedWindowsSandboxConfig;
use windows_sandbox_config::network_config_allows_mxc;
use windows_sandbox_config::config_allows_mxc;
pub use windows_sandbox_config::prepare_windows_sandbox_config;
use windows_sandbox_config::resolve_windows_sandbox_type;
@@ -3518,7 +3518,8 @@ impl Config {
permission_config_syntax,
);
let prefer_mxc = features.enabled(Feature::PreferMxc)
&& network_config_allows_mxc(
&& config_allows_mxc(
&constrained_windows_sandbox_mode,
&effective_permission_selection,
profiles_are_active,
permission_profile.as_ref(),
@@ -58,6 +58,9 @@ pub fn prepare_windows_sandbox_config(
constraint: &mut ConstrainedWithSource<Option<WindowsSandboxModeToml>>,
warnings: &mut Vec<String>,
) -> std::io::Result<PreparedWindowsSandboxConfig> {
if configured_mode == Some(WindowsSandboxModeToml::Mxc) {
constraint.can_set(&configured_mode)?;
}
let selected_mode = configured_mode.or(match feature_level {
WindowsSandboxLevel::Elevated => Some(WindowsSandboxModeToml::Elevated),
WindowsSandboxLevel::RestrictedToken => Some(WindowsSandboxModeToml::Unelevated),
@@ -93,7 +96,8 @@ pub fn prepare_windows_sandbox_config(
/// Managed requirements take precedence; otherwise preserve explicit
/// feature-level or active-profile binding denials during automatic selection.
pub(super) fn network_config_allows_mxc(
pub(super) fn config_allows_mxc(
windows_sandbox_mode: &ConstrainedWithSource<Option<WindowsSandboxModeToml>>,
permission_selection: &EffectivePermissionSelection<'_>,
profiles_are_active: bool,
permission_profile: Option<&PermissionProfile>,
@@ -101,6 +105,12 @@ pub(super) fn network_config_allows_mxc(
features: Option<&FeaturesToml>,
enable_network_proxy: bool,
) -> std::io::Result<bool> {
if windows_sandbox_mode
.can_set(&Some(WindowsSandboxModeToml::Mxc))
.is_err()
{
return Ok(false);
}
let profile_local_binding = if profiles_are_active
&& permission_profile.is_none_or(profile_allows_configured_network_proxy)
&& let Some(profile) = permission_selection.selected_profile_id
+50
View File
@@ -1,13 +1,17 @@
use anyhow::Context;
use anyhow::Result;
use codex_config::CloudConfigBundleLoader;
use codex_config::LoaderOverrides;
use codex_config::test_support::CloudConfigBundleFixture;
use codex_config::types::WindowsSandboxModeToml;
use codex_core::CodexThreadSettingsOverrides;
use codex_core::config::ConfigBuilder;
use codex_features::Feature;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::ThreadSettingsOverrides;
use codex_protocol::sandbox::SandboxType;
use codex_utils_absolute_path::AbsolutePathBuf;
use core_test_support::responses::start_mock_server;
use core_test_support::skip_if_target_windows;
@@ -18,6 +22,52 @@ use std::sync::Arc;
use std::sync::RwLock;
use tempfile::TempDir;
#[tokio::test]
async fn managed_mxc_opt_out_blocks_preferred_and_explicit_selection() -> Result<()> {
for mode in [None, Some("elevated"), Some("mxc")] {
let home = TempDir::new()?;
let sandbox = mode
.map(|mode| format!("sandbox = {mode:?}\n"))
.unwrap_or_default();
std::fs::write(
home.path().join("config.toml"),
format!("[windows]\n{sandbox}[features]\nprefer_mxc = true\n"),
)?;
let result = ConfigBuilder::default()
.loader_overrides(LoaderOverrides::without_managed_config_for_tests())
.codex_home(home.path().to_path_buf())
.fallback_cwd(Some(home.path().to_path_buf()))
.cloud_config_bundle(
CloudConfigBundleFixture::loader_with_enterprise_requirement(
"[windows]\nallow_mxc = false\n[features]\nelevated_windows_sandbox = true\n",
),
)
.build()
.await;
if mode == Some("mxc") {
let error =
result.expect_err("explicit MXC should be rejected by managed requirements");
assert_eq!(error.kind(), std::io::ErrorKind::InvalidInput);
assert!(error.to_string().contains("windows.allow_mxc = false"));
} else {
let config = result?;
assert_eq!(
(
config.permissions.windows_sandbox_mode,
config.prefer_mxc,
config.effective_local_windows_sandbox_type()
),
(
mode.map(|_| WindowsSandboxModeToml::Elevated),
false,
SandboxType::WindowsRestrictedToken,
),
);
}
}
Ok(())
}
#[tokio::test]
async fn refreshed_cloud_bundle_updates_later_sessions() -> Result<()> {
let server = start_mock_server().await;
+1
View File
@@ -974,6 +974,7 @@ interrupt_message = false
in_app_browser: None,
windows: Some(WindowsRequirementsToml {
allowed_sandbox_implementations: None,
allow_mxc: None,
}),
additional_developer_instructions: None,
guardian_policy_config: Some("Use the managed guardian policy.".to_string()),