mirror of
https://github.com/openai/codex.git
synced 2026-10-19 17:28:46 +00:00
Enable MXC selection through Windows sandbox configuration (#46271)
## What changed - Accept `windows.sandbox = "mxc"` and preserve the selected backend through environment configuration, command execution, patch writes, and sandbox metadata. - Treat MXC as enabled in the TUI and report Windows sandbox readiness as `ready`, avoiding legacy setup prompts. - Keep `allowed_sandbox_implementations` scoped to the legacy elevated and unelevated backends without restricting MXC. - Default `windows.sandbox_private_desktop` to `false` for MXC while retaining `true` for legacy sandboxes. ## Testing Add coverage for MXC configuration precedence, legacy requirement handling, sandbox selection, and TUI state. Add a Wine integration test that verifies command and patch routing fails when native MXC is unavailable and reports `windows_mxc` in turn metadata. GitOrigin-RevId: e2162447d0750f60753864c92a20e02a7f297bca
This commit is contained in:
@@ -287,12 +287,12 @@ The origin of a required `chatgpt_base_url` must match the discovered origin by
|
||||
|
||||
## Windows sandbox implementation selection
|
||||
|
||||
`windowsSandbox/setupStart` and `windowsSandbox/readiness` apply only to the
|
||||
legacy `elevated` and `unelevated` backends. Clients resolve the desired sandbox
|
||||
implementation from configuration. When it is `mxc`, they skip both methods;
|
||||
`allowedWindowsSandboxImplementations` can allow `mxc` independently of the
|
||||
legacy setup modes. Non-Windows hosts report `notConfigured` for the legacy
|
||||
readiness API.
|
||||
`windowsSandbox/setupStart` applies only to the legacy `elevated` and
|
||||
`unelevated` backends. `windowsSandbox/readiness` reports `ready` when MXC is
|
||||
selected so clients do not offer legacy setup. The
|
||||
`allowedWindowsSandboxImplementations` requirement governs only the legacy
|
||||
backends and does not restrict MXC. Its `mxc` enum member is retained for wire
|
||||
compatibility but is not emitted. Non-Windows hosts report `notConfigured`.
|
||||
|
||||
MXC uses the standard `command/exec` streaming and process-control path, including
|
||||
ConPTY when `tty` is enabled. The buffered legacy Windows sandbox restrictions on
|
||||
|
||||
@@ -320,6 +320,7 @@ impl CommandExecRequestProcessor {
|
||||
windows_sandbox_workspace_roots.as_slice(),
|
||||
&codex_linux_sandbox_exe,
|
||||
&self.arg0_paths.codex_self_exe,
|
||||
self.config.permissions.windows_sandbox_type,
|
||||
use_legacy_landlock,
|
||||
)
|
||||
.map_err(|err| internal_error(format!("exec failed: {err}")))?;
|
||||
|
||||
@@ -472,10 +472,10 @@ fn map_requirements_to_api(
|
||||
implementations
|
||||
.into_iter()
|
||||
.map(|implementation| match implementation {
|
||||
codex_config::types::WindowsSandboxModeToml::Elevated => {
|
||||
codex_config::WindowsSandboxImplementationToml::Elevated => {
|
||||
WindowsSandboxImplementation::Elevated
|
||||
}
|
||||
codex_config::types::WindowsSandboxModeToml::Unelevated => {
|
||||
codex_config::WindowsSandboxImplementationToml::Unelevated => {
|
||||
WindowsSandboxImplementation::Unelevated
|
||||
}
|
||||
})
|
||||
@@ -1102,8 +1102,8 @@ mod tests {
|
||||
let mapped = map_test_requirements(ConfigRequirementsToml {
|
||||
windows: Some(WindowsRequirementsToml {
|
||||
allowed_sandbox_implementations: Some(vec![
|
||||
codex_config::types::WindowsSandboxModeToml::Elevated,
|
||||
codex_config::types::WindowsSandboxModeToml::Unelevated,
|
||||
codex_config::WindowsSandboxImplementationToml::Elevated,
|
||||
codex_config::WindowsSandboxImplementationToml::Unelevated,
|
||||
]),
|
||||
sandbox_private_desktop: Some(false),
|
||||
}),
|
||||
|
||||
@@ -21,6 +21,7 @@ use codex_app_server_protocol::ThreadSection;
|
||||
use codex_app_server_protocol::ThreadSectionAppearance;
|
||||
use codex_app_server_protocol::ThreadSectionMoveParams;
|
||||
use codex_app_server_protocol::ThreadSectionMoveResponse;
|
||||
use codex_config::types::WindowsSandboxModeToml;
|
||||
use codex_extension_api::ExtensionDataInit;
|
||||
use codex_extension_api::ThreadIdleCause;
|
||||
use codex_protocol::SanitizedGitUrl;
|
||||
@@ -4931,18 +4932,17 @@ impl ThreadRequestProcessor {
|
||||
// Persist Windows sandbox mode.
|
||||
let mut cli_overrides = cli_overrides.unwrap_or_default();
|
||||
if cfg!(windows) {
|
||||
match WindowsSandboxLevel::from_config(&self.config) {
|
||||
WindowsSandboxLevel::Elevated => {
|
||||
cli_overrides
|
||||
.insert("windows.sandbox".to_string(), serde_json::json!("elevated"));
|
||||
let mode = self.config.permissions.windows_sandbox_mode.or_else(|| {
|
||||
match WindowsSandboxLevel::from_config(&self.config) {
|
||||
WindowsSandboxLevel::Elevated => Some(WindowsSandboxModeToml::Elevated),
|
||||
WindowsSandboxLevel::RestrictedToken => {
|
||||
Some(WindowsSandboxModeToml::Unelevated)
|
||||
}
|
||||
WindowsSandboxLevel::Disabled => None,
|
||||
}
|
||||
WindowsSandboxLevel::RestrictedToken => {
|
||||
cli_overrides.insert(
|
||||
"windows.sandbox".to_string(),
|
||||
serde_json::json!("unelevated"),
|
||||
);
|
||||
}
|
||||
WindowsSandboxLevel::Disabled => {}
|
||||
});
|
||||
if let Some(mode) = mode {
|
||||
cli_overrides.insert("windows.sandbox".to_string(), serde_json::json!(mode));
|
||||
}
|
||||
}
|
||||
let request_overrides = if cli_overrides.is_empty() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use super::*;
|
||||
#[cfg(target_os = "windows")]
|
||||
use anyhow::Context as _;
|
||||
use codex_protocol::sandbox::SandboxType;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -305,6 +306,12 @@ fn determine_windows_sandbox_readiness(config: &Config) -> WindowsSandboxReadine
|
||||
};
|
||||
}
|
||||
|
||||
if config.permissions.windows_sandbox_type == SandboxType::WindowsMxc {
|
||||
return WindowsSandboxReadinessResponse {
|
||||
status: WindowsSandboxReadiness::Ready,
|
||||
};
|
||||
}
|
||||
|
||||
determine_windows_sandbox_readiness_from_state(
|
||||
WindowsSandboxLevel::from_config(config),
|
||||
sandbox_setup_is_complete(config.codex_home.as_path()),
|
||||
|
||||
@@ -122,13 +122,18 @@ pub(super) fn sandbox_check(config: &Config, arg0_paths: &Arg0DispatchPaths) ->
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let configured_backend = WindowsSandboxLevel::from_config(config);
|
||||
let elevated = configured_backend != WindowsSandboxLevel::Disabled
|
||||
&& windows_sandbox_uses_elevated_backend(configured_backend);
|
||||
let backend = if elevated {
|
||||
WindowsSandboxLevel::Elevated
|
||||
let configured_level = WindowsSandboxLevel::from_config(config);
|
||||
let mxc = config.permissions.windows_sandbox_type
|
||||
== codex_protocol::sandbox::SandboxType::WindowsMxc;
|
||||
let elevated = !mxc
|
||||
&& configured_level != WindowsSandboxLevel::Disabled
|
||||
&& windows_sandbox_uses_elevated_backend(configured_level);
|
||||
let backend = if mxc {
|
||||
"mxc".to_string()
|
||||
} else if elevated {
|
||||
WindowsSandboxLevel::Elevated.to_string()
|
||||
} else {
|
||||
configured_backend
|
||||
configured_level.to_string()
|
||||
};
|
||||
let denied = !file_system_sandbox.has_full_disk_read_access();
|
||||
|
||||
@@ -137,7 +142,7 @@ pub(super) fn sandbox_check(config: &Config, arg0_paths: &Arg0DispatchPaths) ->
|
||||
.details
|
||||
.push(format!("denied-read restrictions: {denied}"));
|
||||
|
||||
if denied && !elevated {
|
||||
if denied && !elevated && !mxc {
|
||||
check.issues.push(
|
||||
DoctorIssue::new(
|
||||
CheckStatus::Fail,
|
||||
|
||||
@@ -867,10 +867,17 @@ impl fmt::Display for WebSearchModeRequirement {
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Default, PartialEq, Eq)]
|
||||
pub struct WindowsRequirementsToml {
|
||||
pub allowed_sandbox_implementations: Option<Vec<WindowsSandboxModeToml>>,
|
||||
pub allowed_sandbox_implementations: Option<Vec<WindowsSandboxImplementationToml>>,
|
||||
pub sandbox_private_desktop: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum WindowsSandboxImplementationToml {
|
||||
Elevated,
|
||||
Unelevated,
|
||||
}
|
||||
|
||||
impl WindowsRequirementsToml {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.allowed_sandbox_implementations.is_none() && self.sandbox_private_desktop.is_none()
|
||||
@@ -1926,18 +1933,32 @@ impl TryFrom<ConfigRequirementsWithSources> for ConfigRequirements {
|
||||
));
|
||||
}
|
||||
// Prefer elevated when both Windows sandbox implementations are allowed.
|
||||
let initial_value =
|
||||
if implementations.contains(&WindowsSandboxModeToml::Elevated) {
|
||||
WindowsSandboxModeToml::Elevated
|
||||
} else {
|
||||
WindowsSandboxModeToml::Unelevated
|
||||
};
|
||||
let initial_value = if implementations
|
||||
.contains(&WindowsSandboxImplementationToml::Elevated)
|
||||
{
|
||||
WindowsSandboxModeToml::Elevated
|
||||
} else {
|
||||
WindowsSandboxModeToml::Unelevated
|
||||
};
|
||||
|
||||
let requirement_source_for_error = requirement_source.clone();
|
||||
let constrained = Constrained::new(
|
||||
Some(initial_value),
|
||||
move |candidate| match candidate {
|
||||
Some(candidate) if implementations.contains(candidate) => Ok(()),
|
||||
Some(WindowsSandboxModeToml::Mxc) => Ok(()),
|
||||
Some(WindowsSandboxModeToml::Elevated)
|
||||
if implementations
|
||||
.contains(&WindowsSandboxImplementationToml::Elevated) =>
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
Some(WindowsSandboxModeToml::Unelevated)
|
||||
if implementations.contains(
|
||||
&WindowsSandboxImplementationToml::Unelevated,
|
||||
) =>
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(ConstraintError::InvalidValue {
|
||||
field_name: "windows.sandbox",
|
||||
candidate: format!("{candidate:?}"),
|
||||
@@ -3779,6 +3800,12 @@ allowed_approvals_reviewers = ["user"]
|
||||
.can_set(&Some(WindowsSandboxModeToml::Unelevated))
|
||||
.is_err()
|
||||
);
|
||||
assert!(
|
||||
requirements
|
||||
.windows_sandbox_mode
|
||||
.can_set(&Some(WindowsSandboxModeToml::Mxc))
|
||||
.is_ok()
|
||||
);
|
||||
assert!(requirements.windows_sandbox_mode.can_set(&None).is_err());
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -126,6 +126,7 @@ pub use config_requirements::SandboxModeRequirement;
|
||||
pub use config_requirements::Sourced;
|
||||
pub use config_requirements::WebSearchModeRequirement;
|
||||
pub use config_requirements::WindowsRequirementsToml;
|
||||
pub use config_requirements::WindowsSandboxImplementationToml;
|
||||
pub use config_requirements::sandbox_mode_requirement_for_permission_profile;
|
||||
pub use constraint::Constrained;
|
||||
pub use constraint::ConstraintError;
|
||||
|
||||
@@ -163,14 +163,15 @@ impl Default for AuthKeyringBackendKind {
|
||||
pub enum WindowsSandboxModeToml {
|
||||
Elevated,
|
||||
Unelevated,
|
||||
Mxc,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Default, PartialEq, Eq, JsonSchema)]
|
||||
#[schemars(deny_unknown_fields)]
|
||||
pub struct WindowsToml {
|
||||
pub sandbox: Option<WindowsSandboxModeToml>,
|
||||
/// Defaults to `true`. Set to `false` to launch the final sandboxed child
|
||||
/// process on `Winsta0\\Default` instead of a private desktop.
|
||||
/// Defaults to `true` for legacy sandboxes and `false` for MXC. Set to `false`
|
||||
/// to launch the final sandboxed child process on `Winsta0\\Default`.
|
||||
pub sandbox_private_desktop: Option<bool>,
|
||||
}
|
||||
|
||||
|
||||
@@ -6098,7 +6098,8 @@
|
||||
"WindowsSandboxModeToml": {
|
||||
"enum": [
|
||||
"elevated",
|
||||
"unelevated"
|
||||
"unelevated",
|
||||
"mxc"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
@@ -6109,7 +6110,7 @@
|
||||
"$ref": "#/definitions/WindowsSandboxModeToml"
|
||||
},
|
||||
"sandbox_private_desktop": {
|
||||
"description": "Defaults to `true`. Set to `false` to launch the final sandboxed child process on `Winsta0\\\\Default` instead of a private desktop.",
|
||||
"description": "Defaults to `true` for legacy sandboxes and `false` for MXC. Set to `false` to launch the final sandboxed child process on `Winsta0\\\\Default`.",
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -375,6 +375,7 @@ fn resolved_local_environments<const N: usize>(
|
||||
allow_login_shell: true,
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_type: codex_protocol::sandbox::SandboxType::None,
|
||||
windows_sandbox_private_desktop: true,
|
||||
use_legacy_landlock: false,
|
||||
permission_profile: PermissionProfileSnapshot::legacy(
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::unified_exec::MIN_EMPTY_YIELD_TIME_MS;
|
||||
use crate::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use crate::windows_sandbox::resolve_windows_sandbox_mode;
|
||||
use crate::windows_sandbox::resolve_windows_sandbox_private_desktop;
|
||||
use crate::windows_sandbox::windows_sandbox_level_for_legacy_checks;
|
||||
use codex_agent_roles::load_agent_roles;
|
||||
use codex_config::CloudConfigBundleLoader;
|
||||
use codex_config::ConfigLayerSource;
|
||||
@@ -3352,6 +3353,10 @@ impl Config {
|
||||
&mut constrained_windows_sandbox_mode,
|
||||
&mut startup_warnings,
|
||||
)?;
|
||||
let legacy_windows_sandbox_level = windows_sandbox_level_for_legacy_checks(
|
||||
windows_sandbox_type,
|
||||
windows_sandbox_level,
|
||||
);
|
||||
let windows_sandbox_private_desktop = resolve_windows_sandbox_private_desktop(&cfg);
|
||||
let resolved_cwd = AbsolutePathBuf::try_from(normalize_for_native_workdir({
|
||||
use std::env;
|
||||
@@ -3502,7 +3507,7 @@ impl Config {
|
||||
.unwrap_or_else(|| {
|
||||
default_builtin_permission_profile_name(
|
||||
&active_project,
|
||||
windows_sandbox_level,
|
||||
legacy_windows_sandbox_level,
|
||||
)
|
||||
});
|
||||
network_proxy_config_for_profile_selection(
|
||||
@@ -3523,7 +3528,10 @@ impl Config {
|
||||
let default_permissions = effective_permission_selection
|
||||
.selected_profile_id
|
||||
.unwrap_or_else(|| {
|
||||
default_builtin_permission_profile_name(&active_project, windows_sandbox_level)
|
||||
default_builtin_permission_profile_name(
|
||||
&active_project,
|
||||
legacy_windows_sandbox_level,
|
||||
)
|
||||
});
|
||||
let builtin_workspace_write_settings = if using_implicit_builtin_profile {
|
||||
cfg.sandbox_workspace_write.as_ref().map(|settings| WorkspaceWriteSettings {
|
||||
@@ -3588,7 +3596,7 @@ impl Config {
|
||||
let mut permission_profile = cfg
|
||||
.derive_permission_profile(
|
||||
sandbox_mode,
|
||||
windows_sandbox_level,
|
||||
legacy_windows_sandbox_level,
|
||||
Some(&active_project),
|
||||
Some(&constrained_permission_profile),
|
||||
)
|
||||
|
||||
@@ -37,15 +37,19 @@ pub fn prepare_windows_sandbox_config(
|
||||
} else {
|
||||
configured_mode
|
||||
};
|
||||
let level = match effective_mode {
|
||||
Some(WindowsSandboxModeToml::Elevated) => WindowsSandboxLevel::Elevated,
|
||||
Some(WindowsSandboxModeToml::Unelevated) => WindowsSandboxLevel::RestrictedToken,
|
||||
None => WindowsSandboxLevel::Disabled,
|
||||
};
|
||||
let sandbox_type = if level == WindowsSandboxLevel::Disabled {
|
||||
SandboxType::None
|
||||
} else {
|
||||
SandboxType::WindowsRestrictedToken
|
||||
let (sandbox_type, level) = match effective_mode {
|
||||
Some(WindowsSandboxModeToml::Elevated) => (
|
||||
SandboxType::WindowsRestrictedToken,
|
||||
WindowsSandboxLevel::Elevated,
|
||||
),
|
||||
Some(WindowsSandboxModeToml::Unelevated) => (
|
||||
SandboxType::WindowsRestrictedToken,
|
||||
WindowsSandboxLevel::RestrictedToken,
|
||||
),
|
||||
Some(WindowsSandboxModeToml::Mxc) => {
|
||||
(SandboxType::WindowsMxc, WindowsSandboxLevel::Disabled)
|
||||
}
|
||||
None => (SandboxType::None, WindowsSandboxLevel::Disabled),
|
||||
};
|
||||
Ok(PreparedWindowsSandboxConfig {
|
||||
mode,
|
||||
|
||||
@@ -23,6 +23,14 @@ fn configured_mode_takes_priority_without_persisting_feature_fallback() -> std::
|
||||
level: WindowsSandboxLevel::RestrictedToken,
|
||||
},
|
||||
),
|
||||
(
|
||||
Some(WindowsSandboxModeToml::Mxc),
|
||||
PreparedWindowsSandboxConfig {
|
||||
mode: Some(WindowsSandboxModeToml::Mxc),
|
||||
sandbox_type: SandboxType::WindowsMxc,
|
||||
level: WindowsSandboxLevel::Disabled,
|
||||
},
|
||||
),
|
||||
] {
|
||||
let actual = prepare_windows_sandbox_config(
|
||||
configured_mode,
|
||||
|
||||
@@ -1112,6 +1112,7 @@ mod tests {
|
||||
allow_login_shell: true,
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_type: codex_protocol::sandbox::SandboxType::None,
|
||||
windows_sandbox_private_desktop: true,
|
||||
use_legacy_landlock: false,
|
||||
permission_profile: PermissionProfileSnapshot::legacy(PermissionProfile::read_only()),
|
||||
@@ -1292,6 +1293,7 @@ url = "ws://127.0.0.1:8765"
|
||||
allow_login_shell: false,
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_type: codex_protocol::sandbox::SandboxType::None,
|
||||
windows_sandbox_private_desktop: true,
|
||||
use_legacy_landlock: false,
|
||||
permission_profile: PermissionProfileSnapshot::active_with_profile_workspace_roots(
|
||||
@@ -1556,6 +1558,7 @@ url = "ws://127.0.0.1:8765"
|
||||
allow_login_shell: false,
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_type: codex_protocol::sandbox::SandboxType::None,
|
||||
windows_sandbox_private_desktop: true,
|
||||
use_legacy_landlock: false,
|
||||
permission_profile: PermissionProfileSnapshot::active_with_profile_workspace_roots(
|
||||
@@ -1937,6 +1940,7 @@ url = "ws://127.0.0.1:8765"
|
||||
allow_login_shell: false,
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_type: codex_protocol::sandbox::SandboxType::None,
|
||||
windows_sandbox_private_desktop: true,
|
||||
use_legacy_landlock: false,
|
||||
permission_profile: PermissionProfileSnapshot::active_with_profile_workspace_roots(
|
||||
@@ -2007,6 +2011,7 @@ url = "ws://127.0.0.1:8765"
|
||||
allow_login_shell: false,
|
||||
workspace_roots: selection.workspace_roots.clone(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_type: codex_protocol::sandbox::SandboxType::None,
|
||||
windows_sandbox_private_desktop: true,
|
||||
use_legacy_landlock: false,
|
||||
permission_profile: PermissionProfileSnapshot::legacy(PermissionProfile::read_only()),
|
||||
|
||||
@@ -125,13 +125,13 @@ pub enum ExecCapturePolicy {
|
||||
|
||||
fn select_process_exec_tool_sandbox_type(
|
||||
permission_profile: &PermissionProfile,
|
||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel,
|
||||
windows_sandbox_type: SandboxType,
|
||||
enforce_managed_network: bool,
|
||||
) -> SandboxType {
|
||||
SandboxManager::new().select_initial(
|
||||
permission_profile,
|
||||
SandboxablePreference::Auto,
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_type,
|
||||
enforce_managed_network,
|
||||
)
|
||||
}
|
||||
@@ -308,6 +308,13 @@ pub async fn process_exec_tool_call(
|
||||
use_legacy_landlock: bool,
|
||||
stdout_stream: Option<StdoutStream>,
|
||||
) -> Result<ExecToolCallOutput> {
|
||||
let windows_sandbox_type = if params.windows_sandbox_level
|
||||
== codex_protocol::config_types::WindowsSandboxLevel::Disabled
|
||||
{
|
||||
SandboxType::None
|
||||
} else {
|
||||
SandboxType::WindowsRestrictedToken
|
||||
};
|
||||
let windows_sandbox_workspace_roots = windows_sandbox_workspace_roots
|
||||
.iter()
|
||||
.map(PathUri::from_abs_path)
|
||||
@@ -319,6 +326,7 @@ pub async fn process_exec_tool_call(
|
||||
&windows_sandbox_workspace_roots,
|
||||
codex_linux_sandbox_exe,
|
||||
codex_self_exe,
|
||||
windows_sandbox_type,
|
||||
use_legacy_landlock,
|
||||
)?;
|
||||
|
||||
@@ -328,6 +336,7 @@ pub async fn process_exec_tool_call(
|
||||
|
||||
/// Transform a portable exec request into the concrete argv/env that should be
|
||||
/// spawned under the requested sandbox policy.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn build_exec_request(
|
||||
params: ExecParams,
|
||||
permission_profile: &PermissionProfile,
|
||||
@@ -335,6 +344,7 @@ pub fn build_exec_request(
|
||||
windows_sandbox_workspace_roots: &[PathUri],
|
||||
codex_linux_sandbox_exe: &Option<PathBuf>,
|
||||
codex_self_exe: &Option<PathBuf>,
|
||||
windows_sandbox_type: SandboxType,
|
||||
use_legacy_landlock: bool,
|
||||
) -> Result<ExecRequest> {
|
||||
let ExecParams {
|
||||
@@ -358,7 +368,7 @@ pub fn build_exec_request(
|
||||
let enforce_managed_network = network.is_some();
|
||||
let sandbox_type = select_process_exec_tool_sandbox_type(
|
||||
permission_profile,
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_type,
|
||||
enforce_managed_network,
|
||||
);
|
||||
tracing::debug!("Sandbox type: {sandbox_type:?}");
|
||||
|
||||
@@ -1187,7 +1187,7 @@ fn process_exec_tool_call_uses_platform_sandbox_for_network_only_restrictions()
|
||||
&FileSystemSandboxPolicy::unrestricted(),
|
||||
NetworkSandboxPolicy::Restricted,
|
||||
),
|
||||
codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
SandboxType::None,
|
||||
/*enforce_managed_network*/ false,
|
||||
),
|
||||
expected
|
||||
@@ -1219,6 +1219,7 @@ fn build_exec_request_projects_workspace_roots_only_for_windows_sandbox() -> Res
|
||||
roots,
|
||||
&Some(temp_dir.path().join("codex-linux-sandbox")),
|
||||
/*codex_self_exe*/ &None,
|
||||
SandboxType::WindowsRestrictedToken,
|
||||
/*use_legacy_landlock*/ false,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -1348,6 +1348,7 @@ async fn mcp_sandbox_cwd_uses_matching_server_environment_uri() -> anyhow::Resul
|
||||
allow_login_shell: true,
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: turn_context.windows_sandbox_level,
|
||||
windows_sandbox_type: turn_context.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: turn_context
|
||||
.config
|
||||
.permissions
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! Labels never inspect the filesystem and must not be used for authorization.
|
||||
|
||||
use crate::responses_metadata::CodexResponsesMetadata;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_file_system::WindowsSandboxSelection;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_sandboxing::get_platform_sandbox;
|
||||
@@ -21,13 +21,13 @@ impl SandboxTags {
|
||||
pub(crate) fn new(
|
||||
profile: &PermissionProfile,
|
||||
cwd: &Path,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
windows_sandbox_selection: WindowsSandboxSelection,
|
||||
enforce_managed_network: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
sandbox: permission_profile_sandbox_tag(
|
||||
profile,
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_selection,
|
||||
enforce_managed_network,
|
||||
),
|
||||
policy: permission_profile_policy_tag(profile, cwd),
|
||||
@@ -57,9 +57,10 @@ pub(crate) fn record_policy_metadata(
|
||||
|
||||
fn permission_profile_sandbox_tag(
|
||||
profile: &PermissionProfile,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
windows_sandbox_selection: impl Into<WindowsSandboxSelection>,
|
||||
enforce_managed_network: bool,
|
||||
) -> &'static str {
|
||||
let windows_sandbox_selection = windows_sandbox_selection.into();
|
||||
match profile {
|
||||
PermissionProfile::Disabled => return "none",
|
||||
PermissionProfile::External { .. } => return "external",
|
||||
@@ -77,16 +78,18 @@ fn permission_profile_sandbox_tag(
|
||||
}
|
||||
}
|
||||
}
|
||||
if cfg!(target_os = "windows") {
|
||||
match windows_sandbox_level {
|
||||
WindowsSandboxLevel::Elevated => return "windows_elevated",
|
||||
WindowsSandboxLevel::Disabled | WindowsSandboxLevel::RestrictedToken => {}
|
||||
match windows_sandbox_selection {
|
||||
WindowsSandboxSelection::Mxc => SandboxType::WindowsMxc.as_metric_tag(),
|
||||
WindowsSandboxSelection::Elevated => "windows_elevated",
|
||||
WindowsSandboxSelection::RestrictedToken => {
|
||||
SandboxType::WindowsRestrictedToken.as_metric_tag()
|
||||
}
|
||||
WindowsSandboxSelection::Disabled => {
|
||||
get_platform_sandbox(/*windows_sandbox_enabled*/ false)
|
||||
.map(SandboxType::as_metric_tag)
|
||||
.unwrap_or("none")
|
||||
}
|
||||
}
|
||||
|
||||
get_platform_sandbox(windows_sandbox_level != WindowsSandboxLevel::Disabled)
|
||||
.map(SandboxType::as_metric_tag)
|
||||
.unwrap_or("none")
|
||||
}
|
||||
|
||||
fn permission_profile_policy_tag(profile: &PermissionProfile, cwd: &Path) -> &'static str {
|
||||
|
||||
@@ -114,6 +114,18 @@ pub(super) async fn spawn_review_thread(
|
||||
step_settings.telemetry(&parent_turn_context.session_telemetry);
|
||||
let per_turn_config = Arc::new(per_turn_config);
|
||||
let review_turn_id = sub_id.to_string();
|
||||
#[allow(deprecated)]
|
||||
let windows_sandbox_selection = parent_turn_context
|
||||
.environments
|
||||
.primary()
|
||||
.map(TurnEnvironment::windows_sandbox_selection_for_turn_metadata)
|
||||
.unwrap_or_else(|| {
|
||||
crate::tools::sandboxing::configured_windows_sandbox_selection(
|
||||
parent_turn_context.config.permissions.windows_sandbox_type,
|
||||
parent_turn_context.windows_sandbox_level,
|
||||
&codex_utils_path_uri::PathUri::from_abs_path(&parent_turn_context.cwd),
|
||||
)
|
||||
});
|
||||
let turn_metadata_state = Arc::new(TurnMetadataState::new(
|
||||
sess.session_id().to_string(),
|
||||
sess.thread_id().to_string(),
|
||||
@@ -125,7 +137,7 @@ pub(super) async fn spawn_review_thread(
|
||||
#[allow(deprecated)]
|
||||
parent_turn_context.cwd.clone(),
|
||||
&parent_turn_context.permission_profile(),
|
||||
parent_turn_context.windows_sandbox_level,
|
||||
windows_sandbox_selection,
|
||||
parent_turn_context.network.is_some(),
|
||||
auto_review_enabled,
|
||||
&model_info,
|
||||
|
||||
@@ -177,6 +177,7 @@ impl SessionConfiguration {
|
||||
permission_profile: self.permission_profile_state.snapshot(),
|
||||
shell_environment_policy: self.shell_environment_policy.clone(),
|
||||
windows_sandbox_level: self.windows_sandbox_level,
|
||||
windows_sandbox_type: self.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
||||
use_legacy_landlock: self.use_legacy_landlock,
|
||||
exec_policy: None,
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::exec_policy::AllowPrefixRules;
|
||||
use crate::shell_snapshot::ShellSnapshot;
|
||||
use crate::shell_snapshot::ShellSnapshotFile;
|
||||
use crate::shell_snapshot::ShellSnapshotSandbox;
|
||||
use crate::tools::sandboxing::configured_windows_sandbox_selection;
|
||||
use crate::tools::sandboxing::executor_windows_sandbox_selection;
|
||||
use arc_swap::ArcSwap;
|
||||
use codex_core_plugins::PluginCommandAttribution;
|
||||
@@ -228,6 +229,7 @@ impl TurnEnvironment {
|
||||
user_home_dir: self.user_home_dir.clone(),
|
||||
temporary_directories: self.temporary_directories.clone(),
|
||||
windows_sandbox_selection: executor_windows_sandbox_selection(
|
||||
config.windows_sandbox_type,
|
||||
config.windows_sandbox_level,
|
||||
self.cwd(),
|
||||
),
|
||||
@@ -237,6 +239,25 @@ impl TurnEnvironment {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn windows_sandbox_selection_for_turn_metadata(
|
||||
&self,
|
||||
) -> codex_file_system::WindowsSandboxSelection {
|
||||
let config = self.config();
|
||||
if self.environment.is_remote() {
|
||||
executor_windows_sandbox_selection(
|
||||
config.windows_sandbox_type,
|
||||
config.windows_sandbox_level,
|
||||
self.cwd(),
|
||||
)
|
||||
} else {
|
||||
configured_windows_sandbox_selection(
|
||||
config.windows_sandbox_type,
|
||||
config.windows_sandbox_level,
|
||||
self.cwd(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn active_permission_profile(&self) -> Option<ActivePermissionProfile> {
|
||||
self.config().permission_profile.active_permission_profile()
|
||||
}
|
||||
@@ -863,6 +884,16 @@ impl Session {
|
||||
per_turn_config.permissions.approval_policy.value(),
|
||||
per_turn_config.approvals_reviewer,
|
||||
);
|
||||
let windows_sandbox_selection = environments
|
||||
.primary()
|
||||
.map(TurnEnvironment::windows_sandbox_selection_for_turn_metadata)
|
||||
.unwrap_or_else(|| {
|
||||
configured_windows_sandbox_selection(
|
||||
session_configuration.windows_sandbox_type,
|
||||
session_configuration.windows_sandbox_level,
|
||||
&PathUri::from_abs_path(&cwd),
|
||||
)
|
||||
});
|
||||
let per_turn_config = Arc::new(per_turn_config);
|
||||
let turn_metadata_state = Arc::new(TurnMetadataState::new(
|
||||
session_id.to_string(),
|
||||
@@ -874,7 +905,7 @@ impl Session {
|
||||
sub_id.clone(),
|
||||
cwd.clone(),
|
||||
&permission_profile,
|
||||
session_configuration.windows_sandbox_level,
|
||||
windows_sandbox_selection,
|
||||
network.is_some(),
|
||||
auto_review_enabled,
|
||||
model_info,
|
||||
|
||||
@@ -44,6 +44,7 @@ async fn snapshot_failure_omits_credentials_and_stops_descendants(
|
||||
workspace_roots: std::slice::from_ref(&cwd_uri),
|
||||
sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_type: SandboxType::None,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
network_denial_cancellation_token: cancellation.clone(),
|
||||
|
||||
@@ -299,6 +299,7 @@ async fn inactive_profiles_keep_snapshots_but_active_brokers_require_sandbox() -
|
||||
allow_login_shell: false,
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_type: SandboxType::None,
|
||||
windows_sandbox_private_desktop: true,
|
||||
use_legacy_landlock: false,
|
||||
permission_profile: PermissionProfileSnapshot::legacy(permission_profile),
|
||||
@@ -419,6 +420,7 @@ async fn inactive_profiles_keep_snapshots_but_active_brokers_require_sandbox() -
|
||||
workspace_roots: std::slice::from_ref(&cwd_uri),
|
||||
sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_type: SandboxType::None,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
network_denial_cancellation_token: Some(cancellation.clone()),
|
||||
|
||||
@@ -1349,6 +1349,7 @@ async fn spawn_internal_session_preserves_parent_lineage_without_forking_history
|
||||
permission_profile: config.permissions.permission_profile_state().snapshot(),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
windows_sandbox_type: config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: config.permissions.windows_sandbox_private_desktop,
|
||||
use_legacy_landlock: config.features.use_legacy_landlock(),
|
||||
exec_policy: Some(codex_execpolicy::RequirementsExecPolicy::new(
|
||||
|
||||
@@ -37,6 +37,7 @@ use crate::tools::registry::ToolExecutor;
|
||||
use crate::tools::runtimes::apply_patch::ApplyPatchRequest;
|
||||
use crate::tools::runtimes::apply_patch::ApplyPatchRuntime;
|
||||
use crate::tools::sandboxing::ToolCtx;
|
||||
use crate::windows_sandbox::windows_sandbox_level_for_legacy_checks;
|
||||
use codex_apply_patch::ApplyPatchAction;
|
||||
use codex_apply_patch::ApplyPatchFileChange;
|
||||
use codex_apply_patch::ApplyPatchFileUpdateMode;
|
||||
@@ -556,7 +557,10 @@ async fn execute_verified_patch(
|
||||
let sandbox_route = if turn_environment.environment.is_remote() {
|
||||
PatchSandboxRoute::ExecutorManaged
|
||||
} else {
|
||||
PatchSandboxRoute::Platform(turn_environment.config().windows_sandbox_level)
|
||||
PatchSandboxRoute::Platform(windows_sandbox_level_for_legacy_checks(
|
||||
turn_environment.config().windows_sandbox_type,
|
||||
turn_environment.config().windows_sandbox_level,
|
||||
))
|
||||
};
|
||||
let (file_paths, effective_additional_permissions, file_system_sandbox_policy) =
|
||||
effective_patch_permissions(
|
||||
|
||||
@@ -212,7 +212,10 @@ impl ExecCommandHandler {
|
||||
&& SandboxManager::new().select_initial(
|
||||
turn_environment.permission_profile(),
|
||||
SandboxablePreference::Auto,
|
||||
turn_environment.config().windows_sandbox_level,
|
||||
codex_protocol::sandbox::effective_windows_sandbox_type(
|
||||
turn_environment.config().windows_sandbox_type,
|
||||
turn_environment.config().windows_sandbox_level,
|
||||
),
|
||||
turn.network.is_some(),
|
||||
) != SandboxType::None;
|
||||
// `to_abs_path()` alone cannot identify foreign drive paths: `file:///C:/repo` is
|
||||
|
||||
@@ -90,6 +90,7 @@ impl ToolOrchestrator {
|
||||
workspace_roots: attempt.workspace_roots,
|
||||
sandbox_exe: attempt.sandbox_exe,
|
||||
use_legacy_landlock: attempt.use_legacy_landlock,
|
||||
windows_sandbox_type: attempt.windows_sandbox_type,
|
||||
windows_sandbox_level: attempt.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: attempt.windows_sandbox_private_desktop,
|
||||
network_denial_cancellation_token: network_approval
|
||||
@@ -275,11 +276,15 @@ impl ToolOrchestrator {
|
||||
managed_network_active,
|
||||
),
|
||||
};
|
||||
let windows_sandbox_type = codex_protocol::sandbox::effective_windows_sandbox_type(
|
||||
sandbox_config.windows_sandbox_type,
|
||||
sandbox_config.windows_sandbox_level,
|
||||
);
|
||||
let initial_sandbox = if sandbox_requested && !executor_managed_process_sandbox {
|
||||
sandbox_manager.select_initial(
|
||||
&permissions,
|
||||
sandbox_preference,
|
||||
sandbox_config.windows_sandbox_level,
|
||||
windows_sandbox_type,
|
||||
managed_network_active,
|
||||
)
|
||||
} else {
|
||||
@@ -306,6 +311,7 @@ impl ToolOrchestrator {
|
||||
workspace_roots,
|
||||
sandbox_exe: codex_sandbox_exe,
|
||||
use_legacy_landlock: sandbox_config.use_legacy_landlock,
|
||||
windows_sandbox_type,
|
||||
windows_sandbox_level: sandbox_config.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: sandbox_config.windows_sandbox_private_desktop,
|
||||
network_denial_cancellation_token: None,
|
||||
@@ -458,7 +464,7 @@ impl ToolOrchestrator {
|
||||
sandbox_manager.select_initial(
|
||||
&permissions,
|
||||
sandbox_preference,
|
||||
sandbox_config.windows_sandbox_level,
|
||||
windows_sandbox_type,
|
||||
managed_network_active,
|
||||
)
|
||||
} else {
|
||||
@@ -480,6 +486,7 @@ impl ToolOrchestrator {
|
||||
workspace_roots,
|
||||
sandbox_exe: retry_sandbox_exe,
|
||||
use_legacy_landlock: sandbox_config.use_legacy_landlock,
|
||||
windows_sandbox_type,
|
||||
windows_sandbox_level: sandbox_config.windows_sandbox_level,
|
||||
windows_sandbox_private_desktop: sandbox_config.windows_sandbox_private_desktop,
|
||||
network_denial_cancellation_token: None,
|
||||
|
||||
@@ -103,6 +103,7 @@ impl ApplyPatchRuntime {
|
||||
user_home_dir: req.turn_environment.user_home_dir.clone(),
|
||||
temporary_directories: None,
|
||||
windows_sandbox_selection: executor_windows_sandbox_selection(
|
||||
attempt.windows_sandbox_type,
|
||||
attempt.windows_sandbox_level,
|
||||
attempt.sandbox_cwd,
|
||||
),
|
||||
|
||||
@@ -29,6 +29,7 @@ fn test_turn_environment(environment_id: &str) -> crate::session::turn_context::
|
||||
allow_login_shell: true,
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_type: SandboxType::None,
|
||||
windows_sandbox_private_desktop: true,
|
||||
use_legacy_landlock: false,
|
||||
permission_profile: PermissionProfileSnapshot::legacy(
|
||||
@@ -248,6 +249,7 @@ async fn file_system_sandbox_context_preserves_executor_workspace_permissions()
|
||||
workspace_roots: std::slice::from_ref(&sandbox_policy_cwd),
|
||||
sandbox_exe: None,
|
||||
use_legacy_landlock: true,
|
||||
windows_sandbox_type: SandboxType::WindowsRestrictedToken,
|
||||
windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
|
||||
windows_sandbox_private_desktop: true,
|
||||
network_denial_cancellation_token: None,
|
||||
@@ -316,6 +318,7 @@ async fn file_system_sandbox_context_respects_sandbox_request() {
|
||||
workspace_roots: std::slice::from_ref(&sandbox_policy_cwd),
|
||||
sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_type: SandboxType::None,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
network_denial_cancellation_token: None,
|
||||
|
||||
@@ -126,6 +126,7 @@ async fn explicit_escalation_prepares_exec_without_managed_network() -> anyhow::
|
||||
workspace_roots: std::slice::from_ref(&sandbox_policy_cwd),
|
||||
sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_type: SandboxType::None,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
network_denial_cancellation_token: None,
|
||||
|
||||
@@ -752,6 +752,7 @@ mod tests {
|
||||
allow_login_shell: true,
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_type: codex_sandboxing::SandboxType::None,
|
||||
windows_sandbox_private_desktop: true,
|
||||
use_legacy_landlock: false,
|
||||
permission_profile: PermissionProfileSnapshot::legacy(
|
||||
|
||||
@@ -768,7 +768,7 @@ impl CoreShellCommandExecutor {
|
||||
let sandbox = sandbox_manager.select_initial(
|
||||
permission_profile,
|
||||
SandboxablePreference::Auto,
|
||||
self.windows_sandbox_level,
|
||||
SandboxType::None,
|
||||
self.network.is_some(),
|
||||
);
|
||||
let cwd = PathUri::from_abs_path(workdir);
|
||||
|
||||
@@ -399,6 +399,7 @@ pub(crate) struct SandboxAttempt<'a> {
|
||||
// TODO(anp): Reconcile these attempt settings with TurnEnvironment::sandbox_context
|
||||
// so process execution and patch writes honor the selected environment's backend.
|
||||
pub use_legacy_landlock: bool,
|
||||
pub windows_sandbox_type: SandboxType,
|
||||
pub windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel,
|
||||
pub windows_sandbox_private_desktop: bool,
|
||||
pub network_denial_cancellation_token: Option<CancellationToken>,
|
||||
@@ -406,10 +407,12 @@ pub(crate) struct SandboxAttempt<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn executor_windows_sandbox_level(
|
||||
windows_sandbox_type: SandboxType,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
cwd: &PathUri,
|
||||
) -> WindowsSandboxLevel {
|
||||
if windows_sandbox_level == WindowsSandboxLevel::Disabled
|
||||
if windows_sandbox_type != SandboxType::WindowsMxc
|
||||
&& windows_sandbox_level == WindowsSandboxLevel::Disabled
|
||||
&& cwd.infer_path_convention() == Some(PathConvention::Windows)
|
||||
{
|
||||
WindowsSandboxLevel::RestrictedToken
|
||||
@@ -419,13 +422,30 @@ pub(crate) fn executor_windows_sandbox_level(
|
||||
}
|
||||
|
||||
pub(crate) fn executor_windows_sandbox_selection(
|
||||
windows_sandbox_type: SandboxType,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
cwd: &PathUri,
|
||||
) -> WindowsSandboxSelection {
|
||||
if cwd.infer_path_convention() == Some(PathConvention::Windows) {
|
||||
executor_windows_sandbox_level(windows_sandbox_level, cwd).into()
|
||||
configured_windows_sandbox_selection(
|
||||
windows_sandbox_type,
|
||||
executor_windows_sandbox_level(windows_sandbox_type, windows_sandbox_level, cwd),
|
||||
cwd,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn configured_windows_sandbox_selection(
|
||||
windows_sandbox_type: SandboxType,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
cwd: &PathUri,
|
||||
) -> WindowsSandboxSelection {
|
||||
if cwd.infer_path_convention() != Some(PathConvention::Windows) {
|
||||
return WindowsSandboxSelection::Disabled;
|
||||
}
|
||||
|
||||
if windows_sandbox_type == SandboxType::WindowsMxc {
|
||||
WindowsSandboxSelection::Mxc
|
||||
} else {
|
||||
WindowsSandboxSelection::Disabled
|
||||
windows_sandbox_level.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,6 +546,7 @@ impl<'a> SandboxAttempt<'a> {
|
||||
user_home_dir: None,
|
||||
temporary_directories: None,
|
||||
windows_sandbox_selection: executor_windows_sandbox_selection(
|
||||
self.windows_sandbox_type,
|
||||
self.windows_sandbox_level,
|
||||
self.sandbox_cwd,
|
||||
),
|
||||
|
||||
@@ -73,6 +73,43 @@ fn restricted_sandbox_requires_exec_approval_on_request() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn windows_sandbox_selection_distinguishes_configured_and_executor_defaults() {
|
||||
let cwd = PathUri::parse("file:///C:/workspace").expect("Windows path URI");
|
||||
assert_eq!(
|
||||
executor_windows_sandbox_level(
|
||||
SandboxType::WindowsMxc,
|
||||
codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
&cwd,
|
||||
),
|
||||
codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
);
|
||||
assert_eq!(
|
||||
executor_windows_sandbox_selection(
|
||||
SandboxType::WindowsMxc,
|
||||
codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
&cwd,
|
||||
),
|
||||
codex_file_system::WindowsSandboxSelection::Mxc,
|
||||
);
|
||||
assert_eq!(
|
||||
configured_windows_sandbox_selection(
|
||||
SandboxType::None,
|
||||
codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
&cwd,
|
||||
),
|
||||
codex_file_system::WindowsSandboxSelection::Disabled,
|
||||
);
|
||||
assert_eq!(
|
||||
executor_windows_sandbox_selection(
|
||||
SandboxType::None,
|
||||
codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
&cwd,
|
||||
),
|
||||
codex_file_system::WindowsSandboxSelection::RestrictedToken,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_exec_approval_requirement_rejects_sandbox_prompt_when_granular_disables_it() {
|
||||
let policy = AskForApproval::Granular(GranularApprovalConfig {
|
||||
@@ -257,6 +294,7 @@ fn windows_sandbox_env_preserves_denied_reads_or_rejects_unsupported_backend() {
|
||||
workspace_roots: std::slice::from_ref(&cwd_uri),
|
||||
sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_type: SandboxType::WindowsRestrictedToken,
|
||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Elevated,
|
||||
windows_sandbox_private_desktop: false,
|
||||
network_denial_cancellation_token: None,
|
||||
@@ -328,6 +366,7 @@ fn exec_server_env_keeps_command_native_and_carries_sandbox_context() {
|
||||
workspace_roots: std::slice::from_ref(&cwd_uri),
|
||||
sandbox_exe: None,
|
||||
use_legacy_landlock: false,
|
||||
windows_sandbox_type: SandboxType::None,
|
||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
windows_sandbox_private_desktop: false,
|
||||
network_denial_cancellation_token: None,
|
||||
|
||||
@@ -1256,6 +1256,7 @@ async fn zsh_fork_unified_exec_keeps_shell_parameter_when_remote_environment_ava
|
||||
allow_login_shell: true,
|
||||
workspace_roots: Vec::new(),
|
||||
windows_sandbox_level: turn.windows_sandbox_level,
|
||||
windows_sandbox_type: turn.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: turn
|
||||
.config
|
||||
.permissions
|
||||
|
||||
@@ -26,13 +26,13 @@ use crate::responses_metadata::subagent_metadata_kind;
|
||||
use crate::sandbox_tags::SandboxTags;
|
||||
use crate::sandbox_tags::record_policy_metadata;
|
||||
use crate::session::step_settings::ResolvedStepSettings;
|
||||
use codex_file_system::WindowsSandboxSelection;
|
||||
use codex_git_utils::SanitizedGitUrl;
|
||||
use codex_git_utils::get_git_remote_urls_assume_git_repo;
|
||||
use codex_git_utils::get_has_changes_in_repo;
|
||||
use codex_git_utils::get_head_commit_hash;
|
||||
use codex_protocol::AgentPath;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
@@ -224,7 +224,7 @@ impl TurnMetadataState {
|
||||
turn_id: String,
|
||||
cwd: AbsolutePathBuf,
|
||||
permission_profile: &PermissionProfile,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
windows_sandbox_selection: impl Into<WindowsSandboxSelection>,
|
||||
enforce_managed_network: bool,
|
||||
auto_review_enabled: bool,
|
||||
model_info: &ModelInfo,
|
||||
@@ -232,7 +232,7 @@ impl TurnMetadataState {
|
||||
let sandbox_tags = SandboxTags::new(
|
||||
permission_profile,
|
||||
cwd.as_path(),
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_selection.into(),
|
||||
enforce_managed_network,
|
||||
);
|
||||
let agent_name = session_source
|
||||
|
||||
@@ -29,6 +29,7 @@ use codex_analytics::CompactionTrigger;
|
||||
use codex_analytics::TurnAnalyticsMetadata;
|
||||
use codex_models_manager::model_info::model_info_from_slug;
|
||||
use codex_protocol::AgentPath;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
use codex_protocol::protocol::SessionSource;
|
||||
|
||||
@@ -68,6 +68,7 @@ use crate::unified_exec::process::UnifiedExecProcess;
|
||||
use crate::unified_exec::shell_snapshot::shell_snapshot_request;
|
||||
use crate::unified_exec::take_plugin_metrics_sidecar;
|
||||
use crate::unified_exec::trace_id;
|
||||
use crate::windows_sandbox::windows_sandbox_level_for_legacy_checks;
|
||||
use codex_core_plugins::PLUGIN_METRICS_OUTPUT_ENV_VAR;
|
||||
use codex_core_plugins::PluginCommandAttribution;
|
||||
use codex_core_plugins::PluginMetricsSidecar;
|
||||
@@ -1488,7 +1489,10 @@ impl UnifiedExecProcessManager {
|
||||
approval_policy: context.step_context.settings.approval_policy(),
|
||||
permission_profile: request.turn_environment.permission_profile().clone(),
|
||||
environment_policy: request.turn_environment.config().exec_policy.as_ref(),
|
||||
windows_sandbox_level: request.turn_environment.config().windows_sandbox_level,
|
||||
windows_sandbox_level: windows_sandbox_level_for_legacy_checks(
|
||||
request.turn_environment.config().windows_sandbox_type,
|
||||
request.turn_environment.config().windows_sandbox_level,
|
||||
),
|
||||
sandbox_permissions: if request.additional_permissions_preapproved {
|
||||
crate::sandboxing::SandboxPermissions::UseDefault
|
||||
} else {
|
||||
|
||||
@@ -98,6 +98,7 @@ impl Session {
|
||||
user_home_dir: None,
|
||||
temporary_directories: None,
|
||||
windows_sandbox_selection: executor_windows_sandbox_selection(
|
||||
environment.config().windows_sandbox_type,
|
||||
environment.config().windows_sandbox_level,
|
||||
environment.cwd(),
|
||||
),
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::session::turn_context::TurnContext;
|
||||
use crate::session::turn_context::TurnEnvironment;
|
||||
use crate::tools::sandboxing::ApprovalAction;
|
||||
use crate::tools::sandboxing::ToolError;
|
||||
use crate::windows_sandbox::windows_sandbox_level_for_legacy_checks;
|
||||
use codex_features::Feature;
|
||||
use codex_file_system::FileSystemSandboxContext;
|
||||
use codex_network_proxy::EnvironmentNetworkPolicy;
|
||||
@@ -52,10 +53,16 @@ impl TerminalPolicy {
|
||||
additional_permissions: Option<AdditionalPermissionProfile>,
|
||||
) -> Self {
|
||||
let mut sandbox = environment.sandbox_context(additional_permissions);
|
||||
if matches!(sandbox_source, TerminalSandboxSource::Native) {
|
||||
if matches!(sandbox_source, TerminalSandboxSource::Native)
|
||||
&& windows_sandbox_level_for_legacy_checks(
|
||||
environment.config().windows_sandbox_type,
|
||||
environment.config().windows_sandbox_level,
|
||||
) == codex_protocol::config_types::WindowsSandboxLevel::Disabled
|
||||
{
|
||||
// The filesystem helper applies executor defaults, but native process
|
||||
// launches honor Disabled. Preserve it so later enablement is detected.
|
||||
sandbox.windows_sandbox_selection = environment.config().windows_sandbox_level.into();
|
||||
sandbox.windows_sandbox_selection =
|
||||
codex_protocol::config_types::WindowsSandboxLevel::Disabled.into();
|
||||
}
|
||||
Self {
|
||||
sandbox,
|
||||
|
||||
@@ -10,6 +10,7 @@ use codex_protocol::permissions::FileSystemAccessMode;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::json;
|
||||
@@ -221,11 +222,13 @@ async fn readable_snapshot_does_not_require_stdin_approval() -> anyhow::Result<(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case::test_case(TerminalSandboxSource::Native, SandboxPermissions::RequireEscalated; "native_disabled_sandbox_needs_review_when_enabled")]
|
||||
#[test_case::test_case(TerminalSandboxSource::Executor, SandboxPermissions::UseDefault; "executor_keeps_its_restricted_token_default")]
|
||||
#[test_case::test_case(TerminalSandboxSource::Native, SandboxType::None, SandboxPermissions::RequireEscalated; "native_disabled_sandbox_needs_review_when_enabled")]
|
||||
#[test_case::test_case(TerminalSandboxSource::Executor, SandboxType::None, SandboxPermissions::UseDefault; "executor_keeps_its_restricted_token_default")]
|
||||
#[test_case::test_case(TerminalSandboxSource::Native, SandboxType::WindowsMxc, SandboxPermissions::UseDefault; "native_mxc_ignores_legacy_level_changes")]
|
||||
#[tokio::test]
|
||||
async fn enabling_windows_sandbox_respects_the_launch_backend(
|
||||
source: TerminalSandboxSource,
|
||||
sandbox_type: SandboxType,
|
||||
expected: SandboxPermissions,
|
||||
) -> anyhow::Result<()> {
|
||||
let (_session, turn) = make_session_and_context().await;
|
||||
@@ -233,7 +236,16 @@ async fn enabling_windows_sandbox_respects_the_launch_backend(
|
||||
environment.selection.cwd = PathUri::parse("file:///C:/workspace")?;
|
||||
environment.config_mut().permission_profile =
|
||||
PermissionProfileSnapshot::legacy(PermissionProfile::read_only());
|
||||
environment.config_mut().windows_sandbox_type = sandbox_type;
|
||||
environment.config_mut().windows_sandbox_level = WindowsSandboxLevel::Disabled;
|
||||
assert_eq!(
|
||||
environment.windows_sandbox_selection_for_turn_metadata(),
|
||||
if sandbox_type == SandboxType::WindowsMxc {
|
||||
codex_file_system::WindowsSandboxSelection::Mxc
|
||||
} else {
|
||||
codex_file_system::WindowsSandboxSelection::Disabled
|
||||
}
|
||||
);
|
||||
let permissions = TerminalPermissions::for_launch(
|
||||
&environment,
|
||||
&turn,
|
||||
|
||||
@@ -9,6 +9,7 @@ use codex_login::default_client::originator;
|
||||
use codex_otel::sanitize_metric_tag_value;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::sandbox::effective_windows_sandbox_type;
|
||||
use codex_sandboxing::SandboxType;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use std::collections::BTreeMap;
|
||||
@@ -27,6 +28,19 @@ pub fn managed_proxy_routing_for_windows_sandbox(
|
||||
}
|
||||
}
|
||||
|
||||
/// Adapts the selected implementation for legacy checks that only understand setup levels.
|
||||
/// Backend selection must continue to use [`SandboxType`] directly.
|
||||
pub(crate) fn windows_sandbox_level_for_legacy_checks(
|
||||
sandbox_type: SandboxType,
|
||||
sandbox_level: WindowsSandboxLevel,
|
||||
) -> WindowsSandboxLevel {
|
||||
if effective_windows_sandbox_type(sandbox_type, sandbox_level) == SandboxType::WindowsMxc {
|
||||
WindowsSandboxLevel::RestrictedToken
|
||||
} else {
|
||||
sandbox_level
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WindowsSandboxLevelExt {
|
||||
fn from_config(config: &Config) -> WindowsSandboxLevel;
|
||||
fn from_features(features: &Features) -> WindowsSandboxLevel;
|
||||
@@ -37,6 +51,7 @@ impl WindowsSandboxLevelExt for WindowsSandboxLevel {
|
||||
match config.permissions.windows_sandbox_mode {
|
||||
Some(WindowsSandboxModeToml::Elevated) => WindowsSandboxLevel::Elevated,
|
||||
Some(WindowsSandboxModeToml::Unelevated) => WindowsSandboxLevel::RestrictedToken,
|
||||
Some(WindowsSandboxModeToml::Mxc) => WindowsSandboxLevel::Disabled,
|
||||
None => Self::from_features(&config.features),
|
||||
}
|
||||
}
|
||||
@@ -64,7 +79,7 @@ pub fn resolve_windows_sandbox_private_desktop(cfg: &ConfigToml) -> bool {
|
||||
cfg.windows
|
||||
.as_ref()
|
||||
.and_then(|windows| windows.sandbox_private_desktop)
|
||||
.unwrap_or(true)
|
||||
.unwrap_or_else(|| resolve_windows_sandbox_mode(cfg) != Some(WindowsSandboxModeToml::Mxc))
|
||||
}
|
||||
|
||||
pub fn legacy_windows_sandbox_mode(
|
||||
|
||||
@@ -385,6 +385,7 @@ async fn v2_residency_reload_preserves_inherited_environment_and_tools(
|
||||
permission_profile: PermissionProfileSnapshot::legacy(child_permissions),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
|
||||
@@ -52,11 +52,13 @@ use codex_utils_path_uri::PathUri;
|
||||
use core_test_support::PathBufExt;
|
||||
use core_test_support::TestTargetOs;
|
||||
use core_test_support::assert_regex_match;
|
||||
use core_test_support::is_wine_exec_test_environment;
|
||||
use core_test_support::responses::ev_assistant_message;
|
||||
use core_test_support::responses::ev_completed;
|
||||
use core_test_support::responses::ev_exec_command_call_with_args;
|
||||
use core_test_support::responses::ev_function_call;
|
||||
use core_test_support::responses::ev_response_created;
|
||||
use core_test_support::responses::mount_function_call_agent_response;
|
||||
use core_test_support::responses::mount_sse_sequence;
|
||||
use core_test_support::responses::sse;
|
||||
use core_test_support::responses::start_mock_server;
|
||||
@@ -244,6 +246,68 @@ pub async fn mount_apply_patch(
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn mxc_config_routes_command_and_patch_to_the_windows_executor() -> Result<()> {
|
||||
if !is_wine_exec_test_environment() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let harness = apply_patch_harness_with(|builder| {
|
||||
builder.with_pre_build_hook(|home| {
|
||||
fs::write(home.join("config.toml"), "[windows]\nsandbox = \"mxc\"\n")
|
||||
.expect("write MXC config");
|
||||
})
|
||||
})
|
||||
.await?;
|
||||
let exec_response = mount_function_call_agent_response(
|
||||
harness.server(),
|
||||
"mxc-exec",
|
||||
&json!({"cmd": "Write-Output should-not-run"}).to_string(),
|
||||
"exec_command",
|
||||
)
|
||||
.await;
|
||||
harness
|
||||
.submit_with_permission_profile(
|
||||
"exercise MXC command routing",
|
||||
PermissionProfile::workspace_write(),
|
||||
)
|
||||
.await?;
|
||||
mount_apply_patch(
|
||||
&harness,
|
||||
"mxc-patch",
|
||||
"*** Begin Patch\n*** Add File: should-not-exist.txt\n+blocked\n*** End Patch",
|
||||
"done",
|
||||
)
|
||||
.await;
|
||||
harness
|
||||
.submit_with_permission_profile(
|
||||
"exercise MXC patch routing",
|
||||
PermissionProfile::workspace_write(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let exec_output = harness.function_call_stdout("mxc-exec").await;
|
||||
assert!(
|
||||
exec_output.contains("native MXC is unavailable"),
|
||||
"expected MXC unavailability error, got: {exec_output:?}"
|
||||
);
|
||||
let patch_output = harness.apply_patch_output("mxc-patch").await;
|
||||
assert!(
|
||||
patch_output.contains("Failed to write file"),
|
||||
"expected apply_patch failure, got: {patch_output:?}"
|
||||
);
|
||||
let metadata: serde_json::Value = serde_json::from_str(
|
||||
&exec_response
|
||||
.function_call
|
||||
.single_request()
|
||||
.header("x-codex-turn-metadata")
|
||||
.expect("turn metadata header"),
|
||||
)?;
|
||||
assert_eq!(metadata["sandbox"], "windows_mxc");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn mount_apply_patch_model_output(
|
||||
harness: &TestCodexHarness,
|
||||
call_id: &str,
|
||||
|
||||
@@ -720,6 +720,7 @@ async fn environment_command_restrictions_override_saved_prefix_approvals() -> R
|
||||
permission_profile: PermissionProfileSnapshot::legacy(PermissionProfile::Disabled),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
@@ -750,6 +751,7 @@ async fn environment_command_restrictions_override_saved_prefix_approvals() -> R
|
||||
permission_profile: PermissionProfileSnapshot::legacy(PermissionProfile::Disabled),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
@@ -861,6 +863,7 @@ async fn environment_command_policy_changes_invalidate_session_approvals() -> Re
|
||||
),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
|
||||
@@ -1633,6 +1633,7 @@ async fn guardian_session_is_reused_for_consecutive_tool_reviews_without_prewarm
|
||||
),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test.config.permissions.windows_sandbox_private_desktop,
|
||||
use_legacy_landlock: test.config.features.use_legacy_landlock(),
|
||||
exec_policy: None,
|
||||
|
||||
@@ -838,6 +838,7 @@ impl ExecutorHookFixture {
|
||||
),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&self.test.config),
|
||||
windows_sandbox_type: self.test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: self
|
||||
.test
|
||||
.config
|
||||
|
||||
@@ -366,6 +366,7 @@ async fn skill_mcp_dependency_oauth_uses_configured_http_client() -> Result<()>
|
||||
permission_profile: PermissionProfileSnapshot::legacy(PermissionProfile::Disabled),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&fixture.config),
|
||||
windows_sandbox_type: fixture.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: fixture.config.permissions.windows_sandbox_private_desktop,
|
||||
use_legacy_landlock: fixture.config.features.use_legacy_landlock(),
|
||||
exec_policy: None,
|
||||
|
||||
@@ -497,6 +497,7 @@ async fn root_reconciliation_reuses_pending_apps_startup() -> Result<()> {
|
||||
),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
|
||||
@@ -2429,6 +2429,7 @@ async fn owner_network_policy_follows_the_selected_remote_command() -> Result<()
|
||||
}),
|
||||
shell_environment_policy: test.config.permissions.shell_environment_policy.clone(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
|
||||
@@ -597,6 +597,7 @@ async fn environment_permissions_follow_configuration_ownership() -> Result<()>
|
||||
permission_profile: owner_permission_profile,
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
@@ -1461,6 +1462,7 @@ async fn shared_executor_keeps_ready_capability_roots_scoped_to_each_attachment(
|
||||
permission_profile: permission_profile.clone(),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
@@ -1506,6 +1508,7 @@ async fn shared_executor_keeps_ready_capability_roots_scoped_to_each_attachment(
|
||||
permission_profile: permission_profile.clone(),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
@@ -1535,6 +1538,7 @@ async fn shared_executor_keeps_ready_capability_roots_scoped_to_each_attachment(
|
||||
permission_profile: permission_profile.clone(),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
@@ -1603,6 +1607,7 @@ async fn shared_executor_keeps_ready_capability_roots_scoped_to_each_attachment(
|
||||
permission_profile: permission_profile.clone(),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
@@ -1679,6 +1684,7 @@ async fn owner_network_policy_rejects_unsupported_environment_authority() -> Res
|
||||
permission_profile: PermissionProfileSnapshot::legacy(PermissionProfile::Disabled),
|
||||
shell_environment_policy: test.config.permissions.shell_environment_policy.clone(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test.config.permissions.windows_sandbox_private_desktop,
|
||||
use_legacy_landlock: test.config.features.use_legacy_landlock(),
|
||||
exec_policy: None,
|
||||
@@ -1768,6 +1774,7 @@ async fn pending_attachment_installs_configuration_before_waiting_turn_resumes()
|
||||
permission_profile: PermissionProfileSnapshot::legacy(PermissionProfile::read_only()),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test.config.permissions.windows_sandbox_private_desktop,
|
||||
use_legacy_landlock: test.config.features.use_legacy_landlock(),
|
||||
exec_policy: None,
|
||||
@@ -2435,6 +2442,7 @@ async fn deferred_executor_spawn_agent_inherits_ready_step_environments(
|
||||
),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
|
||||
@@ -828,6 +828,7 @@ async fn environment_mcp_policy_filters_runtime_config_and_model_tools(
|
||||
),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&fixture.config),
|
||||
windows_sandbox_type: fixture.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: fixture
|
||||
.config
|
||||
.permissions
|
||||
@@ -1890,6 +1891,7 @@ async fn stdio_mcp_tool_call_includes_sandbox_state_meta(
|
||||
),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&fixture.config),
|
||||
windows_sandbox_type: fixture.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: fixture
|
||||
.config
|
||||
.permissions
|
||||
|
||||
@@ -700,6 +700,7 @@ async fn shell_snapshot_v2_guardian_uses_its_resolved_permissions_and_tools(
|
||||
permission_profile: PermissionProfileSnapshot::legacy(PermissionProfile::Disabled),
|
||||
shell_environment_policy: test.config.permissions.shell_environment_policy.clone(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&test.config),
|
||||
windows_sandbox_type: test.config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: test.config.permissions.windows_sandbox_private_desktop,
|
||||
use_legacy_landlock: test.config.features.use_legacy_landlock(),
|
||||
exec_policy: None,
|
||||
|
||||
@@ -505,6 +505,7 @@ async fn exec_command_uses_installed_environment_shell_policy_with_explicit_over
|
||||
..Default::default()
|
||||
},
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&harness.test().config),
|
||||
windows_sandbox_type: harness.test().config.permissions.windows_sandbox_type,
|
||||
windows_sandbox_private_desktop: harness
|
||||
.test()
|
||||
.config
|
||||
|
||||
@@ -7,6 +7,7 @@ use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::models::PermissionProfileSnapshot;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_protocol::sandbox::SandboxType;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
#[cfg(windows)]
|
||||
@@ -277,6 +278,7 @@ async fn workspace_roots_allow_file_and_command_writes_in_secondary_root(
|
||||
),
|
||||
shell_environment_policy: Default::default(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
|
||||
windows_sandbox_type: SandboxType::WindowsRestrictedToken,
|
||||
windows_sandbox_private_desktop: test
|
||||
.config
|
||||
.permissions
|
||||
|
||||
@@ -20,10 +20,16 @@ pub(crate) fn select_sandbox(
|
||||
WindowsSandboxSelection::Elevated => WindowsSandboxLevel::Elevated,
|
||||
WindowsSandboxSelection::Mxc => return (SandboxType::WindowsMxc, None),
|
||||
};
|
||||
let windows_sandbox_type = match windows_sandbox_level {
|
||||
WindowsSandboxLevel::Disabled => SandboxType::None,
|
||||
WindowsSandboxLevel::RestrictedToken | WindowsSandboxLevel::Elevated => {
|
||||
SandboxType::WindowsRestrictedToken
|
||||
}
|
||||
};
|
||||
let sandbox_type = manager.select_initial(
|
||||
permission_profile,
|
||||
SandboxablePreference::Require,
|
||||
windows_sandbox_level,
|
||||
windows_sandbox_type,
|
||||
has_managed_network_requirements,
|
||||
);
|
||||
(sandbox_type, Some(windows_sandbox_level))
|
||||
|
||||
@@ -54,6 +54,7 @@ pub(super) async fn spawn_command_under_sandbox(
|
||||
&[PathUri::from_abs_path(sandbox_cwd)],
|
||||
&codex_linux_sandbox_exe,
|
||||
/*codex_self_exe*/ &None,
|
||||
codex_protocol::sandbox::SandboxType::None,
|
||||
/*use_legacy_landlock*/ false,
|
||||
)
|
||||
.map_err(|err| io::Error::other(err.to_string()))?;
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::mcp_policy::EnvironmentMcpPolicy;
|
||||
use crate::models::PermissionProfile;
|
||||
use crate::models::PermissionProfileSnapshot;
|
||||
use crate::protocol::AskForApproval;
|
||||
use crate::sandbox::SandboxType;
|
||||
use codex_execpolicy::RequirementsExecPolicy;
|
||||
use codex_network_proxy::EnvironmentNetworkPolicy;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
@@ -64,8 +65,10 @@ pub struct EnvironmentConfig {
|
||||
pub permission_profile: PermissionProfileSnapshot,
|
||||
/// Controls which environment variables shell commands may inherit.
|
||||
pub shell_environment_policy: ShellEnvironmentPolicy,
|
||||
/// Windows sandbox implementation for this environment attachment.
|
||||
/// Legacy Windows restricted-token setup level for this environment attachment.
|
||||
pub windows_sandbox_level: WindowsSandboxLevel,
|
||||
/// Concrete Windows sandbox backend selected for this environment attachment.
|
||||
pub windows_sandbox_type: SandboxType,
|
||||
/// Whether Windows sandbox processes use a private desktop.
|
||||
pub windows_sandbox_private_desktop: bool,
|
||||
/// Whether Linux sandbox processes use the legacy Landlock backend.
|
||||
@@ -89,6 +92,7 @@ impl std::fmt::Debug for EnvironmentConfig {
|
||||
.field("permission_profile", &self.permission_profile)
|
||||
.field("shell_environment_policy", &"<redacted>")
|
||||
.field("windows_sandbox_level", &self.windows_sandbox_level)
|
||||
.field("windows_sandbox_type", &self.windows_sandbox_type)
|
||||
.field(
|
||||
"windows_sandbox_private_desktop",
|
||||
&self.windows_sandbox_private_desktop,
|
||||
|
||||
@@ -48,6 +48,7 @@ pub mod realtime;
|
||||
pub mod request_permissions;
|
||||
pub mod request_user_input;
|
||||
pub mod review_format;
|
||||
pub mod sandbox;
|
||||
pub mod security_risk;
|
||||
pub mod shell_environment;
|
||||
pub mod turn_input;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//! Identifies the platform sandbox implementation selected for execution.
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::config_types::WindowsSandboxLevel;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum SandboxType {
|
||||
None,
|
||||
MacosSeatbelt,
|
||||
LinuxSeccomp,
|
||||
WindowsRestrictedToken,
|
||||
WindowsMxc,
|
||||
}
|
||||
|
||||
impl SandboxType {
|
||||
pub fn as_metric_tag(self) -> &'static str {
|
||||
match self {
|
||||
Self::None => "none",
|
||||
Self::MacosSeatbelt => "seatbelt",
|
||||
Self::LinuxSeccomp => "seccomp",
|
||||
Self::WindowsRestrictedToken => "windows_sandbox",
|
||||
Self::WindowsMxc => "windows_mxc",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Preserves explicit MXC selection while honoring legacy runtime level updates.
|
||||
pub fn effective_windows_sandbox_type(
|
||||
sandbox_type: SandboxType,
|
||||
sandbox_level: WindowsSandboxLevel,
|
||||
) -> SandboxType {
|
||||
match (sandbox_type, sandbox_level) {
|
||||
(SandboxType::WindowsMxc, _) => SandboxType::WindowsMxc,
|
||||
(_, WindowsSandboxLevel::Disabled) => SandboxType::None,
|
||||
(_, WindowsSandboxLevel::RestrictedToken | WindowsSandboxLevel::Elevated) => {
|
||||
SandboxType::WindowsRestrictedToken
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_protocol::protocol::SandboxPolicy;
|
||||
pub use codex_protocol::sandbox::SandboxType;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use std::collections::HashMap;
|
||||
@@ -37,27 +38,6 @@ const WINDOWS_SANDBOX_WRAPPER_SETUP_ENV_ALLOWLIST: &[&str] = &[
|
||||
"SYSTEMROOT",
|
||||
];
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum SandboxType {
|
||||
None,
|
||||
MacosSeatbelt,
|
||||
LinuxSeccomp,
|
||||
WindowsRestrictedToken,
|
||||
WindowsMxc,
|
||||
}
|
||||
|
||||
impl SandboxType {
|
||||
pub fn as_metric_tag(self) -> &'static str {
|
||||
match self {
|
||||
SandboxType::None => "none",
|
||||
SandboxType::MacosSeatbelt => "seatbelt",
|
||||
SandboxType::LinuxSeccomp => "seccomp",
|
||||
SandboxType::WindowsRestrictedToken => "windows_sandbox",
|
||||
SandboxType::WindowsMxc => "windows_mxc",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum SandboxablePreference {
|
||||
Auto,
|
||||
@@ -324,7 +304,7 @@ impl SandboxManager {
|
||||
&self,
|
||||
permission_profile: &PermissionProfile,
|
||||
pref: SandboxablePreference,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
windows_sandbox_type: SandboxType,
|
||||
has_managed_network_requirements: bool,
|
||||
) -> SandboxType {
|
||||
#[cfg(windows)]
|
||||
@@ -333,8 +313,10 @@ impl SandboxManager {
|
||||
if !self.should_sandbox(permission_profile, pref, has_managed_network_requirements) {
|
||||
return SandboxType::None;
|
||||
}
|
||||
get_platform_sandbox(windows_sandbox_level != WindowsSandboxLevel::Disabled)
|
||||
.unwrap_or(SandboxType::None)
|
||||
if cfg!(windows) && windows_sandbox_type == SandboxType::WindowsMxc {
|
||||
return SandboxType::WindowsMxc;
|
||||
}
|
||||
get_platform_sandbox(windows_sandbox_type != SandboxType::None).unwrap_or(SandboxType::None)
|
||||
}
|
||||
|
||||
/// Returns whether the request needs a sandbox, independently of whether
|
||||
|
||||
@@ -31,7 +31,7 @@ fn danger_full_access_defaults_to_no_sandbox_without_network_requirements() {
|
||||
let sandbox = manager.select_initial(
|
||||
&PermissionProfile::Disabled,
|
||||
SandboxablePreference::Auto,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
SandboxType::None,
|
||||
/*has_managed_network_requirements*/ false,
|
||||
);
|
||||
assert_eq!(sandbox, SandboxType::None);
|
||||
@@ -45,7 +45,7 @@ fn danger_full_access_uses_platform_sandbox_with_network_requirements() {
|
||||
let sandbox = manager.select_initial(
|
||||
&PermissionProfile::Disabled,
|
||||
SandboxablePreference::Auto,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
SandboxType::None,
|
||||
/*has_managed_network_requirements*/ true,
|
||||
);
|
||||
assert_eq!(sandbox, expected);
|
||||
@@ -69,12 +69,29 @@ fn restricted_file_system_uses_platform_sandbox_without_managed_network() {
|
||||
let sandbox = manager.select_initial(
|
||||
&permissions,
|
||||
SandboxablePreference::Auto,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
SandboxType::None,
|
||||
/*has_managed_network_requirements*/ false,
|
||||
);
|
||||
assert_eq!(sandbox, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_mxc_only_overrides_the_windows_sandbox() {
|
||||
let manager = SandboxManager::new();
|
||||
let sandbox = manager.select_initial(
|
||||
&PermissionProfile::read_only(),
|
||||
SandboxablePreference::Auto,
|
||||
SandboxType::WindowsMxc,
|
||||
/*has_managed_network_requirements*/ false,
|
||||
);
|
||||
let expected = if cfg!(windows) {
|
||||
SandboxType::WindowsMxc
|
||||
} else {
|
||||
get_platform_sandbox(/*windows_sandbox_enabled*/ false).unwrap_or(SandboxType::None)
|
||||
};
|
||||
assert_eq!(sandbox, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unsandboxed_transform_preserves_foreign_cwd_and_unrestricted_file_system_policy() {
|
||||
let manager = SandboxManager::new();
|
||||
|
||||
@@ -104,7 +104,7 @@ impl App {
|
||||
windows_sandbox_ready(app_server).await;
|
||||
}
|
||||
let show_nux = std::mem::take(&mut self.windows_sandbox.prompt_after_trust)
|
||||
&& self.chat_widget.windows_sandbox_config.mode.is_none()
|
||||
&& !self.chat_widget.windows_sandbox_config.is_enabled()
|
||||
|| self.chat_widget.windows_sandbox_config.requires_elevated();
|
||||
if self.windows_sandbox_setup_is_local() {
|
||||
self.chat_widget.maybe_prompt_windows_sandbox_enable(
|
||||
|
||||
@@ -1666,6 +1666,7 @@ async fn windows_sandbox_config_refresh_uses_connected_server() -> Result<()> {
|
||||
Some(codex_app_server_protocol::WindowsSandboxSetupMode::Elevated);
|
||||
assert!(app.refresh_windows_sandbox_config(&server).await);
|
||||
let loaded_config = crate::windows_sandbox::WindowsSandboxConfig {
|
||||
mxc_selected: false,
|
||||
mode: Some(codex_app_server_protocol::WindowsSandboxSetupMode::Unelevated),
|
||||
requirements: Some(None),
|
||||
};
|
||||
|
||||
@@ -368,7 +368,7 @@ impl ChatWidget {
|
||||
// cannot be set up from this TUI's Windows account.
|
||||
return apply_actions();
|
||||
}
|
||||
if self.windows_sandbox_config.level() == WindowsSandboxLevel::Disabled {
|
||||
if !self.windows_sandbox_config.is_enabled() {
|
||||
let preset = preset.clone();
|
||||
return vec![Box::new(move |tx| {
|
||||
tx.send(AppEvent::OpenWindowsSandboxEnablePrompt {
|
||||
|
||||
@@ -64,7 +64,7 @@ impl ChatWidget {
|
||||
| crate::app::WindowsSandboxHost::Unknown
|
||||
) || (self.windows_sandbox_host != crate::app::WindowsSandboxHost::Remote
|
||||
&& reviewer == ApprovalsReviewer::User
|
||||
&& self.windows_sandbox_config.level() == WindowsSandboxLevel::Disabled))
|
||||
&& !self.windows_sandbox_config.is_enabled()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -272,8 +272,7 @@ impl ChatWidget {
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub(crate) fn maybe_prompt_windows_sandbox_enable(&mut self, show_now: bool) {
|
||||
let windows_sandbox_level = self.windows_sandbox_config.level();
|
||||
let setup_is_required = windows_sandbox_level == WindowsSandboxLevel::Disabled
|
||||
let setup_is_required = !self.windows_sandbox_config.is_enabled()
|
||||
|| self.elevated_windows_sandbox_setup_required();
|
||||
if show_now
|
||||
&& setup_is_required
|
||||
|
||||
@@ -18,8 +18,6 @@ use codex_app_server_protocol::RequestId;
|
||||
use codex_app_server_protocol::ServerNotification;
|
||||
use codex_exec_server::LOCAL_FS;
|
||||
use codex_git_utils::resolve_root_git_project_for_trust;
|
||||
#[cfg(target_os = "windows")]
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use crossterm::event::KeyCode;
|
||||
use crossterm::event::KeyEvent;
|
||||
use crossterm::event::KeyEventKind;
|
||||
@@ -149,7 +147,7 @@ impl OnboardingScreen {
|
||||
config.cwd.display().to_string(),
|
||||
)
|
||||
.await
|
||||
.is_ok_and(|state| state.level() == WindowsSandboxLevel::Disabled)
|
||||
.is_ok_and(|state| !state.is_enabled())
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ use uuid::Uuid;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub(crate) struct WindowsSandboxConfig {
|
||||
pub(crate) mxc_selected: bool,
|
||||
pub(crate) mode: Option<WindowsSandboxSetupMode>,
|
||||
// None means policy has not been loaded; a loaded null list allows both modes.
|
||||
pub(crate) requirements: Option<Option<Vec<WindowsSandboxSetupMode>>>,
|
||||
@@ -23,14 +24,22 @@ impl WindowsSandboxConfig {
|
||||
config: &ConfigReadResponse,
|
||||
requirements: ConfigRequirementsReadResponse,
|
||||
) -> Self {
|
||||
let configured_sandbox = config
|
||||
.config
|
||||
.additional
|
||||
.get("windows")
|
||||
.and_then(|windows| windows.get("sandbox"));
|
||||
let mxc_selected = configured_sandbox
|
||||
.and_then(|implementation| serde_json::from_value(implementation.clone()).ok())
|
||||
== Some(WindowsSandboxImplementation::Mxc);
|
||||
let mut state = Self {
|
||||
mode: config
|
||||
.config
|
||||
.additional
|
||||
.get("windows")
|
||||
.and_then(|windows| windows.get("sandbox"))
|
||||
mxc_selected,
|
||||
mode: configured_sandbox
|
||||
.and_then(|mode| serde_json::from_value(mode.clone()).ok())
|
||||
.or_else(|| {
|
||||
if mxc_selected {
|
||||
return None;
|
||||
}
|
||||
let features = config.config.additional.get("features")?;
|
||||
[
|
||||
(
|
||||
@@ -72,7 +81,8 @@ impl WindowsSandboxConfig {
|
||||
}),
|
||||
),
|
||||
};
|
||||
if let Some(Some(allowed)) = &state.requirements
|
||||
if !state.mxc_selected
|
||||
&& let Some(Some(allowed)) = &state.requirements
|
||||
&& !state.mode.is_some_and(|mode| allowed.contains(&mode))
|
||||
{
|
||||
// Managed requirements prefer elevated when the configured value is disallowed.
|
||||
@@ -86,6 +96,10 @@ impl WindowsSandboxConfig {
|
||||
state
|
||||
}
|
||||
|
||||
pub(crate) fn is_enabled(&self) -> bool {
|
||||
self.mxc_selected || self.mode.is_some()
|
||||
}
|
||||
|
||||
pub(crate) fn level(&self) -> WindowsSandboxLevel {
|
||||
match self.mode {
|
||||
Some(WindowsSandboxSetupMode::Elevated) => WindowsSandboxLevel::Elevated,
|
||||
|
||||
@@ -11,36 +11,45 @@ fn windows_sandbox_server_policy_controls_setup_choices() {
|
||||
Some("unelevated"),
|
||||
serde_json::Value::Null,
|
||||
Some(Unelevated),
|
||||
(true, true, false),
|
||||
(true, true, false, true),
|
||||
),
|
||||
(
|
||||
Some("unelevated"),
|
||||
json!(["elevated"]),
|
||||
Some(Elevated),
|
||||
(true, false, true),
|
||||
(true, false, true, true),
|
||||
),
|
||||
(
|
||||
None,
|
||||
json!(["unelevated", "elevated"]),
|
||||
Some(Elevated),
|
||||
(true, true, true),
|
||||
(true, true, true, true),
|
||||
),
|
||||
(
|
||||
None,
|
||||
json!(["unelevated"]),
|
||||
Some(Unelevated),
|
||||
(false, true, false),
|
||||
(false, true, false, true),
|
||||
),
|
||||
(Some("unelevated"), json!([]), None, (false, false, false)),
|
||||
(
|
||||
Some("unelevated"),
|
||||
json!(["mxc"]),
|
||||
json!([]),
|
||||
None,
|
||||
(false, false, false),
|
||||
(false, false, false, false),
|
||||
),
|
||||
(
|
||||
Some("mxc"),
|
||||
json!(["elevated"]),
|
||||
None,
|
||||
(true, false, false, true),
|
||||
),
|
||||
] {
|
||||
let config = serde_json::from_value(json!({
|
||||
"config": {"windows": {"sandbox": configured}}, "origins": {}
|
||||
"config": {
|
||||
"windows": {"sandbox": configured},
|
||||
"features": {"elevated_windows_sandbox": configured == Some("mxc")}
|
||||
},
|
||||
"origins": {}
|
||||
}))
|
||||
.unwrap();
|
||||
let requirements = serde_json::from_value(json!({
|
||||
@@ -53,7 +62,8 @@ fn windows_sandbox_server_policy_controls_setup_choices() {
|
||||
(
|
||||
state.allows(Elevated),
|
||||
state.allows(Unelevated),
|
||||
state.requires_elevated()
|
||||
state.requires_elevated(),
|
||||
state.is_enabled(),
|
||||
),
|
||||
expected
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ use anyhow::bail;
|
||||
use codex_cloud_config::cloud_config_bundle_loader_for_storage_without_cache;
|
||||
use codex_config::ConfigLoadOptions;
|
||||
use codex_config::ConfigRequirementsToml;
|
||||
use codex_config::types::WindowsSandboxModeToml;
|
||||
use codex_config::WindowsSandboxImplementationToml;
|
||||
use codex_core::config::bootstrap_auth_config;
|
||||
use codex_core::config::load_config_toml_with_layer_stack;
|
||||
use codex_windows_sandbox::WindowsSandboxProvisioningSettings;
|
||||
@@ -92,7 +92,7 @@ fn validate_requirements(
|
||||
.windows
|
||||
.as_ref()
|
||||
.and_then(|windows| windows.allowed_sandbox_implementations.as_ref())
|
||||
.is_some_and(|allowed| !allowed.contains(&WindowsSandboxModeToml::Elevated))
|
||||
.is_some_and(|allowed| !allowed.contains(&WindowsSandboxImplementationToml::Elevated))
|
||||
{
|
||||
bail!("managed policy does not permit the elevated Windows sandbox");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user