mirror of
https://github.com/openai/codex.git
synced 2026-11-08 08:37:30 +00:00
Wire the Windows MXC sandbox into command execution (#45176)
## What changed - Add explicit MXC backend selection and carry its identity through exec-server process reporting and sandbox violation classification. - Launch MXC through the Codex executable with the effective permission profile and command environment. - Reject exec-server MXC requests when native MXC is unavailable or when they request a TTY, an `arg0` override, or managed networking. Reject private desktop isolation during MXC preparation. - Allow an explicitly empty child environment and avoid exposing request payload values in launcher decode errors. ## Testing Add coverage for sandbox selection and unsupported-request rejection, plus Windows RPC tests for stdin writes and temporary-directory permissions derived from the command environment. Native MXC tests skip when MXC is unavailable. GitOrigin-RevId: 3626ff0f9ad7f9b812ce09b68c31ea9a5a9c72b1
This commit is contained in:
1 parent
dfaf451426
commit
c379459bba
37 files changed
+422
-85
No files matched your search
@@ -315,6 +315,7 @@ impl CommandExecRequestProcessor {
|
|||||||
&sandbox_cwd,
|
&sandbox_cwd,
|
||||||
windows_sandbox_workspace_roots.as_slice(),
|
windows_sandbox_workspace_roots.as_slice(),
|
||||||
&codex_linux_sandbox_exe,
|
&codex_linux_sandbox_exe,
|
||||||
|
&self.arg0_paths.codex_self_exe,
|
||||||
use_legacy_landlock,
|
use_legacy_landlock,
|
||||||
)
|
)
|
||||||
.map_err(|err| internal_error(format!("exec failed: {err}")))?;
|
.map_err(|err| internal_error(format!("exec failed: {err}")))?;
|
||||||
|
|||||||
@@ -4907,7 +4907,7 @@ impl ThreadRequestProcessor {
|
|||||||
serde_json::json!("unelevated"),
|
serde_json::json!("unelevated"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
WindowsSandboxLevel::Disabled => {}
|
WindowsSandboxLevel::Disabled | WindowsSandboxLevel::Mxc => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let request_overrides = if cli_overrides.is_empty() {
|
let request_overrides = if cli_overrides.is_empty() {
|
||||||
|
|||||||
@@ -222,7 +222,9 @@ fn determine_windows_sandbox_readiness_from_state(
|
|||||||
) -> WindowsSandboxReadinessResponse {
|
) -> WindowsSandboxReadinessResponse {
|
||||||
let status = match windows_sandbox_level {
|
let status = match windows_sandbox_level {
|
||||||
WindowsSandboxLevel::Disabled => WindowsSandboxReadiness::NotConfigured,
|
WindowsSandboxLevel::Disabled => WindowsSandboxReadiness::NotConfigured,
|
||||||
WindowsSandboxLevel::RestrictedToken => WindowsSandboxReadiness::Ready,
|
WindowsSandboxLevel::RestrictedToken | WindowsSandboxLevel::Mxc => {
|
||||||
|
WindowsSandboxReadiness::Ready
|
||||||
|
}
|
||||||
WindowsSandboxLevel::Elevated => {
|
WindowsSandboxLevel::Elevated => {
|
||||||
if sandbox_setup_is_complete {
|
if sandbox_setup_is_complete {
|
||||||
WindowsSandboxReadiness::Ready
|
WindowsSandboxReadiness::Ready
|
||||||
|
|||||||
@@ -3329,7 +3329,7 @@ impl Config {
|
|||||||
match WindowsSandboxLevel::from_features(&features) {
|
match WindowsSandboxLevel::from_features(&features) {
|
||||||
WindowsSandboxLevel::Elevated => Some(WindowsSandboxModeToml::Elevated),
|
WindowsSandboxLevel::Elevated => Some(WindowsSandboxModeToml::Elevated),
|
||||||
WindowsSandboxLevel::RestrictedToken => Some(WindowsSandboxModeToml::Unelevated),
|
WindowsSandboxLevel::RestrictedToken => Some(WindowsSandboxModeToml::Unelevated),
|
||||||
WindowsSandboxLevel::Disabled => None,
|
WindowsSandboxLevel::Disabled | WindowsSandboxLevel::Mxc => None,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
apply_requirement_constrained_value(
|
apply_requirement_constrained_value(
|
||||||
|
|||||||
@@ -300,6 +300,7 @@ pub async fn process_exec_tool_call(
|
|||||||
sandbox_cwd: &AbsolutePathBuf,
|
sandbox_cwd: &AbsolutePathBuf,
|
||||||
windows_sandbox_workspace_roots: &[AbsolutePathBuf],
|
windows_sandbox_workspace_roots: &[AbsolutePathBuf],
|
||||||
codex_linux_sandbox_exe: &Option<PathBuf>,
|
codex_linux_sandbox_exe: &Option<PathBuf>,
|
||||||
|
codex_self_exe: &Option<PathBuf>,
|
||||||
use_legacy_landlock: bool,
|
use_legacy_landlock: bool,
|
||||||
stdout_stream: Option<StdoutStream>,
|
stdout_stream: Option<StdoutStream>,
|
||||||
) -> Result<ExecToolCallOutput> {
|
) -> Result<ExecToolCallOutput> {
|
||||||
@@ -309,6 +310,7 @@ pub async fn process_exec_tool_call(
|
|||||||
sandbox_cwd,
|
sandbox_cwd,
|
||||||
windows_sandbox_workspace_roots,
|
windows_sandbox_workspace_roots,
|
||||||
codex_linux_sandbox_exe,
|
codex_linux_sandbox_exe,
|
||||||
|
codex_self_exe,
|
||||||
use_legacy_landlock,
|
use_legacy_landlock,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -324,6 +326,7 @@ pub fn build_exec_request(
|
|||||||
sandbox_cwd: &AbsolutePathBuf,
|
sandbox_cwd: &AbsolutePathBuf,
|
||||||
windows_sandbox_workspace_roots: &[AbsolutePathBuf],
|
windows_sandbox_workspace_roots: &[AbsolutePathBuf],
|
||||||
codex_linux_sandbox_exe: &Option<PathBuf>,
|
codex_linux_sandbox_exe: &Option<PathBuf>,
|
||||||
|
codex_self_exe: &Option<PathBuf>,
|
||||||
use_legacy_landlock: bool,
|
use_legacy_landlock: bool,
|
||||||
) -> Result<ExecRequest> {
|
) -> Result<ExecRequest> {
|
||||||
let ExecParams {
|
let ExecParams {
|
||||||
@@ -390,7 +393,11 @@ pub fn build_exec_request(
|
|||||||
environment_id: network_environment_id.as_deref(),
|
environment_id: network_environment_id.as_deref(),
|
||||||
network: network.as_ref(),
|
network: network.as_ref(),
|
||||||
sandbox_policy_cwd: &sandbox_policy_cwd_uri,
|
sandbox_policy_cwd: &sandbox_policy_cwd_uri,
|
||||||
codex_linux_sandbox_exe: codex_linux_sandbox_exe.as_deref(),
|
sandbox_exe: if cfg!(windows) {
|
||||||
|
codex_self_exe.as_deref()
|
||||||
|
} else {
|
||||||
|
codex_linux_sandbox_exe.as_deref()
|
||||||
|
},
|
||||||
use_legacy_landlock,
|
use_legacy_landlock,
|
||||||
windows_sandbox_level,
|
windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop,
|
windows_sandbox_private_desktop,
|
||||||
|
|||||||
@@ -515,6 +515,7 @@ async fn process_exec_tool_call_preserves_full_buffer_capture_policy(
|
|||||||
&cwd,
|
&cwd,
|
||||||
std::slice::from_ref(&cwd),
|
std::slice::from_ref(&cwd),
|
||||||
&None,
|
&None,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
/*use_legacy_landlock*/ false,
|
/*use_legacy_landlock*/ false,
|
||||||
/*stdout_stream*/ None,
|
/*stdout_stream*/ None,
|
||||||
)
|
)
|
||||||
@@ -1219,6 +1220,7 @@ fn build_exec_request_preserves_windows_workspace_roots() -> Result<()> {
|
|||||||
&cwd,
|
&cwd,
|
||||||
workspace_roots.as_slice(),
|
workspace_roots.as_slice(),
|
||||||
&None,
|
&None,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
/*use_legacy_landlock*/ false,
|
/*use_legacy_landlock*/ false,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -1338,6 +1340,7 @@ async fn process_exec_tool_call_respects_cancellation_token() -> Result<()> {
|
|||||||
&cwd,
|
&cwd,
|
||||||
std::slice::from_ref(&cwd),
|
std::slice::from_ref(&cwd),
|
||||||
&None,
|
&None,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
/*use_legacy_landlock*/ false,
|
/*use_legacy_landlock*/ false,
|
||||||
/*stdout_stream*/ None,
|
/*stdout_stream*/ None,
|
||||||
),
|
),
|
||||||
@@ -1419,6 +1422,7 @@ while :; do sleep 1; done"#
|
|||||||
&cwd,
|
&cwd,
|
||||||
std::slice::from_ref(&cwd),
|
std::slice::from_ref(&cwd),
|
||||||
&None,
|
&None,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
/*use_legacy_landlock*/ false,
|
/*use_legacy_landlock*/ false,
|
||||||
/*stdout_stream*/ None,
|
/*stdout_stream*/ None,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -77,9 +77,12 @@ fn permission_profile_sandbox_tag(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cfg!(target_os = "windows") && matches!(windows_sandbox_level, WindowsSandboxLevel::Elevated)
|
if cfg!(target_os = "windows") {
|
||||||
{
|
match windows_sandbox_level {
|
||||||
return "windows_elevated";
|
WindowsSandboxLevel::Mxc => return "windows_mxc",
|
||||||
|
WindowsSandboxLevel::Elevated => return "windows_elevated",
|
||||||
|
WindowsSandboxLevel::Disabled | WindowsSandboxLevel::RestrictedToken => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get_platform_sandbox(windows_sandbox_level != WindowsSandboxLevel::Disabled)
|
get_platform_sandbox(windows_sandbox_level != WindowsSandboxLevel::Disabled)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ pub(crate) struct ShellSnapshotSandbox {
|
|||||||
enforce_managed_network: bool,
|
enforce_managed_network: bool,
|
||||||
sandbox_policy_cwd: PathUri,
|
sandbox_policy_cwd: PathUri,
|
||||||
workspace_roots: Vec<PathUri>,
|
workspace_roots: Vec<PathUri>,
|
||||||
codex_linux_sandbox_exe: Option<PathBuf>,
|
sandbox_exe: Option<PathBuf>,
|
||||||
use_legacy_landlock: bool,
|
use_legacy_landlock: bool,
|
||||||
windows_sandbox_level: WindowsSandboxLevel,
|
windows_sandbox_level: WindowsSandboxLevel,
|
||||||
windows_sandbox_private_desktop: bool,
|
windows_sandbox_private_desktop: bool,
|
||||||
@@ -84,7 +84,7 @@ impl ShellSnapshotSandbox {
|
|||||||
enforce_managed_network: attempt.enforce_managed_network,
|
enforce_managed_network: attempt.enforce_managed_network,
|
||||||
sandbox_policy_cwd: attempt.sandbox_cwd.clone(),
|
sandbox_policy_cwd: attempt.sandbox_cwd.clone(),
|
||||||
workspace_roots: attempt.workspace_roots.to_vec(),
|
workspace_roots: attempt.workspace_roots.to_vec(),
|
||||||
codex_linux_sandbox_exe: attempt.codex_linux_sandbox_exe.cloned(),
|
sandbox_exe: attempt.sandbox_exe.cloned(),
|
||||||
use_legacy_landlock: attempt.use_legacy_landlock,
|
use_legacy_landlock: attempt.use_legacy_landlock,
|
||||||
windows_sandbox_level: attempt.windows_sandbox_level,
|
windows_sandbox_level: attempt.windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop: attempt.windows_sandbox_private_desktop,
|
windows_sandbox_private_desktop: attempt.windows_sandbox_private_desktop,
|
||||||
@@ -103,7 +103,7 @@ impl ShellSnapshotSandbox {
|
|||||||
self.enforce_managed_network,
|
self.enforce_managed_network,
|
||||||
&self.sandbox_policy_cwd,
|
&self.sandbox_policy_cwd,
|
||||||
&self.workspace_roots,
|
&self.workspace_roots,
|
||||||
&self.codex_linux_sandbox_exe,
|
&self.sandbox_exe,
|
||||||
self.use_legacy_landlock,
|
self.use_legacy_landlock,
|
||||||
self.windows_sandbox_level,
|
self.windows_sandbox_level,
|
||||||
self.windows_sandbox_private_desktop,
|
self.windows_sandbox_private_desktop,
|
||||||
@@ -172,7 +172,7 @@ impl ShellSnapshotSandbox {
|
|||||||
environment_id: Some(&self.environment_id),
|
environment_id: Some(&self.environment_id),
|
||||||
network: self.network.as_ref(),
|
network: self.network.as_ref(),
|
||||||
sandbox_policy_cwd: &self.sandbox_policy_cwd,
|
sandbox_policy_cwd: &self.sandbox_policy_cwd,
|
||||||
codex_linux_sandbox_exe: self.codex_linux_sandbox_exe.as_deref(),
|
sandbox_exe: self.sandbox_exe.as_deref(),
|
||||||
use_legacy_landlock: self.use_legacy_landlock,
|
use_legacy_landlock: self.use_legacy_landlock,
|
||||||
windows_sandbox_level: self.windows_sandbox_level,
|
windows_sandbox_level: self.windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ async fn snapshot_failure_omits_credentials_and_stops_descendants(
|
|||||||
manager: &manager,
|
manager: &manager,
|
||||||
sandbox_cwd: &cwd_uri,
|
sandbox_cwd: &cwd_uri,
|
||||||
workspace_roots: std::slice::from_ref(&cwd_uri),
|
workspace_roots: std::slice::from_ref(&cwd_uri),
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
|
|||||||
@@ -416,7 +416,7 @@ async fn inactive_profiles_keep_snapshots_but_active_brokers_require_sandbox() -
|
|||||||
manager: &manager,
|
manager: &manager,
|
||||||
sandbox_cwd: &cwd_uri,
|
sandbox_cwd: &cwd_uri,
|
||||||
workspace_roots: std::slice::from_ref(&cwd_uri),
|
workspace_roots: std::slice::from_ref(&cwd_uri),
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ impl ToolOrchestrator {
|
|||||||
manager: attempt.manager,
|
manager: attempt.manager,
|
||||||
sandbox_cwd: attempt.sandbox_cwd,
|
sandbox_cwd: attempt.sandbox_cwd,
|
||||||
workspace_roots: attempt.workspace_roots,
|
workspace_roots: attempt.workspace_roots,
|
||||||
codex_linux_sandbox_exe: attempt.codex_linux_sandbox_exe,
|
sandbox_exe: attempt.sandbox_exe,
|
||||||
use_legacy_landlock: attempt.use_legacy_landlock,
|
use_legacy_landlock: attempt.use_legacy_landlock,
|
||||||
windows_sandbox_level: attempt.windows_sandbox_level,
|
windows_sandbox_level: attempt.windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop: attempt.windows_sandbox_private_desktop,
|
windows_sandbox_private_desktop: attempt.windows_sandbox_private_desktop,
|
||||||
@@ -290,6 +290,11 @@ impl ToolOrchestrator {
|
|||||||
.sandbox_cwd(req)
|
.sandbox_cwd(req)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| environment.cwd().clone());
|
.unwrap_or_else(|| environment.cwd().clone());
|
||||||
|
let codex_sandbox_exe = if cfg!(windows) {
|
||||||
|
turn_ctx.config.codex_self_exe.as_ref()
|
||||||
|
} else {
|
||||||
|
turn_ctx.config.codex_linux_sandbox_exe.as_ref()
|
||||||
|
};
|
||||||
let initial_attempt = SandboxAttempt {
|
let initial_attempt = SandboxAttempt {
|
||||||
sandbox: initial_sandbox,
|
sandbox: initial_sandbox,
|
||||||
sandbox_requested,
|
sandbox_requested,
|
||||||
@@ -299,7 +304,7 @@ impl ToolOrchestrator {
|
|||||||
manager: &sandbox_manager,
|
manager: &sandbox_manager,
|
||||||
sandbox_cwd: &sandbox_policy_cwd,
|
sandbox_cwd: &sandbox_policy_cwd,
|
||||||
workspace_roots,
|
workspace_roots,
|
||||||
codex_linux_sandbox_exe: turn_ctx.config.codex_linux_sandbox_exe.as_ref(),
|
sandbox_exe: codex_sandbox_exe,
|
||||||
use_legacy_landlock: sandbox_config.use_legacy_landlock,
|
use_legacy_landlock: sandbox_config.use_legacy_landlock,
|
||||||
windows_sandbox_level: sandbox_config.windows_sandbox_level,
|
windows_sandbox_level: sandbox_config.windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop: sandbox_config.windows_sandbox_private_desktop,
|
windows_sandbox_private_desktop: sandbox_config.windows_sandbox_private_desktop,
|
||||||
@@ -459,10 +464,10 @@ impl ToolOrchestrator {
|
|||||||
} else {
|
} else {
|
||||||
SandboxType::None
|
SandboxType::None
|
||||||
};
|
};
|
||||||
let retry_codex_linux_sandbox_exe = if unsandboxed_allowed {
|
let retry_sandbox_exe = if unsandboxed_allowed {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
turn_ctx.config.codex_linux_sandbox_exe.as_ref()
|
codex_sandbox_exe
|
||||||
};
|
};
|
||||||
let retry_attempt = SandboxAttempt {
|
let retry_attempt = SandboxAttempt {
|
||||||
sandbox: retry_sandbox,
|
sandbox: retry_sandbox,
|
||||||
@@ -473,7 +478,7 @@ impl ToolOrchestrator {
|
|||||||
manager: &sandbox_manager,
|
manager: &sandbox_manager,
|
||||||
sandbox_cwd: &sandbox_policy_cwd,
|
sandbox_cwd: &sandbox_policy_cwd,
|
||||||
workspace_roots,
|
workspace_roots,
|
||||||
codex_linux_sandbox_exe: retry_codex_linux_sandbox_exe,
|
sandbox_exe: retry_sandbox_exe,
|
||||||
use_legacy_landlock: sandbox_config.use_legacy_landlock,
|
use_legacy_landlock: sandbox_config.use_legacy_landlock,
|
||||||
windows_sandbox_level: sandbox_config.windows_sandbox_level,
|
windows_sandbox_level: sandbox_config.windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop: sandbox_config.windows_sandbox_private_desktop,
|
windows_sandbox_private_desktop: sandbox_config.windows_sandbox_private_desktop,
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ async fn file_system_sandbox_context_preserves_executor_workspace_permissions()
|
|||||||
manager: &manager,
|
manager: &manager,
|
||||||
sandbox_cwd: &sandbox_policy_cwd,
|
sandbox_cwd: &sandbox_policy_cwd,
|
||||||
workspace_roots: std::slice::from_ref(&sandbox_policy_cwd),
|
workspace_roots: std::slice::from_ref(&sandbox_policy_cwd),
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: true,
|
use_legacy_landlock: true,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
|
windows_sandbox_level: WindowsSandboxLevel::RestrictedToken,
|
||||||
windows_sandbox_private_desktop: true,
|
windows_sandbox_private_desktop: true,
|
||||||
@@ -315,7 +315,7 @@ async fn file_system_sandbox_context_respects_sandbox_request() {
|
|||||||
manager: &manager,
|
manager: &manager,
|
||||||
sandbox_cwd: &sandbox_policy_cwd,
|
sandbox_cwd: &sandbox_policy_cwd,
|
||||||
workspace_roots: std::slice::from_ref(&sandbox_policy_cwd),
|
workspace_roots: std::slice::from_ref(&sandbox_policy_cwd),
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ async fn explicit_escalation_prepares_exec_without_managed_network() -> anyhow::
|
|||||||
manager: &manager,
|
manager: &manager,
|
||||||
sandbox_cwd: &sandbox_policy_cwd,
|
sandbox_cwd: &sandbox_policy_cwd,
|
||||||
workspace_roots: std::slice::from_ref(&sandbox_policy_cwd),
|
workspace_roots: std::slice::from_ref(&sandbox_policy_cwd),
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
|
|||||||
@@ -789,7 +789,7 @@ impl CoreShellCommandExecutor {
|
|||||||
environment_id: self.network_environment_id.as_deref(),
|
environment_id: self.network_environment_id.as_deref(),
|
||||||
network: self.network.as_ref(),
|
network: self.network.as_ref(),
|
||||||
sandbox_policy_cwd: &sandbox_policy_cwd,
|
sandbox_policy_cwd: &sandbox_policy_cwd,
|
||||||
codex_linux_sandbox_exe: self.codex_linux_sandbox_exe.as_deref(),
|
sandbox_exe: self.codex_linux_sandbox_exe.as_deref(),
|
||||||
use_legacy_landlock: self.use_legacy_landlock,
|
use_legacy_landlock: self.use_legacy_landlock,
|
||||||
windows_sandbox_level: self.windows_sandbox_level,
|
windows_sandbox_level: self.windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ pub(crate) struct SandboxAttempt<'a> {
|
|||||||
pub(crate) manager: &'a SandboxManager,
|
pub(crate) manager: &'a SandboxManager,
|
||||||
pub(crate) sandbox_cwd: &'a PathUri,
|
pub(crate) sandbox_cwd: &'a PathUri,
|
||||||
pub(crate) workspace_roots: &'a [PathUri],
|
pub(crate) workspace_roots: &'a [PathUri],
|
||||||
pub codex_linux_sandbox_exe: Option<&'a std::path::PathBuf>,
|
pub sandbox_exe: Option<&'a std::path::PathBuf>,
|
||||||
// TODO(anp): Reconcile these attempt settings with TurnEnvironment::sandbox_context
|
// TODO(anp): Reconcile these attempt settings with TurnEnvironment::sandbox_context
|
||||||
// so process execution and patch writes honor the selected environment's backend.
|
// so process execution and patch writes honor the selected environment's backend.
|
||||||
pub use_legacy_landlock: bool,
|
pub use_legacy_landlock: bool,
|
||||||
@@ -459,9 +459,7 @@ impl<'a> SandboxAttempt<'a> {
|
|||||||
environment_id,
|
environment_id,
|
||||||
network,
|
network,
|
||||||
sandbox_policy_cwd: self.sandbox_cwd,
|
sandbox_policy_cwd: self.sandbox_cwd,
|
||||||
codex_linux_sandbox_exe: self
|
sandbox_exe: self.sandbox_exe.map(std::path::PathBuf::as_path),
|
||||||
.codex_linux_sandbox_exe
|
|
||||||
.map(std::path::PathBuf::as_path),
|
|
||||||
use_legacy_landlock: self.use_legacy_landlock,
|
use_legacy_landlock: self.use_legacy_landlock,
|
||||||
windows_sandbox_level: self.windows_sandbox_level,
|
windows_sandbox_level: self.windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
||||||
@@ -496,7 +494,7 @@ impl<'a> SandboxAttempt<'a> {
|
|||||||
environment_id: None,
|
environment_id: None,
|
||||||
network: None,
|
network: None,
|
||||||
sandbox_policy_cwd: self.sandbox_cwd,
|
sandbox_policy_cwd: self.sandbox_cwd,
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: self.use_legacy_landlock,
|
use_legacy_landlock: self.use_legacy_landlock,
|
||||||
windows_sandbox_level: self.windows_sandbox_level,
|
windows_sandbox_level: self.windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
windows_sandbox_private_desktop: self.windows_sandbox_private_desktop,
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ fn windows_sandbox_env_preserves_denied_reads_or_rejects_unsupported_backend() {
|
|||||||
manager: &manager,
|
manager: &manager,
|
||||||
sandbox_cwd: &cwd_uri,
|
sandbox_cwd: &cwd_uri,
|
||||||
workspace_roots: std::slice::from_ref(&cwd_uri),
|
workspace_roots: std::slice::from_ref(&cwd_uri),
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Elevated,
|
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Elevated,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
@@ -326,7 +326,7 @@ fn exec_server_env_keeps_command_native_and_carries_sandbox_context() {
|
|||||||
manager: &manager,
|
manager: &manager,
|
||||||
sandbox_cwd: &cwd_uri,
|
sandbox_cwd: &cwd_uri,
|
||||||
workspace_roots: std::slice::from_ref(&cwd_uri),
|
workspace_roots: std::slice::from_ref(&cwd_uri),
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ where
|
|||||||
&cwd,
|
&cwd,
|
||||||
std::slice::from_ref(&cwd),
|
std::slice::from_ref(&cwd),
|
||||||
&None,
|
&None,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
/*use_legacy_landlock*/ false,
|
/*use_legacy_landlock*/ false,
|
||||||
/*stdout_stream*/ None,
|
/*stdout_stream*/ None,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -375,6 +375,7 @@ async fn windows_restricted_token_rejects_exact_and_glob_deny_read_policy() -> a
|
|||||||
&cwd,
|
&cwd,
|
||||||
std::slice::from_ref(&cwd),
|
std::slice::from_ref(&cwd),
|
||||||
&None,
|
&None,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
/*use_legacy_landlock*/ false,
|
/*use_legacy_landlock*/ false,
|
||||||
/*stdout_stream*/ None,
|
/*stdout_stream*/ None,
|
||||||
)
|
)
|
||||||
@@ -424,6 +425,7 @@ async fn windows_elevated_does_not_create_missing_workspace_metadata() -> anyhow
|
|||||||
&cwd,
|
&cwd,
|
||||||
std::slice::from_ref(&cwd),
|
std::slice::from_ref(&cwd),
|
||||||
&None,
|
&None,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
/*use_legacy_landlock*/ false,
|
/*use_legacy_landlock*/ false,
|
||||||
/*stdout_stream*/ None,
|
/*stdout_stream*/ None,
|
||||||
)
|
)
|
||||||
@@ -603,6 +605,7 @@ async fn windows_elevated_enforces_deny_read_and_protects_setup_marker() -> anyh
|
|||||||
&cwd,
|
&cwd,
|
||||||
std::slice::from_ref(&cwd),
|
std::slice::from_ref(&cwd),
|
||||||
&None,
|
&None,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
/*use_legacy_landlock*/ false,
|
/*use_legacy_landlock*/ false,
|
||||||
/*stdout_stream*/ None,
|
/*stdout_stream*/ None,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -152,6 +152,9 @@ pub struct EnvironmentCapabilities {
|
|||||||
/// Whether shell state can be cached and restored entirely inside the executor.
|
/// Whether shell state can be cached and restored entirely inside the executor.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub shell_snapshot_v2: bool,
|
pub shell_snapshot_v2: bool,
|
||||||
|
/// Whether requests may explicitly select the MXC Windows sandbox backend.
|
||||||
|
#[serde(default)]
|
||||||
|
pub windows_mxc: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Status returned by an initialized exec-server connection.
|
/// Status returned by an initialized exec-server connection.
|
||||||
@@ -243,6 +246,7 @@ impl EnvironmentInfo {
|
|||||||
http_header_env_vars: true,
|
http_header_env_vars: true,
|
||||||
sandboxed_file_streaming: true,
|
sandboxed_file_streaming: true,
|
||||||
shell_snapshot_v2: cfg!(unix),
|
shell_snapshot_v2: cfg!(unix),
|
||||||
|
windows_mxc: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,6 +362,7 @@ pub enum ProcessSandboxType {
|
|||||||
MacosSeatbelt,
|
MacosSeatbelt,
|
||||||
LinuxSeccomp,
|
LinuxSeccomp,
|
||||||
WindowsRestrictedToken,
|
WindowsRestrictedToken,
|
||||||
|
WindowsMxc,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
@@ -1057,6 +1062,7 @@ mod tests {
|
|||||||
http_header_env_vars: false,
|
http_header_env_vars: false,
|
||||||
sandboxed_file_streaming: false,
|
sandboxed_file_streaming: false,
|
||||||
shell_snapshot_v2: false,
|
shell_snapshot_v2: false,
|
||||||
|
windows_mxc: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1078,6 +1084,7 @@ mod tests {
|
|||||||
"httpHeaderEnvVars": false,
|
"httpHeaderEnvVars": false,
|
||||||
"sandboxedFileStreaming": false,
|
"sandboxedFileStreaming": false,
|
||||||
"shellSnapshotV2": false,
|
"shellSnapshotV2": false,
|
||||||
|
"windowsMxc": false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
let info: EnvironmentInfo = serde_json::from_value(expected.clone())
|
let info: EnvironmentInfo = serde_json::from_value(expected.clone())
|
||||||
|
|||||||
@@ -176,7 +176,11 @@ impl FileSystemSandboxRunner {
|
|||||||
environment_id: None,
|
environment_id: None,
|
||||||
network: None,
|
network: None,
|
||||||
sandbox_policy_cwd: &cwd.uri,
|
sandbox_policy_cwd: &cwd.uri,
|
||||||
codex_linux_sandbox_exe: self.runtime_paths.codex_linux_sandbox_exe.as_deref(),
|
sandbox_exe: if cfg!(windows) {
|
||||||
|
Some(self.runtime_paths.codex_self_exe.as_path())
|
||||||
|
} else {
|
||||||
|
self.runtime_paths.codex_linux_sandbox_exe.as_deref()
|
||||||
|
},
|
||||||
use_legacy_landlock: sandbox_context.use_legacy_landlock,
|
use_legacy_landlock: sandbox_context.use_legacy_landlock,
|
||||||
windows_sandbox_level: sandbox_context.windows_sandbox_level,
|
windows_sandbox_level: sandbox_context.windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop: sandbox_context
|
windows_sandbox_private_desktop: sandbox_context
|
||||||
|
|||||||
@@ -396,6 +396,7 @@ impl LocalProcess {
|
|||||||
SandboxType::MacosSeatbelt => Some(ProcessSandboxType::MacosSeatbelt),
|
SandboxType::MacosSeatbelt => Some(ProcessSandboxType::MacosSeatbelt),
|
||||||
SandboxType::LinuxSeccomp => Some(ProcessSandboxType::LinuxSeccomp),
|
SandboxType::LinuxSeccomp => Some(ProcessSandboxType::LinuxSeccomp),
|
||||||
SandboxType::WindowsRestrictedToken => Some(ProcessSandboxType::WindowsRestrictedToken),
|
SandboxType::WindowsRestrictedToken => Some(ProcessSandboxType::WindowsRestrictedToken),
|
||||||
|
SandboxType::WindowsMxc => Some(ProcessSandboxType::WindowsMxc),
|
||||||
};
|
};
|
||||||
|
|
||||||
let start = Arc::new(ProcessStart);
|
let start = Arc::new(ProcessStart);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ pub(crate) fn sandbox_type_from_protocol(
|
|||||||
Some(ProcessSandboxType::WindowsRestrictedToken) => {
|
Some(ProcessSandboxType::WindowsRestrictedToken) => {
|
||||||
Some(SandboxType::WindowsRestrictedToken)
|
Some(SandboxType::WindowsRestrictedToken)
|
||||||
}
|
}
|
||||||
|
Some(ProcessSandboxType::WindowsMxc) => Some(SandboxType::WindowsMxc),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -84,6 +84,28 @@ pub(crate) async fn prepare_exec_request_with_telemetry(
|
|||||||
network_policy_audit_observer: Option<NetworkPolicyAuditObserver>,
|
network_policy_audit_observer: Option<NetworkPolicyAuditObserver>,
|
||||||
telemetry: &ProcessTelemetry,
|
telemetry: &ProcessTelemetry,
|
||||||
) -> Result<PreparedExecRequest, JSONRPCErrorError> {
|
) -> Result<PreparedExecRequest, JSONRPCErrorError> {
|
||||||
|
if let Some(sandbox) = params.sandbox.as_ref()
|
||||||
|
&& sandbox.windows_sandbox_level == codex_protocol::config_types::WindowsSandboxLevel::Mxc
|
||||||
|
{
|
||||||
|
if params.tty || params.arg0.is_some() {
|
||||||
|
return Err(invalid_params(
|
||||||
|
"MXC currently supports ordinary pipe launches only".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if params.enforce_managed_network
|
||||||
|
|| params.managed_network.is_some()
|
||||||
|
|| params.network_proxy.is_some()
|
||||||
|
{
|
||||||
|
return Err(invalid_params(
|
||||||
|
"MXC managed networking is not supported yet".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if !codex_sandboxing::windows_mxc_available() {
|
||||||
|
return Err(invalid_params(
|
||||||
|
"native MXC is unavailable on this executor".to_owned(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
let mut env = env;
|
let mut env = env;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@@ -241,7 +263,11 @@ pub(crate) async fn prepare_exec_request_with_telemetry(
|
|||||||
environment_id: None,
|
environment_id: None,
|
||||||
network: None,
|
network: None,
|
||||||
sandbox_policy_cwd,
|
sandbox_policy_cwd,
|
||||||
codex_linux_sandbox_exe: runtime_paths.codex_linux_sandbox_exe.as_deref(),
|
sandbox_exe: if cfg!(windows) {
|
||||||
|
Some(runtime_paths.codex_self_exe.as_path())
|
||||||
|
} else {
|
||||||
|
runtime_paths.codex_linux_sandbox_exe.as_deref()
|
||||||
|
},
|
||||||
use_legacy_landlock: sandbox_context.use_legacy_landlock,
|
use_legacy_landlock: sandbox_context.use_legacy_landlock,
|
||||||
windows_sandbox_level: sandbox_context.windows_sandbox_level,
|
windows_sandbox_level: sandbox_context.windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop: sandbox_context.windows_sandbox_private_desktop,
|
windows_sandbox_private_desktop: sandbox_context.windows_sandbox_private_desktop,
|
||||||
|
|||||||
@@ -129,6 +129,33 @@ async fn sandbox_request_wraps_native_argv_on_executor() {
|
|||||||
prepared.command.first().map(String::as_str),
|
prepared.command.first().map(String::as_str),
|
||||||
Some("/usr/bin/sandbox-exec")
|
Some("/usr/bin/sandbox-exec")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut params = params;
|
||||||
|
params.sandbox.as_mut().unwrap().windows_sandbox_level =
|
||||||
|
codex_protocol::config_types::WindowsSandboxLevel::Mxc;
|
||||||
|
for (tty, managed, message) in [
|
||||||
|
(false, false, "native MXC is unavailable on this executor"),
|
||||||
|
(
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
"MXC currently supports ordinary pipe launches only",
|
||||||
|
),
|
||||||
|
(false, true, "MXC managed networking is not supported yet"),
|
||||||
|
] {
|
||||||
|
params.tty = tty;
|
||||||
|
params.enforce_managed_network = managed;
|
||||||
|
let error = prepare_exec_request(
|
||||||
|
¶ms,
|
||||||
|
HashMap::new(),
|
||||||
|
Some(&runtime_paths),
|
||||||
|
/*network_policy_decider*/ None,
|
||||||
|
/*network_policy_audit_observer*/ None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.expect("unsupported MXC must fail closed");
|
||||||
|
assert_eq!(error.message, message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ pub static TEST_BINARY_DISPATCH_GUARD: Option<TestBinaryDispatchGuard> = {
|
|||||||
if argv1 == Some(CODEX_WINDOWS_SANDBOX_ARG1) {
|
if argv1 == Some(CODEX_WINDOWS_SANDBOX_ARG1) {
|
||||||
return TestBinaryDispatchMode::DispatchArg0Only;
|
return TestBinaryDispatchMode::DispatchArg0Only;
|
||||||
}
|
}
|
||||||
|
if argv1 == Some(codex_sandboxing::CODEX_WINDOWS_MXC_ARG1) {
|
||||||
|
return TestBinaryDispatchMode::DispatchArg0Only;
|
||||||
|
}
|
||||||
if exe_name == CODEX_LINUX_SANDBOX_ARG0 {
|
if exe_name == CODEX_LINUX_SANDBOX_ARG0 {
|
||||||
return TestBinaryDispatchMode::DispatchArg0Only;
|
return TestBinaryDispatchMode::DispatchArg0Only;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
mod common;
|
mod common;
|
||||||
|
#[path = "exec_process/windows_sandbox.rs"]
|
||||||
|
mod windows_sandbox;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
@@ -102,6 +104,10 @@ async fn create_process_context(use_remote: bool) -> Result<ProcessContext> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn selected_windows_sandbox_available(windows_sandbox_level: WindowsSandboxLevel) -> bool {
|
||||||
|
windows_sandbox_level != WindowsSandboxLevel::Mxc || codex_sandboxing::windows_mxc_available()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn codex_home_symlink_opt_out_respects_host_config_and_scope() -> Result<()> {
|
async fn codex_home_symlink_opt_out_respects_host_config_and_scope() -> Result<()> {
|
||||||
@@ -914,13 +920,20 @@ async fn collect_process_output_from_reads(
|
|||||||
|
|
||||||
async fn collect_process_output_from_events(
|
async fn collect_process_output_from_events(
|
||||||
session: Arc<dyn ExecProcess>,
|
session: Arc<dyn ExecProcess>,
|
||||||
|
) -> Result<(String, String, Option<i32>, bool)> {
|
||||||
|
collect_process_output_from_events_with_timeout(session, Duration::from_secs(2)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn collect_process_output_from_events_with_timeout(
|
||||||
|
session: Arc<dyn ExecProcess>,
|
||||||
|
event_timeout: Duration,
|
||||||
) -> Result<(String, String, Option<i32>, bool)> {
|
) -> Result<(String, String, Option<i32>, bool)> {
|
||||||
let mut events = session.subscribe_events();
|
let mut events = session.subscribe_events();
|
||||||
let mut stdout = String::new();
|
let mut stdout = String::new();
|
||||||
let mut stderr = String::new();
|
let mut stderr = String::new();
|
||||||
let mut exit_code = None;
|
let mut exit_code = None;
|
||||||
loop {
|
loop {
|
||||||
match timeout(Duration::from_secs(2), events.recv()).await?? {
|
match timeout(event_timeout, events.recv()).await?? {
|
||||||
ExecProcessEvent::Output(chunk) => match chunk.stream {
|
ExecProcessEvent::Output(chunk) => match chunk.stream {
|
||||||
ExecOutputStream::Stdout | ExecOutputStream::Pty => {
|
ExecOutputStream::Stdout | ExecOutputStream::Pty => {
|
||||||
stdout.push_str(&String::from_utf8_lossy(&chunk.chunk.into_inner()));
|
stdout.push_str(&String::from_utf8_lossy(&chunk.chunk.into_inner()));
|
||||||
@@ -1281,7 +1294,14 @@ async fn assert_exec_process_write_then_read_without_tty(use_remote: bool) -> Re
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn assert_remote_windows_sandbox_process_write() -> Result<()> {
|
async fn assert_remote_windows_sandbox_process_write(
|
||||||
|
windows_sandbox_level: WindowsSandboxLevel,
|
||||||
|
expected_sandbox_type: codex_sandboxing::SandboxType,
|
||||||
|
) -> Result<()> {
|
||||||
|
if !selected_windows_sandbox_available(windows_sandbox_level) {
|
||||||
|
eprintln!("skipping MXC enforcement test: native MXC is unavailable on this host");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
let context = create_process_context(/*use_remote*/ true).await?;
|
let context = create_process_context(/*use_remote*/ true).await?;
|
||||||
let workspace = TempDir::new()?;
|
let workspace = TempDir::new()?;
|
||||||
let blocked_file = workspace.path().join("blocked.txt");
|
let blocked_file = workspace.path().join("blocked.txt");
|
||||||
@@ -1290,7 +1310,7 @@ async fn assert_remote_windows_sandbox_process_write() -> Result<()> {
|
|||||||
SandboxPolicy::new_read_only_policy(),
|
SandboxPolicy::new_read_only_policy(),
|
||||||
cwd.clone(),
|
cwd.clone(),
|
||||||
)?;
|
)?;
|
||||||
sandbox.windows_sandbox_level = WindowsSandboxLevel::RestrictedToken;
|
sandbox.windows_sandbox_level = windows_sandbox_level;
|
||||||
|
|
||||||
let session = match context
|
let session = match context
|
||||||
.backend
|
.backend
|
||||||
@@ -1325,6 +1345,7 @@ async fn assert_remote_windows_sandbox_process_write() -> Result<()> {
|
|||||||
Ok(session) => session,
|
Ok(session) => session,
|
||||||
Err(err) => return Err(err.into()),
|
Err(err) => return Err(err.into()),
|
||||||
};
|
};
|
||||||
|
assert_eq!(session.sandbox_type, Some(expected_sandbox_type));
|
||||||
|
|
||||||
let write_response = session.process.write(b"hello\n".to_vec()).await?;
|
let write_response = session.process.write(b"hello\n".to_vec()).await?;
|
||||||
assert_eq!(write_response.status, WriteStatus::Accepted);
|
assert_eq!(write_response.status, WriteStatus::Accepted);
|
||||||
@@ -1743,11 +1764,24 @@ async fn exec_process_write_then_read_without_tty(use_remote: bool) -> Result<()
|
|||||||
assert_exec_process_write_then_read_without_tty(use_remote).await
|
assert_exec_process_write_then_read_without_tty(use_remote).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[test_case(
|
||||||
|
WindowsSandboxLevel::RestrictedToken,
|
||||||
|
codex_sandboxing::SandboxType::WindowsRestrictedToken;
|
||||||
|
"restricted_token"
|
||||||
|
)]
|
||||||
|
#[test_case(
|
||||||
|
WindowsSandboxLevel::Mxc,
|
||||||
|
codex_sandboxing::SandboxType::WindowsMxc;
|
||||||
|
"mxc"
|
||||||
|
)]
|
||||||
#[cfg_attr(not(windows), ignore = "Windows-only exec-server sandbox process test")]
|
#[cfg_attr(not(windows), ignore = "Windows-only exec-server sandbox process test")]
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
#[serial_test::serial(remote_exec_server)]
|
#[serial_test::serial(remote_exec_server)]
|
||||||
async fn remote_windows_sandbox_process_accepts_process_write() -> Result<()> {
|
async fn remote_windows_sandbox_process_accepts_process_write(
|
||||||
assert_remote_windows_sandbox_process_write().await
|
windows_sandbox_level: WindowsSandboxLevel,
|
||||||
|
expected_sandbox_type: codex_sandboxing::SandboxType,
|
||||||
|
) -> Result<()> {
|
||||||
|
assert_remote_windows_sandbox_process_write(windows_sandbox_level, expected_sandbox_type).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case(false ; "local")]
|
#[test_case(false ; "local")]
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
//! Shared Windows sandbox behavior over the real exec-server RPC connection.
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use codex_protocol::models::PermissionProfile;
|
||||||
|
use codex_protocol::permissions::FileSystemAccessMode;
|
||||||
|
use codex_protocol::permissions::FileSystemPath;
|
||||||
|
use codex_protocol::permissions::FileSystemSandboxEntry;
|
||||||
|
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||||
|
use codex_protocol::permissions::FileSystemSpecialPath;
|
||||||
|
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
#[cfg_attr(not(windows), ignore = "requires a native Windows sandbox")]
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
#[serial_test::serial(remote_exec_server)]
|
||||||
|
async fn mxc_tmpdir_uses_command_environment_over_rpc() -> Result<()> {
|
||||||
|
if !selected_windows_sandbox_available(WindowsSandboxLevel::Mxc) {
|
||||||
|
eprintln!("skipping MXC enforcement test: native MXC is unavailable on this host");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let root = TempDir::new()?;
|
||||||
|
let command_temp = root.path().join("command temp");
|
||||||
|
let server_temp = root.path().join("server temp");
|
||||||
|
std::fs::create_dir(&command_temp)?;
|
||||||
|
std::fs::create_dir(&server_temp)?;
|
||||||
|
let outside = server_temp.join("outside.txt");
|
||||||
|
std::fs::write(&outside, "original")?;
|
||||||
|
let server = common::exec_server::exec_server_with_env(
|
||||||
|
[
|
||||||
|
("TEMP", server_temp.as_os_str()),
|
||||||
|
("TMP", server_temp.as_os_str()),
|
||||||
|
],
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let environment = Environment::create_for_tests(Some(server.websocket_url().to_owned()))?;
|
||||||
|
let cwd = PathUri::from_host_native_path(root.path())?;
|
||||||
|
let fs = FileSystemSandboxPolicy::restricted(vec![
|
||||||
|
FileSystemSandboxEntry::new(
|
||||||
|
FileSystemPath::Special {
|
||||||
|
value: FileSystemSpecialPath::Root,
|
||||||
|
},
|
||||||
|
FileSystemAccessMode::Read,
|
||||||
|
),
|
||||||
|
FileSystemSandboxEntry::new(
|
||||||
|
FileSystemPath::Special {
|
||||||
|
value: FileSystemSpecialPath::Tmpdir,
|
||||||
|
},
|
||||||
|
FileSystemAccessMode::Write,
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
let mut sandbox = FileSystemSandboxContext::from_permission_profile_with_cwd(
|
||||||
|
PermissionProfile::from_runtime_permissions(&fs, NetworkSandboxPolicy::Restricted),
|
||||||
|
cwd.clone(),
|
||||||
|
);
|
||||||
|
sandbox.windows_sandbox_level = WindowsSandboxLevel::Mxc;
|
||||||
|
let command_temp = command_temp.to_string_lossy().into_owned();
|
||||||
|
let started = environment
|
||||||
|
.get_exec_backend()
|
||||||
|
.start(ExecParams {
|
||||||
|
process_id: ProcessId::from("windows-sandbox-temp"),
|
||||||
|
metadata: None,
|
||||||
|
argv: vec![
|
||||||
|
r"C:\Windows\System32\cmd.exe".to_owned(),
|
||||||
|
"/D".to_owned(),
|
||||||
|
"/S".to_owned(),
|
||||||
|
"/C".to_owned(),
|
||||||
|
format!(
|
||||||
|
"echo allowed>\"%TEMP%\\allowed.txt\" & 2>\"%TEMP%\\denied.txt\" echo modified>\"{}\" & exit /b 0",
|
||||||
|
outside.display()
|
||||||
|
),
|
||||||
|
],
|
||||||
|
cwd,
|
||||||
|
env_policy: None,
|
||||||
|
shell_snapshot: None,
|
||||||
|
env: HashMap::from([
|
||||||
|
("SystemRoot".to_owned(), std::env::var("SystemRoot")?),
|
||||||
|
("TEMP".to_owned(), command_temp.clone()),
|
||||||
|
("TMP".to_owned(), command_temp),
|
||||||
|
]),
|
||||||
|
tty: false,
|
||||||
|
pipe_stdin: false,
|
||||||
|
arg0: None,
|
||||||
|
sandbox: Some(sandbox),
|
||||||
|
enforce_managed_network: false,
|
||||||
|
managed_network: None,
|
||||||
|
network_proxy: None,
|
||||||
|
})
|
||||||
|
.await?;
|
||||||
|
assert_eq!(
|
||||||
|
started.sandbox_type,
|
||||||
|
Some(codex_sandboxing::SandboxType::WindowsMxc)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
collect_process_output_from_events_with_timeout(
|
||||||
|
started.process,
|
||||||
|
Duration::from_secs(/*secs*/ 30),
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
(String::new(), String::new(), Some(0), true)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
(
|
||||||
|
std::fs::read_to_string(root.path().join("command temp").join("allowed.txt"))?
|
||||||
|
.trim_end()
|
||||||
|
.to_owned(),
|
||||||
|
std::fs::read_to_string(outside)?
|
||||||
|
),
|
||||||
|
("allowed".to_owned(), "original".to_owned())
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -52,6 +52,7 @@ pub(super) async fn spawn_command_under_sandbox(
|
|||||||
sandbox_cwd,
|
sandbox_cwd,
|
||||||
std::slice::from_ref(sandbox_cwd),
|
std::slice::from_ref(sandbox_cwd),
|
||||||
&codex_linux_sandbox_exe,
|
&codex_linux_sandbox_exe,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
/*use_legacy_landlock*/ false,
|
/*use_legacy_landlock*/ false,
|
||||||
)
|
)
|
||||||
.map_err(|err| io::Error::other(err.to_string()))?;
|
.map_err(|err| io::Error::other(err.to_string()))?;
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ async fn run_cmd_result_with_permission_profile_for_cwd(
|
|||||||
&sandbox_cwd,
|
&sandbox_cwd,
|
||||||
std::slice::from_ref(&sandbox_cwd),
|
std::slice::from_ref(&sandbox_cwd),
|
||||||
&codex_linux_sandbox_exe,
|
&codex_linux_sandbox_exe,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
use_legacy_landlock,
|
use_legacy_landlock,
|
||||||
/*stdout_stream*/ None,
|
/*stdout_stream*/ None,
|
||||||
)
|
)
|
||||||
@@ -891,6 +892,7 @@ async fn assert_network_blocked(cmd: &[&str]) {
|
|||||||
&sandbox_cwd,
|
&sandbox_cwd,
|
||||||
std::slice::from_ref(&sandbox_cwd),
|
std::slice::from_ref(&sandbox_cwd),
|
||||||
&codex_linux_sandbox_exe,
|
&codex_linux_sandbox_exe,
|
||||||
|
/*codex_self_exe*/ &None,
|
||||||
/*use_legacy_landlock*/ false,
|
/*use_legacy_landlock*/ false,
|
||||||
/*stdout_stream*/ None,
|
/*stdout_stream*/ None,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ dispatcher, edits host ACLs, creates sandbox users, runs setup, or requests
|
|||||||
elevation. The existing Codex Windows sandboxes remain separate backends.
|
elevation. The existing Codex Windows sandboxes remain separate backends.
|
||||||
|
|
||||||
Windows executors record `codex.windows_mxc.available` once per process with an
|
Windows executors record `codex.windows_mxc.available` once per process with an
|
||||||
`available=true|false` tag. This measures capability independently of selection.
|
`available=true|false` tag. This measures runtime availability independently of
|
||||||
The exec-server handshake also exposes `windows_mxc_available`; older or
|
selection. Unsupported Windows executors reject MXC requests before execution.
|
||||||
unsupported Windows executors reject MXC requests before execution.
|
|
||||||
|
|
||||||
`is_available()` uses MXC's cached create/close probe, rather than an OS build
|
`is_available()` uses MXC's cached create/close probe, rather than an OS build
|
||||||
number or the SDK's broad `platform_support()` result. The latter also reports
|
number or the SDK's broad `platform_support()` result. The latter also reports
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ pub fn run_main() -> ! {
|
|||||||
match windows::run() {
|
match windows::run() {
|
||||||
Ok(exit_code) => std::process::exit(exit_code),
|
Ok(exit_code) => std::process::exit(exit_code),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("MXC sandbox: {error:#}");
|
eprintln!("MXC launcher: {error:#}");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ pub(super) fn run() -> Result<i32> {
|
|||||||
Ok((key, value))
|
Ok((key, value))
|
||||||
})
|
})
|
||||||
.collect::<Result<HashMap<_, _>>>()?;
|
.collect::<Result<HashMap<_, _>>>()?;
|
||||||
let command = crate::transport::decode(&mut env)?;
|
// Serde errors can quote arbitrary payload values; do not print them to stderr.
|
||||||
ensure!(
|
let command = crate::transport::decode(&mut env)
|
||||||
!env.is_empty(),
|
.map_err(|_| anyhow::anyhow!("invalid MXC launcher request"))?;
|
||||||
"MXC requires an explicit child environment"
|
// The transport itself proves this is an explicit child environment; it
|
||||||
);
|
// may intentionally contain no variables once launcher state is removed.
|
||||||
let mask = unsafe { GetLogicalDrives() };
|
let mask = unsafe { GetLogicalDrives() };
|
||||||
ensure!(
|
ensure!(
|
||||||
mask != 0,
|
mask != 0,
|
||||||
|
|||||||
@@ -299,6 +299,7 @@ pub enum WindowsSandboxLevel {
|
|||||||
Disabled,
|
Disabled,
|
||||||
RestrictedToken,
|
RestrictedToken,
|
||||||
Elevated,
|
Elevated,
|
||||||
|
Mxc,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Controls whether a Windows sandbox launch reconciles persistent proxy settings or preserves
|
/// Controls whether a Windows sandbox launch reconciles persistent proxy settings or preserves
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ pub use bwrap::find_system_bwrap_in_path;
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub use bwrap::system_bwrap_warning;
|
pub use bwrap::system_bwrap_warning;
|
||||||
pub use codex_mxc_sandbox::CODEX_WINDOWS_MXC_ARG1;
|
pub use codex_mxc_sandbox::CODEX_WINDOWS_MXC_ARG1;
|
||||||
|
pub use codex_mxc_sandbox::is_available as windows_mxc_available;
|
||||||
pub use codex_mxc_sandbox::run_main as run_windows_mxc_main;
|
pub use codex_mxc_sandbox::run_main as run_windows_mxc_main;
|
||||||
pub use codex_windows_sandbox::WindowsSandboxProxySettingsMode;
|
pub use codex_windows_sandbox::WindowsSandboxProxySettingsMode;
|
||||||
pub use denial::is_likely_executor_managed_sandbox_denied;
|
pub use denial::is_likely_executor_managed_sandbox_denied;
|
||||||
@@ -73,6 +74,9 @@ impl From<SandboxTransformError> for CodexErr {
|
|||||||
SandboxTransformError::EnvironmentNetworkProxy(message) => {
|
SandboxTransformError::EnvironmentNetworkProxy(message) => {
|
||||||
CodexErr::UnsupportedOperation(message)
|
CodexErr::UnsupportedOperation(message)
|
||||||
}
|
}
|
||||||
|
SandboxTransformError::WindowsMxcPreparation(message) => {
|
||||||
|
CodexErr::UnsupportedOperation(message)
|
||||||
|
}
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
SandboxTransformError::SeatbeltPreparation(message) => {
|
SandboxTransformError::SeatbeltPreparation(message) => {
|
||||||
CodexErr::UnsupportedOperation(message)
|
CodexErr::UnsupportedOperation(message)
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ pub enum SandboxType {
|
|||||||
MacosSeatbelt,
|
MacosSeatbelt,
|
||||||
LinuxSeccomp,
|
LinuxSeccomp,
|
||||||
WindowsRestrictedToken,
|
WindowsRestrictedToken,
|
||||||
|
WindowsMxc,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SandboxType {
|
impl SandboxType {
|
||||||
@@ -53,6 +54,7 @@ impl SandboxType {
|
|||||||
SandboxType::MacosSeatbelt => "seatbelt",
|
SandboxType::MacosSeatbelt => "seatbelt",
|
||||||
SandboxType::LinuxSeccomp => "seccomp",
|
SandboxType::LinuxSeccomp => "seccomp",
|
||||||
SandboxType::WindowsRestrictedToken => "windows_sandbox",
|
SandboxType::WindowsRestrictedToken => "windows_sandbox",
|
||||||
|
SandboxType::WindowsMxc => "windows_mxc",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +147,7 @@ pub struct SandboxTransformRequest<'a> {
|
|||||||
// to make shared ownership explicit across runtime/sandbox plumbing.
|
// to make shared ownership explicit across runtime/sandbox plumbing.
|
||||||
pub network: Option<&'a NetworkProxy>,
|
pub network: Option<&'a NetworkProxy>,
|
||||||
pub sandbox_policy_cwd: &'a PathUri,
|
pub sandbox_policy_cwd: &'a PathUri,
|
||||||
pub codex_linux_sandbox_exe: Option<&'a Path>,
|
pub sandbox_exe: Option<&'a Path>,
|
||||||
// TODO(anp): Reconcile these backend inputs with the supplied sandbox context
|
// TODO(anp): Reconcile these backend inputs with the supplied sandbox context
|
||||||
// (TurnEnvironment::sandbox_context for turns) so selection shares its authority.
|
// (TurnEnvironment::sandbox_context for turns) so selection shares its authority.
|
||||||
pub use_legacy_landlock: bool,
|
pub use_legacy_landlock: bool,
|
||||||
@@ -215,6 +217,7 @@ pub enum SandboxTransformError {
|
|||||||
source: io::Error,
|
source: io::Error,
|
||||||
},
|
},
|
||||||
MissingLinuxSandboxExecutable,
|
MissingLinuxSandboxExecutable,
|
||||||
|
WindowsMxcPreparation(String),
|
||||||
EnvironmentNetworkProxy(String),
|
EnvironmentNetworkProxy(String),
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
SeatbeltPreparation(String),
|
SeatbeltPreparation(String),
|
||||||
@@ -242,6 +245,9 @@ impl std::fmt::Display for SandboxTransformError {
|
|||||||
Self::MissingLinuxSandboxExecutable => {
|
Self::MissingLinuxSandboxExecutable => {
|
||||||
write!(f, "missing codex-linux-sandbox executable path")
|
write!(f, "missing codex-linux-sandbox executable path")
|
||||||
}
|
}
|
||||||
|
Self::WindowsMxcPreparation(err) => {
|
||||||
|
write!(f, "failed to prepare MXC sandbox: {err}")
|
||||||
|
}
|
||||||
Self::EnvironmentNetworkProxy(err) => {
|
Self::EnvironmentNetworkProxy(err) => {
|
||||||
write!(f, "failed to prepare environment network proxy: {err}")
|
write!(f, "failed to prepare environment network proxy: {err}")
|
||||||
}
|
}
|
||||||
@@ -267,6 +273,7 @@ impl std::error::Error for SandboxTransformError {
|
|||||||
Self::InvalidCommandCwd { source, .. }
|
Self::InvalidCommandCwd { source, .. }
|
||||||
| Self::InvalidSandboxPolicyCwd { source, .. } => Some(source),
|
| Self::InvalidSandboxPolicyCwd { source, .. } => Some(source),
|
||||||
Self::MissingLinuxSandboxExecutable => None,
|
Self::MissingLinuxSandboxExecutable => None,
|
||||||
|
Self::WindowsMxcPreparation(_) => None,
|
||||||
Self::EnvironmentNetworkProxy(_) => None,
|
Self::EnvironmentNetworkProxy(_) => None,
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
Self::SeatbeltPreparation(_) => None,
|
Self::SeatbeltPreparation(_) => None,
|
||||||
@@ -324,12 +331,14 @@ impl SandboxManager {
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
crate::windows_mxc::record_availability_once();
|
crate::windows_mxc::record_availability_once();
|
||||||
|
|
||||||
if self.should_sandbox(permission_profile, pref, has_managed_network_requirements) {
|
if !self.should_sandbox(permission_profile, pref, has_managed_network_requirements) {
|
||||||
get_platform_sandbox(windows_sandbox_level != WindowsSandboxLevel::Disabled)
|
return SandboxType::None;
|
||||||
.unwrap_or(SandboxType::None)
|
|
||||||
} else {
|
|
||||||
SandboxType::None
|
|
||||||
}
|
}
|
||||||
|
if cfg!(windows) && windows_sandbox_level == WindowsSandboxLevel::Mxc {
|
||||||
|
return SandboxType::WindowsMxc;
|
||||||
|
}
|
||||||
|
get_platform_sandbox(windows_sandbox_level != WindowsSandboxLevel::Disabled)
|
||||||
|
.unwrap_or(SandboxType::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether the request needs a sandbox, independently of whether
|
/// Returns whether the request needs a sandbox, independently of whether
|
||||||
@@ -367,7 +376,7 @@ impl SandboxManager {
|
|||||||
environment_id,
|
environment_id,
|
||||||
network,
|
network,
|
||||||
sandbox_policy_cwd,
|
sandbox_policy_cwd,
|
||||||
codex_linux_sandbox_exe,
|
sandbox_exe,
|
||||||
use_legacy_landlock,
|
use_legacy_landlock,
|
||||||
windows_sandbox_level,
|
windows_sandbox_level,
|
||||||
windows_sandbox_private_desktop,
|
windows_sandbox_private_desktop,
|
||||||
@@ -386,11 +395,62 @@ impl SandboxManager {
|
|||||||
managed_mitm_ca_trust_bundle_path.as_ref(),
|
managed_mitm_ca_trust_bundle_path.as_ref(),
|
||||||
);
|
);
|
||||||
let mut argv = Vec::with_capacity(1 + command.args.len());
|
let mut argv = Vec::with_capacity(1 + command.args.len());
|
||||||
argv.push(command.program);
|
argv.push(os_string_to_command_component(command.program));
|
||||||
argv.extend(command.args.into_iter().map(OsString::from));
|
argv.extend(command.args);
|
||||||
|
|
||||||
let (argv, arg0_override, pending_sandboxed_request) = match sandbox {
|
let (argv, arg0_override, pending_sandboxed_request) = match sandbox {
|
||||||
SandboxType::None => (os_argv_to_strings(argv), None, None),
|
SandboxType::None => (argv, None, None),
|
||||||
|
SandboxType::WindowsMxc => {
|
||||||
|
if windows_sandbox_private_desktop {
|
||||||
|
return Err(SandboxTransformError::WindowsMxcPreparation(
|
||||||
|
"private desktop isolation is not supported by MXC".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if !codex_mxc_sandbox::is_available() {
|
||||||
|
return Err(SandboxTransformError::WindowsMxcPreparation(
|
||||||
|
"native MXC is unavailable on this executor".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if enforce_managed_network && command.managed_network.is_none() {
|
||||||
|
let network = network.ok_or_else(|| {
|
||||||
|
SandboxTransformError::WindowsMxcPreparation(
|
||||||
|
"managed networking requires an executor-local proxy".to_string(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let prepared = network
|
||||||
|
.prepare_for_optional_environment(
|
||||||
|
std::mem::take(&mut command.env),
|
||||||
|
environment_id,
|
||||||
|
)
|
||||||
|
.map_err(|err| {
|
||||||
|
SandboxTransformError::EnvironmentNetworkProxy(err.to_string())
|
||||||
|
})?;
|
||||||
|
command.env = prepared.env;
|
||||||
|
command.managed_network = Some(prepared.sandbox_context);
|
||||||
|
}
|
||||||
|
let managed_network = command.managed_network.filter(|_| enforce_managed_network);
|
||||||
|
let pending = pending_sandboxed_request?;
|
||||||
|
let exe = sandbox_exe.ok_or_else(|| {
|
||||||
|
SandboxTransformError::WindowsMxcPreparation(
|
||||||
|
"missing Codex executable path".to_string(),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let mut full_command =
|
||||||
|
vec![os_string_to_command_component(exe.as_os_str().to_owned())];
|
||||||
|
full_command.extend(
|
||||||
|
codex_mxc_sandbox::create_command_args(
|
||||||
|
codex_mxc_sandbox::CreateMxcCommandArgsParams {
|
||||||
|
command: argv,
|
||||||
|
permission_profile: &pending.effective_permission_profile,
|
||||||
|
sandbox_policy_cwd: pending.native_sandbox_policy_cwd.as_path(),
|
||||||
|
managed_network: managed_network.as_ref(),
|
||||||
|
env: &mut command.env,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|err| SandboxTransformError::WindowsMxcPreparation(err.to_string()))?,
|
||||||
|
);
|
||||||
|
(full_command, None, Some(pending))
|
||||||
|
}
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
SandboxType::MacosSeatbelt => {
|
SandboxType::MacosSeatbelt => {
|
||||||
use crate::seatbelt::CreateSeatbeltCommandArgsParams;
|
use crate::seatbelt::CreateSeatbeltCommandArgsParams;
|
||||||
@@ -404,7 +464,7 @@ impl SandboxManager {
|
|||||||
.to_runtime_permissions();
|
.to_runtime_permissions();
|
||||||
let mut args = create_seatbelt_command_args_with_profile(
|
let mut args = create_seatbelt_command_args_with_profile(
|
||||||
CreateSeatbeltCommandArgsParams {
|
CreateSeatbeltCommandArgsParams {
|
||||||
command: os_argv_to_strings(argv),
|
command: argv,
|
||||||
file_system_sandbox_policy: &file_system_sandbox_policy,
|
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||||
network_sandbox_policy,
|
network_sandbox_policy,
|
||||||
sandbox_policy_cwd: pending.native_sandbox_policy_cwd.as_path(),
|
sandbox_policy_cwd: pending.native_sandbox_policy_cwd.as_path(),
|
||||||
@@ -434,8 +494,8 @@ impl SandboxManager {
|
|||||||
SandboxType::MacosSeatbelt => return Err(SandboxTransformError::SeatbeltUnavailable),
|
SandboxType::MacosSeatbelt => return Err(SandboxTransformError::SeatbeltUnavailable),
|
||||||
SandboxType::LinuxSeccomp => {
|
SandboxType::LinuxSeccomp => {
|
||||||
let pending = pending_sandboxed_request?;
|
let pending = pending_sandboxed_request?;
|
||||||
let exe = codex_linux_sandbox_exe
|
let exe =
|
||||||
.ok_or(SandboxTransformError::MissingLinuxSandboxExecutable)?;
|
sandbox_exe.ok_or(SandboxTransformError::MissingLinuxSandboxExecutable)?;
|
||||||
let allow_proxy_network = allow_network_for_proxy(enforce_managed_network);
|
let allow_proxy_network = allow_network_for_proxy(enforce_managed_network);
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
ensure_linux_bubblewrap_is_supported(
|
ensure_linux_bubblewrap_is_supported(
|
||||||
@@ -447,7 +507,7 @@ impl SandboxManager {
|
|||||||
is_wsl1(),
|
is_wsl1(),
|
||||||
)?;
|
)?;
|
||||||
let mut args = create_linux_sandbox_command_args_for_permission_profile(
|
let mut args = create_linux_sandbox_command_args_for_permission_profile(
|
||||||
os_argv_to_strings(argv),
|
argv,
|
||||||
pending.native_command_cwd.as_path(),
|
pending.native_command_cwd.as_path(),
|
||||||
&pending.effective_permission_profile,
|
&pending.effective_permission_profile,
|
||||||
pending.native_sandbox_policy_cwd.as_path(),
|
pending.native_sandbox_policy_cwd.as_path(),
|
||||||
@@ -487,14 +547,10 @@ impl SandboxManager {
|
|||||||
)],
|
)],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(os_argv_to_strings(argv), None, Some(pending))
|
(argv, None, Some(pending))
|
||||||
}
|
}
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
SandboxType::WindowsRestrictedToken => (
|
SandboxType::WindowsRestrictedToken => (argv, None, Some(pending_sandboxed_request?)),
|
||||||
os_argv_to_strings(argv),
|
|
||||||
None,
|
|
||||||
Some(pending_sandboxed_request?),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unsandboxed exec-server requests may have foreign cwd values that cannot be prepared
|
// Unsandboxed exec-server requests may have foreign cwd values that cannot be prepared
|
||||||
@@ -525,16 +581,12 @@ impl SandboxManager {
|
|||||||
request: SandboxDirectSpawnTransformRequest<'_>,
|
request: SandboxDirectSpawnTransformRequest<'_>,
|
||||||
) -> Result<SandboxExecRequest, SandboxTransformError> {
|
) -> Result<SandboxExecRequest, SandboxTransformError> {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
if request.transform.sandbox == SandboxType::WindowsRestrictedToken {
|
||||||
let codex_home = codex_utils_home_dir::find_codex_home()
|
let codex_home = codex_utils_home_dir::find_codex_home()
|
||||||
.map_err(|err| SandboxTransformError::WindowsSandboxPreparation(err.to_string()))?;
|
.map_err(|err| SandboxTransformError::WindowsSandboxPreparation(err.to_string()))?;
|
||||||
self.transform_for_direct_spawn_with_codex_home(request, codex_home.as_path())
|
return self.transform_for_direct_spawn_with_codex_home(request, codex_home.as_path());
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
{
|
|
||||||
self.transform(request.transform)
|
|
||||||
}
|
}
|
||||||
|
self.transform(request.transform)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
@@ -751,12 +803,6 @@ fn ensure_linux_bubblewrap_is_supported(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn os_argv_to_strings(argv: Vec<OsString>) -> Vec<String> {
|
|
||||||
argv.into_iter()
|
|
||||||
.map(os_string_to_command_component)
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn os_string_to_command_component(value: OsString) -> String {
|
fn os_string_to_command_component(value: OsString) -> String {
|
||||||
value
|
value
|
||||||
.into_string()
|
.into_string()
|
||||||
|
|||||||
@@ -25,6 +25,48 @@ use pretty_assertions::assert_eq;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mxc_selection_preserves_unsandboxed_requests_and_other_platforms() {
|
||||||
|
let manager = SandboxManager::new();
|
||||||
|
let platform_sandbox = if cfg!(windows) {
|
||||||
|
SandboxType::WindowsMxc
|
||||||
|
} else {
|
||||||
|
get_platform_sandbox(/*windows_sandbox_enabled*/ false).unwrap_or(SandboxType::None)
|
||||||
|
};
|
||||||
|
for (permissions, preference, expected) in [
|
||||||
|
(
|
||||||
|
PermissionProfile::read_only(),
|
||||||
|
SandboxablePreference::Auto,
|
||||||
|
platform_sandbox,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PermissionProfile::Disabled,
|
||||||
|
SandboxablePreference::Require,
|
||||||
|
platform_sandbox,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PermissionProfile::read_only(),
|
||||||
|
SandboxablePreference::Forbid,
|
||||||
|
SandboxType::None,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
PermissionProfile::Disabled,
|
||||||
|
SandboxablePreference::Auto,
|
||||||
|
SandboxType::None,
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
assert_eq!(
|
||||||
|
manager.select_initial(
|
||||||
|
&permissions,
|
||||||
|
preference,
|
||||||
|
WindowsSandboxLevel::Mxc,
|
||||||
|
/*has_managed_network_requirements*/ false,
|
||||||
|
),
|
||||||
|
expected,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn danger_full_access_defaults_to_no_sandbox_without_network_requirements() {
|
fn danger_full_access_defaults_to_no_sandbox_without_network_requirements() {
|
||||||
let manager = SandboxManager::new();
|
let manager = SandboxManager::new();
|
||||||
@@ -103,7 +145,7 @@ fn unsandboxed_transform_preserves_foreign_cwd_and_unrestricted_file_system_poli
|
|||||||
environment_id: None,
|
environment_id: None,
|
||||||
network: None,
|
network: None,
|
||||||
sandbox_policy_cwd: &cwd_uri,
|
sandbox_policy_cwd: &cwd_uri,
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
@@ -160,7 +202,7 @@ fn symlinked_workspace_reports_seatbelt_preparation_error() {
|
|||||||
environment_id: None,
|
environment_id: None,
|
||||||
network: None,
|
network: None,
|
||||||
sandbox_policy_cwd: &workspace_uri,
|
sandbox_policy_cwd: &workspace_uri,
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
@@ -215,7 +257,7 @@ fn transform_additional_permissions_enable_network_for_external_sandbox() {
|
|||||||
environment_id: None,
|
environment_id: None,
|
||||||
network: None,
|
network: None,
|
||||||
sandbox_policy_cwd: &cwd_uri,
|
sandbox_policy_cwd: &cwd_uri,
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
@@ -286,7 +328,7 @@ fn transform_additional_permissions_preserves_denied_entries() {
|
|||||||
environment_id: None,
|
environment_id: None,
|
||||||
network: None,
|
network: None,
|
||||||
sandbox_policy_cwd: &cwd_uri,
|
sandbox_policy_cwd: &cwd_uri,
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
@@ -388,7 +430,7 @@ fn transform_linux_seccomp_request(
|
|||||||
environment_id: None,
|
environment_id: None,
|
||||||
network: None,
|
network: None,
|
||||||
sandbox_policy_cwd: &cwd_uri,
|
sandbox_policy_cwd: &cwd_uri,
|
||||||
codex_linux_sandbox_exe: Some(codex_linux_sandbox_exe),
|
sandbox_exe: Some(codex_linux_sandbox_exe),
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
@@ -590,7 +632,7 @@ fn transform_for_direct_spawn_windows_materializes_inner_helper() {
|
|||||||
environment_id: None,
|
environment_id: None,
|
||||||
network: None,
|
network: None,
|
||||||
sandbox_policy_cwd: &cwd_uri,
|
sandbox_policy_cwd: &cwd_uri,
|
||||||
codex_linux_sandbox_exe: None,
|
sandbox_exe: None,
|
||||||
use_legacy_landlock: false,
|
use_legacy_landlock: false,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Elevated,
|
windows_sandbox_level: WindowsSandboxLevel::Elevated,
|
||||||
windows_sandbox_private_desktop: false,
|
windows_sandbox_private_desktop: false,
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ pub enum SandboxViolationBackend {
|
|||||||
ManagedNetworkProxy,
|
ManagedNetworkProxy,
|
||||||
Seatbelt,
|
Seatbelt,
|
||||||
WindowsSandbox,
|
WindowsSandbox,
|
||||||
|
WindowsMxc,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SandboxViolationBackend {
|
impl SandboxViolationBackend {
|
||||||
@@ -59,6 +60,7 @@ impl SandboxViolationBackend {
|
|||||||
Self::ManagedNetworkProxy => "managed_network_proxy",
|
Self::ManagedNetworkProxy => "managed_network_proxy",
|
||||||
Self::Seatbelt => "seatbelt",
|
Self::Seatbelt => "seatbelt",
|
||||||
Self::WindowsSandbox => "windows_sandbox",
|
Self::WindowsSandbox => "windows_sandbox",
|
||||||
|
Self::WindowsMxc => "windows_mxc",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,6 +145,7 @@ fn classify_filesystem_sandbox_violation(
|
|||||||
SandboxType::MacosSeatbelt => SandboxViolationBackend::Seatbelt,
|
SandboxType::MacosSeatbelt => SandboxViolationBackend::Seatbelt,
|
||||||
SandboxType::LinuxSeccomp => SandboxViolationBackend::LinuxSandbox,
|
SandboxType::LinuxSeccomp => SandboxViolationBackend::LinuxSandbox,
|
||||||
SandboxType::WindowsRestrictedToken => SandboxViolationBackend::WindowsSandbox,
|
SandboxType::WindowsRestrictedToken => SandboxViolationBackend::WindowsSandbox,
|
||||||
|
SandboxType::WindowsMxc => SandboxViolationBackend::WindowsMxc,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some((reason, output)) = filesystem_reason_from_output(exec_output) {
|
if let Some((reason, output)) = filesystem_reason_from_output(exec_output) {
|
||||||
|
|||||||
Reference in new issue
Block a user