mirror of
https://github.com/openai/codex.git
synced 2026-10-28 18:58:46 +00:00
## What changed Wire existing sandbox integrity checks into local command preparation, exec-server process preparation, and sandboxed filesystem helper and file-open operations. Make execution request preparation asynchronous so callers can await the checks. The checks remain observational: failures and timeouts do not prevent command execution. ## Testing Add regression tests that launch a real command after checker preparation fails or exceeds its timeout, verifying successful output and exit status. GitOrigin-RevId: a82399f8aeb8b08f9965e6121b4c7f460552af3f
221 lines
8.0 KiB
Rust
221 lines
8.0 KiB
Rust
/*
|
|
Module: sandboxing
|
|
|
|
Core-owned adapter types for exec/runtime plumbing. Policy selection and
|
|
command transformation live in the codex-sandboxing crate; this module keeps
|
|
the exec-only metadata and translates transformed sandbox commands back into
|
|
ExecRequest for execution.
|
|
*/
|
|
|
|
use crate::exec::ExecCapturePolicy;
|
|
use crate::exec::ExecExpiration;
|
|
use crate::exec::StdoutStream;
|
|
use crate::exec::execute_exec_request;
|
|
#[cfg(target_os = "macos")]
|
|
use crate::spawn::CODEX_SANDBOX_ENV_VAR;
|
|
use crate::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
|
|
use codex_file_system::FileSystemSandboxContext;
|
|
use codex_network_proxy::ManagedNetworkSandboxContext;
|
|
use codex_network_proxy::NetworkProxy;
|
|
use codex_network_proxy::RemoteNetworkProxyLaunchConfig;
|
|
use codex_protocol::config_types::WindowsSandboxLevel;
|
|
use codex_protocol::error::CodexErr;
|
|
use codex_protocol::exec_output::ExecToolCallOutput;
|
|
use codex_protocol::models::PermissionProfile;
|
|
pub use codex_protocol::models::SandboxPermissions;
|
|
use codex_sandboxing::SandboxExecRequest;
|
|
use codex_sandboxing::SandboxType;
|
|
use codex_sandboxing::WindowsSandboxFilesystemOverrides;
|
|
use codex_sandboxing::resolve_windows_elevated_filesystem_overrides;
|
|
use codex_sandboxing::resolve_windows_restricted_token_filesystem_overrides;
|
|
use codex_sandboxing::windows_sandbox_uses_elevated_backend;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use codex_utils_path_uri::PathUri;
|
|
use codex_utils_string::truncate_middle_with_token_budget;
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct ExecOptions {
|
|
pub(crate) expiration: ExecExpiration,
|
|
pub(crate) capture_policy: ExecCapturePolicy,
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub(crate) struct ExecServerEnvConfig {
|
|
pub(crate) policy: codex_exec_server::ExecEnvPolicy,
|
|
pub(crate) local_policy_env: HashMap<String, String>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ExecRequest {
|
|
pub command: Vec<String>,
|
|
pub cwd: PathUri,
|
|
pub env: HashMap<String, String>,
|
|
pub(crate) exec_server_env_config: Option<ExecServerEnvConfig>,
|
|
pub(crate) exec_server_shell_snapshot: Option<codex_exec_server::ShellSnapshotRequest>,
|
|
pub network: Option<NetworkProxy>,
|
|
pub network_environment_id: Option<String>,
|
|
pub expiration: ExecExpiration,
|
|
pub capture_policy: ExecCapturePolicy,
|
|
pub sandbox: SandboxType,
|
|
pub windows_sandbox_policy_cwd: PathUri,
|
|
pub windows_sandbox_workspace_roots: Vec<AbsolutePathBuf>,
|
|
// TODO(anp): Reconcile these backend copies with TurnEnvironment::sandbox_context
|
|
// and exec_server_sandbox so local and remote launches use the same settings.
|
|
pub windows_sandbox_level: WindowsSandboxLevel,
|
|
pub permission_profile: PermissionProfile,
|
|
pub(crate) windows_sandbox_filesystem_overrides: Option<WindowsSandboxFilesystemOverrides>,
|
|
pub arg0: Option<String>,
|
|
pub(crate) exec_server_sandbox: Option<FileSystemSandboxContext>,
|
|
pub(crate) exec_server_enforce_managed_network: bool,
|
|
pub(crate) exec_server_managed_network: Option<ManagedNetworkSandboxContext>,
|
|
pub(crate) exec_server_network_proxy: Option<RemoteNetworkProxyLaunchConfig>,
|
|
}
|
|
|
|
impl ExecRequest {
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn new(
|
|
command: Vec<String>,
|
|
cwd: AbsolutePathBuf,
|
|
env: HashMap<String, String>,
|
|
network: Option<NetworkProxy>,
|
|
network_environment_id: Option<String>,
|
|
expiration: ExecExpiration,
|
|
capture_policy: ExecCapturePolicy,
|
|
sandbox: SandboxType,
|
|
windows_sandbox_workspace_roots: Vec<AbsolutePathBuf>,
|
|
windows_sandbox_level: WindowsSandboxLevel,
|
|
permission_profile: PermissionProfile,
|
|
arg0: Option<String>,
|
|
) -> Self {
|
|
let cwd = PathUri::from_abs_path(&cwd);
|
|
let windows_sandbox_policy_cwd = cwd.clone();
|
|
Self {
|
|
command,
|
|
cwd,
|
|
env,
|
|
exec_server_env_config: None,
|
|
exec_server_shell_snapshot: None,
|
|
network,
|
|
network_environment_id,
|
|
expiration,
|
|
capture_policy,
|
|
sandbox,
|
|
windows_sandbox_policy_cwd,
|
|
windows_sandbox_workspace_roots,
|
|
windows_sandbox_level,
|
|
permission_profile,
|
|
windows_sandbox_filesystem_overrides: None,
|
|
arg0,
|
|
exec_server_sandbox: None,
|
|
exec_server_enforce_managed_network: false,
|
|
exec_server_managed_network: None,
|
|
exec_server_network_proxy: None,
|
|
}
|
|
}
|
|
|
|
pub(crate) async fn from_sandbox_exec_request(
|
|
request: SandboxExecRequest,
|
|
options: ExecOptions,
|
|
windows_sandbox_workspace_roots: Vec<AbsolutePathBuf>,
|
|
) -> Result<Self, CodexErr> {
|
|
codex_exec_server::run_integrity_checks(&request).await;
|
|
let SandboxExecRequest {
|
|
command,
|
|
cwd,
|
|
sandbox_policy_cwd: windows_sandbox_policy_cwd,
|
|
mut env,
|
|
network,
|
|
network_environment_id,
|
|
sandbox,
|
|
windows_sandbox_level,
|
|
permission_profile,
|
|
arg0,
|
|
..
|
|
} = request;
|
|
let ExecOptions {
|
|
expiration,
|
|
capture_policy,
|
|
} = options;
|
|
let windows_sandbox_filesystem_overrides = if sandbox == SandboxType::WindowsRestrictedToken
|
|
{
|
|
let sandbox_policy_cwd = windows_sandbox_policy_cwd
|
|
.to_abs_path()
|
|
.map_err(|err| CodexErr::InvalidRequest(format!("invalid sandbox cwd: {err}")))?;
|
|
let use_windows_elevated_backend =
|
|
windows_sandbox_uses_elevated_backend(windows_sandbox_level);
|
|
if use_windows_elevated_backend {
|
|
resolve_windows_elevated_filesystem_overrides(
|
|
sandbox,
|
|
&permission_profile,
|
|
&sandbox_policy_cwd,
|
|
use_windows_elevated_backend,
|
|
&env,
|
|
)
|
|
} else {
|
|
resolve_windows_restricted_token_filesystem_overrides(
|
|
sandbox,
|
|
&permission_profile,
|
|
&sandbox_policy_cwd,
|
|
windows_sandbox_level,
|
|
)
|
|
}
|
|
.map_err(|error| {
|
|
CodexErr::UnsupportedOperation(
|
|
truncate_middle_with_token_budget(&error, /*max_tokens*/ 900).0,
|
|
)
|
|
})?
|
|
} else {
|
|
None
|
|
};
|
|
let network_sandbox_policy = permission_profile.network_sandbox_policy();
|
|
if !network_sandbox_policy.is_enabled() {
|
|
env.insert(
|
|
CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR.to_string(),
|
|
"1".to_string(),
|
|
);
|
|
}
|
|
#[cfg(target_os = "macos")]
|
|
if sandbox == SandboxType::MacosSeatbelt {
|
|
env.insert(CODEX_SANDBOX_ENV_VAR.to_string(), "seatbelt".to_string());
|
|
}
|
|
Ok(Self {
|
|
command,
|
|
cwd,
|
|
env,
|
|
exec_server_env_config: None,
|
|
exec_server_shell_snapshot: None,
|
|
network,
|
|
network_environment_id,
|
|
expiration,
|
|
capture_policy,
|
|
sandbox,
|
|
windows_sandbox_policy_cwd,
|
|
windows_sandbox_workspace_roots,
|
|
windows_sandbox_level,
|
|
permission_profile,
|
|
windows_sandbox_filesystem_overrides,
|
|
arg0,
|
|
exec_server_sandbox: None,
|
|
exec_server_enforce_managed_network: false,
|
|
exec_server_managed_network: None,
|
|
exec_server_network_proxy: None,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub async fn execute_env(
|
|
exec_request: ExecRequest,
|
|
stdout_stream: Option<StdoutStream>,
|
|
) -> codex_protocol::error::Result<ExecToolCallOutput> {
|
|
execute_exec_request(exec_request, stdout_stream, /*after_spawn*/ None).await
|
|
}
|
|
|
|
pub async fn execute_exec_request_with_after_spawn(
|
|
exec_request: ExecRequest,
|
|
stdout_stream: Option<StdoutStream>,
|
|
after_spawn: Option<Box<dyn FnOnce() + Send>>,
|
|
) -> codex_protocol::error::Result<ExecToolCallOutput> {
|
|
execute_exec_request(exec_request, stdout_stream, after_spawn).await
|
|
}
|