mirror of
https://github.com/openai/codex.git
synced 2026-05-04 19:36:45 +00:00
sandboxing: plumb split sandbox policies through runtime
This commit is contained in:
@@ -1534,9 +1534,19 @@ impl CodexMessageProcessor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let requested_policy = params.sandbox_policy.map(|policy| policy.to_core());
|
let requested_policy = params.sandbox_policy.map(|policy| policy.to_core());
|
||||||
let effective_policy = match requested_policy {
|
let (
|
||||||
|
effective_policy,
|
||||||
|
effective_file_system_sandbox_policy,
|
||||||
|
effective_network_sandbox_policy,
|
||||||
|
) = match requested_policy {
|
||||||
Some(policy) => match self.config.permissions.sandbox_policy.can_set(&policy) {
|
Some(policy) => match self.config.permissions.sandbox_policy.can_set(&policy) {
|
||||||
Ok(()) => policy,
|
Ok(()) => {
|
||||||
|
let file_system_sandbox_policy =
|
||||||
|
codex_protocol::protocol::FileSystemSandboxPolicy::from(&policy);
|
||||||
|
let network_sandbox_policy =
|
||||||
|
codex_protocol::protocol::NetworkSandboxPolicy::from(&policy);
|
||||||
|
(policy, file_system_sandbox_policy, network_sandbox_policy)
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let error = JSONRPCErrorError {
|
let error = JSONRPCErrorError {
|
||||||
code: INVALID_REQUEST_ERROR_CODE,
|
code: INVALID_REQUEST_ERROR_CODE,
|
||||||
@@ -1547,7 +1557,11 @@ impl CodexMessageProcessor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
None => self.config.permissions.sandbox_policy.get().clone(),
|
None => (
|
||||||
|
self.config.permissions.sandbox_policy.get().clone(),
|
||||||
|
self.config.permissions.file_system_sandbox_policy.clone(),
|
||||||
|
self.config.permissions.network_sandbox_policy,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let codex_linux_sandbox_exe = self.arg0_paths.codex_linux_sandbox_exe.clone();
|
let codex_linux_sandbox_exe = self.arg0_paths.codex_linux_sandbox_exe.clone();
|
||||||
@@ -1562,6 +1576,8 @@ impl CodexMessageProcessor {
|
|||||||
match codex_core::exec::process_exec_tool_call(
|
match codex_core::exec::process_exec_tool_call(
|
||||||
exec_params,
|
exec_params,
|
||||||
&effective_policy,
|
&effective_policy,
|
||||||
|
&effective_file_system_sandbox_policy,
|
||||||
|
effective_network_sandbox_policy,
|
||||||
sandbox_cwd.as_path(),
|
sandbox_cwd.as_path(),
|
||||||
&codex_linux_sandbox_exe,
|
&codex_linux_sandbox_exe,
|
||||||
use_linux_sandbox_bwrap,
|
use_linux_sandbox_bwrap,
|
||||||
|
|||||||
@@ -223,10 +223,12 @@ use crate::protocol::ErrorEvent;
|
|||||||
use crate::protocol::Event;
|
use crate::protocol::Event;
|
||||||
use crate::protocol::EventMsg;
|
use crate::protocol::EventMsg;
|
||||||
use crate::protocol::ExecApprovalRequestEvent;
|
use crate::protocol::ExecApprovalRequestEvent;
|
||||||
|
use crate::protocol::FileSystemSandboxPolicy;
|
||||||
use crate::protocol::McpServerRefreshConfig;
|
use crate::protocol::McpServerRefreshConfig;
|
||||||
use crate::protocol::ModelRerouteEvent;
|
use crate::protocol::ModelRerouteEvent;
|
||||||
use crate::protocol::ModelRerouteReason;
|
use crate::protocol::ModelRerouteReason;
|
||||||
use crate::protocol::NetworkApprovalContext;
|
use crate::protocol::NetworkApprovalContext;
|
||||||
|
use crate::protocol::NetworkSandboxPolicy;
|
||||||
use crate::protocol::Op;
|
use crate::protocol::Op;
|
||||||
use crate::protocol::PlanDeltaEvent;
|
use crate::protocol::PlanDeltaEvent;
|
||||||
use crate::protocol::RateLimitSnapshot;
|
use crate::protocol::RateLimitSnapshot;
|
||||||
@@ -488,6 +490,8 @@ impl Codex {
|
|||||||
compact_prompt: config.compact_prompt.clone(),
|
compact_prompt: config.compact_prompt.clone(),
|
||||||
approval_policy: config.permissions.approval_policy.clone(),
|
approval_policy: config.permissions.approval_policy.clone(),
|
||||||
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
cwd: config.cwd.clone(),
|
cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
@@ -683,6 +687,8 @@ pub(crate) struct TurnContext {
|
|||||||
pub(crate) personality: Option<Personality>,
|
pub(crate) personality: Option<Personality>,
|
||||||
pub(crate) approval_policy: Constrained<AskForApproval>,
|
pub(crate) approval_policy: Constrained<AskForApproval>,
|
||||||
pub(crate) sandbox_policy: Constrained<SandboxPolicy>,
|
pub(crate) sandbox_policy: Constrained<SandboxPolicy>,
|
||||||
|
pub(crate) file_system_sandbox_policy: FileSystemSandboxPolicy,
|
||||||
|
pub(crate) network_sandbox_policy: NetworkSandboxPolicy,
|
||||||
pub(crate) network: Option<NetworkProxy>,
|
pub(crate) network: Option<NetworkProxy>,
|
||||||
pub(crate) windows_sandbox_level: WindowsSandboxLevel,
|
pub(crate) windows_sandbox_level: WindowsSandboxLevel,
|
||||||
pub(crate) shell_environment_policy: ShellEnvironmentPolicy,
|
pub(crate) shell_environment_policy: ShellEnvironmentPolicy,
|
||||||
@@ -773,6 +779,8 @@ impl TurnContext {
|
|||||||
personality: self.personality,
|
personality: self.personality,
|
||||||
approval_policy: self.approval_policy.clone(),
|
approval_policy: self.approval_policy.clone(),
|
||||||
sandbox_policy: self.sandbox_policy.clone(),
|
sandbox_policy: self.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: self.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: self.network_sandbox_policy,
|
||||||
network: self.network.clone(),
|
network: self.network.clone(),
|
||||||
windows_sandbox_level: self.windows_sandbox_level,
|
windows_sandbox_level: self.windows_sandbox_level,
|
||||||
shell_environment_policy: self.shell_environment_policy.clone(),
|
shell_environment_policy: self.shell_environment_policy.clone(),
|
||||||
@@ -878,6 +886,8 @@ pub(crate) struct SessionConfiguration {
|
|||||||
approval_policy: Constrained<AskForApproval>,
|
approval_policy: Constrained<AskForApproval>,
|
||||||
/// How to sandbox commands executed in the system
|
/// How to sandbox commands executed in the system
|
||||||
sandbox_policy: Constrained<SandboxPolicy>,
|
sandbox_policy: Constrained<SandboxPolicy>,
|
||||||
|
file_system_sandbox_policy: FileSystemSandboxPolicy,
|
||||||
|
network_sandbox_policy: NetworkSandboxPolicy,
|
||||||
windows_sandbox_level: WindowsSandboxLevel,
|
windows_sandbox_level: WindowsSandboxLevel,
|
||||||
|
|
||||||
/// Working directory that should be treated as the *root* of the
|
/// Working directory that should be treated as the *root* of the
|
||||||
@@ -944,6 +954,10 @@ impl SessionConfiguration {
|
|||||||
}
|
}
|
||||||
if let Some(sandbox_policy) = updates.sandbox_policy.clone() {
|
if let Some(sandbox_policy) = updates.sandbox_policy.clone() {
|
||||||
next_configuration.sandbox_policy.set(sandbox_policy)?;
|
next_configuration.sandbox_policy.set(sandbox_policy)?;
|
||||||
|
next_configuration.file_system_sandbox_policy =
|
||||||
|
FileSystemSandboxPolicy::from(next_configuration.sandbox_policy.get());
|
||||||
|
next_configuration.network_sandbox_policy =
|
||||||
|
NetworkSandboxPolicy::from(next_configuration.sandbox_policy.get());
|
||||||
}
|
}
|
||||||
if let Some(windows_sandbox_level) = updates.windows_sandbox_level {
|
if let Some(windows_sandbox_level) = updates.windows_sandbox_level {
|
||||||
next_configuration.windows_sandbox_level = windows_sandbox_level;
|
next_configuration.windows_sandbox_level = windows_sandbox_level;
|
||||||
@@ -1156,6 +1170,8 @@ impl Session {
|
|||||||
personality: session_configuration.personality,
|
personality: session_configuration.personality,
|
||||||
approval_policy: session_configuration.approval_policy.clone(),
|
approval_policy: session_configuration.approval_policy.clone(),
|
||||||
sandbox_policy: session_configuration.sandbox_policy.clone(),
|
sandbox_policy: session_configuration.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: session_configuration.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: session_configuration.network_sandbox_policy,
|
||||||
network,
|
network,
|
||||||
windows_sandbox_level: session_configuration.windows_sandbox_level,
|
windows_sandbox_level: session_configuration.windows_sandbox_level,
|
||||||
shell_environment_policy: per_turn_config.permissions.shell_environment_policy.clone(),
|
shell_environment_policy: per_turn_config.permissions.shell_environment_policy.clone(),
|
||||||
@@ -4983,6 +4999,8 @@ async fn spawn_review_thread(
|
|||||||
personality: parent_turn_context.personality,
|
personality: parent_turn_context.personality,
|
||||||
approval_policy: parent_turn_context.approval_policy.clone(),
|
approval_policy: parent_turn_context.approval_policy.clone(),
|
||||||
sandbox_policy: parent_turn_context.sandbox_policy.clone(),
|
sandbox_policy: parent_turn_context.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: parent_turn_context.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: parent_turn_context.network_sandbox_policy,
|
||||||
network: parent_turn_context.network.clone(),
|
network: parent_turn_context.network.clone(),
|
||||||
windows_sandbox_level: parent_turn_context.windows_sandbox_level,
|
windows_sandbox_level: parent_turn_context.windows_sandbox_level,
|
||||||
shell_environment_policy: parent_turn_context.shell_environment_policy.clone(),
|
shell_environment_policy: parent_turn_context.shell_environment_policy.clone(),
|
||||||
|
|||||||
@@ -1416,6 +1416,8 @@ async fn set_rate_limits_retains_previous_credits() {
|
|||||||
compact_prompt: config.compact_prompt.clone(),
|
compact_prompt: config.compact_prompt.clone(),
|
||||||
approval_policy: config.permissions.approval_policy.clone(),
|
approval_policy: config.permissions.approval_policy.clone(),
|
||||||
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
cwd: config.cwd.clone(),
|
cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
@@ -1510,6 +1512,8 @@ async fn set_rate_limits_updates_plan_type_when_present() {
|
|||||||
compact_prompt: config.compact_prompt.clone(),
|
compact_prompt: config.compact_prompt.clone(),
|
||||||
approval_policy: config.permissions.approval_policy.clone(),
|
approval_policy: config.permissions.approval_policy.clone(),
|
||||||
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
cwd: config.cwd.clone(),
|
cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
@@ -1862,6 +1866,8 @@ pub(crate) async fn make_session_configuration_for_tests() -> SessionConfigurati
|
|||||||
compact_prompt: config.compact_prompt.clone(),
|
compact_prompt: config.compact_prompt.clone(),
|
||||||
approval_policy: config.permissions.approval_policy.clone(),
|
approval_policy: config.permissions.approval_policy.clone(),
|
||||||
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
cwd: config.cwd.clone(),
|
cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
@@ -1919,6 +1925,8 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
|
|||||||
compact_prompt: config.compact_prompt.clone(),
|
compact_prompt: config.compact_prompt.clone(),
|
||||||
approval_policy: config.permissions.approval_policy.clone(),
|
approval_policy: config.permissions.approval_policy.clone(),
|
||||||
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
cwd: config.cwd.clone(),
|
cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
@@ -2009,6 +2017,8 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
|||||||
compact_prompt: config.compact_prompt.clone(),
|
compact_prompt: config.compact_prompt.clone(),
|
||||||
approval_policy: config.permissions.approval_policy.clone(),
|
approval_policy: config.permissions.approval_policy.clone(),
|
||||||
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
cwd: config.cwd.clone(),
|
cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
@@ -2414,6 +2424,8 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
|
|||||||
compact_prompt: config.compact_prompt.clone(),
|
compact_prompt: config.compact_prompt.clone(),
|
||||||
approval_policy: config.permissions.approval_policy.clone(),
|
approval_policy: config.permissions.approval_policy.clone(),
|
||||||
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
sandbox_policy: config.permissions.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: config.permissions.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: config.permissions.network_sandbox_policy,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||||
cwd: config.cwd.clone(),
|
cwd: config.cwd.clone(),
|
||||||
codex_home: config.codex_home.clone(),
|
codex_home: config.codex_home.clone(),
|
||||||
@@ -3841,11 +3853,15 @@ async fn rejects_escalated_permissions_when_policy_not_on_request() {
|
|||||||
|
|
||||||
// Now retry the same command WITHOUT escalated permissions; should succeed.
|
// Now retry the same command WITHOUT escalated permissions; should succeed.
|
||||||
// Force DangerFullAccess to avoid platform sandbox dependencies in tests.
|
// Force DangerFullAccess to avoid platform sandbox dependencies in tests.
|
||||||
Arc::get_mut(&mut turn_context)
|
let turn_context_mut = Arc::get_mut(&mut turn_context).expect("unique turn context Arc");
|
||||||
.expect("unique turn context Arc")
|
turn_context_mut
|
||||||
.sandbox_policy
|
.sandbox_policy
|
||||||
.set(SandboxPolicy::DangerFullAccess)
|
.set(SandboxPolicy::DangerFullAccess)
|
||||||
.expect("test setup should allow updating sandbox policy");
|
.expect("test setup should allow updating sandbox policy");
|
||||||
|
turn_context_mut.file_system_sandbox_policy =
|
||||||
|
FileSystemSandboxPolicy::from(turn_context_mut.sandbox_policy.get());
|
||||||
|
turn_context_mut.network_sandbox_policy =
|
||||||
|
NetworkSandboxPolicy::from(turn_context_mut.sandbox_policy.get());
|
||||||
|
|
||||||
let resp2 = handler
|
let resp2 = handler
|
||||||
.handle(ToolInvocation {
|
.handle(ToolInvocation {
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ use crate::protocol::Event;
|
|||||||
use crate::protocol::EventMsg;
|
use crate::protocol::EventMsg;
|
||||||
use crate::protocol::ExecCommandOutputDeltaEvent;
|
use crate::protocol::ExecCommandOutputDeltaEvent;
|
||||||
use crate::protocol::ExecOutputStream;
|
use crate::protocol::ExecOutputStream;
|
||||||
|
use crate::protocol::FileSystemSandboxKind;
|
||||||
|
use crate::protocol::FileSystemSandboxPolicy;
|
||||||
|
use crate::protocol::NetworkSandboxPolicy;
|
||||||
use crate::protocol::SandboxPolicy;
|
use crate::protocol::SandboxPolicy;
|
||||||
use crate::sandboxing::CommandSpec;
|
use crate::sandboxing::CommandSpec;
|
||||||
use crate::sandboxing::ExecRequest;
|
use crate::sandboxing::ExecRequest;
|
||||||
@@ -149,9 +152,12 @@ pub struct StdoutStream {
|
|||||||
pub tx_event: Sender<Event>,
|
pub tx_event: Sender<Event>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub async fn process_exec_tool_call(
|
pub async fn process_exec_tool_call(
|
||||||
params: ExecParams,
|
params: ExecParams,
|
||||||
sandbox_policy: &SandboxPolicy,
|
sandbox_policy: &SandboxPolicy,
|
||||||
|
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||||
|
network_sandbox_policy: NetworkSandboxPolicy,
|
||||||
sandbox_cwd: &Path,
|
sandbox_cwd: &Path,
|
||||||
codex_linux_sandbox_exe: &Option<PathBuf>,
|
codex_linux_sandbox_exe: &Option<PathBuf>,
|
||||||
use_linux_sandbox_bwrap: bool,
|
use_linux_sandbox_bwrap: bool,
|
||||||
@@ -159,8 +165,8 @@ pub async fn process_exec_tool_call(
|
|||||||
) -> Result<ExecToolCallOutput> {
|
) -> Result<ExecToolCallOutput> {
|
||||||
let windows_sandbox_level = params.windows_sandbox_level;
|
let windows_sandbox_level = params.windows_sandbox_level;
|
||||||
let enforce_managed_network = params.network.is_some();
|
let enforce_managed_network = params.network.is_some();
|
||||||
let sandbox_type = match &sandbox_policy {
|
let sandbox_type = match file_system_sandbox_policy.kind {
|
||||||
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => {
|
FileSystemSandboxKind::Unrestricted | FileSystemSandboxKind::ExternalSandbox => {
|
||||||
if enforce_managed_network {
|
if enforce_managed_network {
|
||||||
get_platform_sandbox(
|
get_platform_sandbox(
|
||||||
windows_sandbox_level
|
windows_sandbox_level
|
||||||
@@ -215,6 +221,8 @@ pub async fn process_exec_tool_call(
|
|||||||
.transform(crate::sandboxing::SandboxTransformRequest {
|
.transform(crate::sandboxing::SandboxTransformRequest {
|
||||||
spec,
|
spec,
|
||||||
policy: sandbox_policy,
|
policy: sandbox_policy,
|
||||||
|
file_system_policy: file_system_sandbox_policy,
|
||||||
|
network_policy: network_sandbox_policy,
|
||||||
sandbox: sandbox_type,
|
sandbox: sandbox_type,
|
||||||
enforce_managed_network,
|
enforce_managed_network,
|
||||||
network: network.as_ref(),
|
network: network.as_ref(),
|
||||||
@@ -247,9 +255,12 @@ pub(crate) async fn execute_exec_request(
|
|||||||
windows_sandbox_level,
|
windows_sandbox_level,
|
||||||
sandbox_permissions,
|
sandbox_permissions,
|
||||||
sandbox_policy: _sandbox_policy_from_env,
|
sandbox_policy: _sandbox_policy_from_env,
|
||||||
|
file_system_sandbox_policy,
|
||||||
|
network_sandbox_policy,
|
||||||
justification,
|
justification,
|
||||||
arg0,
|
arg0,
|
||||||
} = exec_request;
|
} = exec_request;
|
||||||
|
let _ = _sandbox_policy_from_env;
|
||||||
|
|
||||||
let params = ExecParams {
|
let params = ExecParams {
|
||||||
command,
|
command,
|
||||||
@@ -264,7 +275,16 @@ pub(crate) async fn execute_exec_request(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let raw_output_result = exec(params, sandbox, sandbox_policy, stdout_stream, after_spawn).await;
|
let raw_output_result = exec(
|
||||||
|
params,
|
||||||
|
sandbox,
|
||||||
|
sandbox_policy,
|
||||||
|
&file_system_sandbox_policy,
|
||||||
|
network_sandbox_policy,
|
||||||
|
stdout_stream,
|
||||||
|
after_spawn,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
let duration = start.elapsed();
|
let duration = start.elapsed();
|
||||||
finalize_exec_result(raw_output_result, sandbox, duration)
|
finalize_exec_result(raw_output_result, sandbox, duration)
|
||||||
}
|
}
|
||||||
@@ -693,16 +713,17 @@ async fn exec(
|
|||||||
params: ExecParams,
|
params: ExecParams,
|
||||||
sandbox: SandboxType,
|
sandbox: SandboxType,
|
||||||
sandbox_policy: &SandboxPolicy,
|
sandbox_policy: &SandboxPolicy,
|
||||||
|
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||||
|
network_sandbox_policy: NetworkSandboxPolicy,
|
||||||
stdout_stream: Option<StdoutStream>,
|
stdout_stream: Option<StdoutStream>,
|
||||||
after_spawn: Option<Box<dyn FnOnce() + Send>>,
|
after_spawn: Option<Box<dyn FnOnce() + Send>>,
|
||||||
) -> Result<RawExecToolCallOutput> {
|
) -> Result<RawExecToolCallOutput> {
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
if sandbox == SandboxType::WindowsRestrictedToken
|
if should_use_windows_restricted_token_sandbox(
|
||||||
&& !matches!(
|
sandbox,
|
||||||
sandbox_policy,
|
sandbox_policy,
|
||||||
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. }
|
file_system_sandbox_policy,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
return exec_windows_sandbox(params, sandbox_policy).await;
|
return exec_windows_sandbox(params, sandbox_policy).await;
|
||||||
}
|
}
|
||||||
let ExecParams {
|
let ExecParams {
|
||||||
@@ -731,7 +752,7 @@ async fn exec(
|
|||||||
args: args.into(),
|
args: args.into(),
|
||||||
arg0: arg0_ref,
|
arg0: arg0_ref,
|
||||||
cwd,
|
cwd,
|
||||||
sandbox_policy,
|
network_sandbox_policy,
|
||||||
// The environment already has attempt-scoped proxy settings from
|
// The environment already has attempt-scoped proxy settings from
|
||||||
// apply_to_env_for_attempt above. Passing network here would reapply
|
// apply_to_env_for_attempt above. Passing network here would reapply
|
||||||
// non-attempt proxy vars and drop attempt correlation metadata.
|
// non-attempt proxy vars and drop attempt correlation metadata.
|
||||||
@@ -746,6 +767,20 @@ async fn exec(
|
|||||||
consume_truncated_output(child, expiration, stdout_stream).await
|
consume_truncated_output(child, expiration, stdout_stream).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||||
|
fn should_use_windows_restricted_token_sandbox(
|
||||||
|
sandbox: SandboxType,
|
||||||
|
sandbox_policy: &SandboxPolicy,
|
||||||
|
file_system_sandbox_policy: &FileSystemSandboxPolicy,
|
||||||
|
) -> bool {
|
||||||
|
sandbox == SandboxType::WindowsRestrictedToken
|
||||||
|
&& file_system_sandbox_policy.kind == FileSystemSandboxKind::Restricted
|
||||||
|
&& !matches!(
|
||||||
|
sandbox_policy,
|
||||||
|
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Consumes the output of a child process, truncating it so it is suitable for
|
/// Consumes the output of a child process, truncating it so it is suitable for
|
||||||
/// use as the output of a `shell` tool call. Also enforces specified timeout.
|
/// use as the output of a `shell` tool call. Also enforces specified timeout.
|
||||||
async fn consume_truncated_output(
|
async fn consume_truncated_output(
|
||||||
@@ -1098,6 +1133,38 @@ mod tests {
|
|||||||
assert_eq!(aggregated.truncated_after_lines, None);
|
assert_eq!(aggregated.truncated_after_lines, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn windows_restricted_token_skips_external_sandbox_policies() {
|
||||||
|
let policy = SandboxPolicy::ExternalSandbox {
|
||||||
|
network_access: codex_protocol::protocol::NetworkAccess::Restricted,
|
||||||
|
};
|
||||||
|
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
should_use_windows_restricted_token_sandbox(
|
||||||
|
SandboxType::WindowsRestrictedToken,
|
||||||
|
&policy,
|
||||||
|
&file_system_policy,
|
||||||
|
),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn windows_restricted_token_runs_for_legacy_restricted_policies() {
|
||||||
|
let policy = SandboxPolicy::new_read_only_policy();
|
||||||
|
let file_system_policy = FileSystemSandboxPolicy::restricted(vec![]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
should_use_windows_restricted_token_sandbox(
|
||||||
|
SandboxType::WindowsRestrictedToken,
|
||||||
|
&policy,
|
||||||
|
&file_system_policy,
|
||||||
|
),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[test]
|
#[test]
|
||||||
fn sandbox_detection_flags_sigsys_exit_code() {
|
fn sandbox_detection_flags_sigsys_exit_code() {
|
||||||
@@ -1140,6 +1207,8 @@ mod tests {
|
|||||||
params,
|
params,
|
||||||
SandboxType::None,
|
SandboxType::None,
|
||||||
&SandboxPolicy::new_read_only_policy(),
|
&SandboxPolicy::new_read_only_policy(),
|
||||||
|
&FileSystemSandboxPolicy::from(&SandboxPolicy::new_read_only_policy()),
|
||||||
|
NetworkSandboxPolicy::Restricted,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
@@ -1196,6 +1265,8 @@ mod tests {
|
|||||||
let result = process_exec_tool_call(
|
let result = process_exec_tool_call(
|
||||||
params,
|
params,
|
||||||
&SandboxPolicy::DangerFullAccess,
|
&SandboxPolicy::DangerFullAccess,
|
||||||
|
&FileSystemSandboxPolicy::from(&SandboxPolicy::DangerFullAccess),
|
||||||
|
NetworkSandboxPolicy::Enabled,
|
||||||
cwd.as_path(),
|
cwd.as_path(),
|
||||||
&None,
|
&None,
|
||||||
false,
|
false,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::protocol::NetworkSandboxPolicy;
|
||||||
use crate::protocol::SandboxPolicy;
|
use crate::protocol::SandboxPolicy;
|
||||||
use crate::spawn::SpawnChildRequest;
|
use crate::spawn::SpawnChildRequest;
|
||||||
use crate::spawn::StdioPolicy;
|
use crate::spawn::StdioPolicy;
|
||||||
@@ -44,7 +45,7 @@ where
|
|||||||
args,
|
args,
|
||||||
arg0,
|
arg0,
|
||||||
cwd: command_cwd,
|
cwd: command_cwd,
|
||||||
sandbox_policy,
|
network_sandbox_policy: NetworkSandboxPolicy::from(sandbox_policy),
|
||||||
network,
|
network,
|
||||||
stdio_policy,
|
stdio_policy,
|
||||||
env,
|
env,
|
||||||
|
|||||||
@@ -15,6 +15,13 @@ use crate::exec::StdoutStream;
|
|||||||
use crate::exec::execute_exec_request;
|
use crate::exec::execute_exec_request;
|
||||||
use crate::landlock::allow_network_for_proxy;
|
use crate::landlock::allow_network_for_proxy;
|
||||||
use crate::landlock::create_linux_sandbox_command_args;
|
use crate::landlock::create_linux_sandbox_command_args;
|
||||||
|
use crate::protocol::FileSystemAccessMode;
|
||||||
|
use crate::protocol::FileSystemPath;
|
||||||
|
use crate::protocol::FileSystemSandboxEntry;
|
||||||
|
use crate::protocol::FileSystemSandboxKind;
|
||||||
|
use crate::protocol::FileSystemSandboxPolicy;
|
||||||
|
use crate::protocol::FileSystemSpecialPathKind;
|
||||||
|
use crate::protocol::NetworkSandboxPolicy;
|
||||||
use crate::protocol::SandboxPolicy;
|
use crate::protocol::SandboxPolicy;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use crate::seatbelt::MACOS_PATH_TO_SEATBELT_EXECUTABLE;
|
use crate::seatbelt::MACOS_PATH_TO_SEATBELT_EXECUTABLE;
|
||||||
@@ -30,6 +37,7 @@ use codex_protocol::models::FileSystemPermissions;
|
|||||||
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
|
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
pub use codex_protocol::models::SandboxPermissions;
|
pub use codex_protocol::models::SandboxPermissions;
|
||||||
|
use codex_protocol::protocol::NetworkAccess;
|
||||||
use codex_protocol::protocol::ReadOnlyAccess;
|
use codex_protocol::protocol::ReadOnlyAccess;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
use dunce::canonicalize;
|
use dunce::canonicalize;
|
||||||
@@ -62,6 +70,8 @@ pub struct ExecRequest {
|
|||||||
pub windows_sandbox_level: WindowsSandboxLevel,
|
pub windows_sandbox_level: WindowsSandboxLevel,
|
||||||
pub sandbox_permissions: SandboxPermissions,
|
pub sandbox_permissions: SandboxPermissions,
|
||||||
pub sandbox_policy: SandboxPolicy,
|
pub sandbox_policy: SandboxPolicy,
|
||||||
|
pub file_system_sandbox_policy: FileSystemSandboxPolicy,
|
||||||
|
pub network_sandbox_policy: NetworkSandboxPolicy,
|
||||||
pub justification: Option<String>,
|
pub justification: Option<String>,
|
||||||
pub arg0: Option<String>,
|
pub arg0: Option<String>,
|
||||||
}
|
}
|
||||||
@@ -72,6 +82,8 @@ pub struct ExecRequest {
|
|||||||
pub(crate) struct SandboxTransformRequest<'a> {
|
pub(crate) struct SandboxTransformRequest<'a> {
|
||||||
pub spec: CommandSpec,
|
pub spec: CommandSpec,
|
||||||
pub policy: &'a SandboxPolicy,
|
pub policy: &'a SandboxPolicy,
|
||||||
|
pub file_system_policy: &'a FileSystemSandboxPolicy,
|
||||||
|
pub network_policy: NetworkSandboxPolicy,
|
||||||
pub sandbox: SandboxType,
|
pub sandbox: SandboxType,
|
||||||
pub enforce_managed_network: bool,
|
pub enforce_managed_network: bool,
|
||||||
// TODO(viyatb): Evaluate switching this to Option<Arc<NetworkProxy>>
|
// TODO(viyatb): Evaluate switching this to Option<Arc<NetworkProxy>>
|
||||||
@@ -203,6 +215,41 @@ fn additional_permission_roots(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(not(test), allow(dead_code))]
|
||||||
|
fn merge_file_system_policy_with_additional_permissions(
|
||||||
|
file_system_policy: &FileSystemSandboxPolicy,
|
||||||
|
extra_reads: Vec<AbsolutePathBuf>,
|
||||||
|
extra_writes: Vec<AbsolutePathBuf>,
|
||||||
|
) -> FileSystemSandboxPolicy {
|
||||||
|
match file_system_policy.kind {
|
||||||
|
FileSystemSandboxKind::Restricted => {
|
||||||
|
let mut merged_policy = file_system_policy.clone();
|
||||||
|
for path in extra_reads {
|
||||||
|
let entry = FileSystemSandboxEntry {
|
||||||
|
path: FileSystemPath::Path { path },
|
||||||
|
access: FileSystemAccessMode::Read,
|
||||||
|
};
|
||||||
|
if !merged_policy.entries.contains(&entry) {
|
||||||
|
merged_policy.entries.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for path in extra_writes {
|
||||||
|
let entry = FileSystemSandboxEntry {
|
||||||
|
path: FileSystemPath::Path { path },
|
||||||
|
access: FileSystemAccessMode::Write,
|
||||||
|
};
|
||||||
|
if !merged_policy.entries.contains(&entry) {
|
||||||
|
merged_policy.entries.push(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
merged_policy
|
||||||
|
}
|
||||||
|
FileSystemSandboxKind::Unrestricted | FileSystemSandboxKind::ExternalSandbox => {
|
||||||
|
file_system_policy.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn merge_read_only_access_with_additional_reads(
|
fn merge_read_only_access_with_additional_reads(
|
||||||
read_only_access: &ReadOnlyAccess,
|
read_only_access: &ReadOnlyAccess,
|
||||||
extra_reads: Vec<AbsolutePathBuf>,
|
extra_reads: Vec<AbsolutePathBuf>,
|
||||||
@@ -246,9 +293,17 @@ fn sandbox_policy_with_additional_permissions(
|
|||||||
let (extra_reads, extra_writes) = additional_permission_roots(additional_permissions);
|
let (extra_reads, extra_writes) = additional_permission_roots(additional_permissions);
|
||||||
|
|
||||||
match sandbox_policy {
|
match sandbox_policy {
|
||||||
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => {
|
SandboxPolicy::DangerFullAccess => SandboxPolicy::DangerFullAccess,
|
||||||
sandbox_policy.clone()
|
SandboxPolicy::ExternalSandbox { network_access } => SandboxPolicy::ExternalSandbox {
|
||||||
}
|
network_access: if merge_network_access(
|
||||||
|
network_access.is_enabled(),
|
||||||
|
additional_permissions,
|
||||||
|
) {
|
||||||
|
NetworkAccess::Enabled
|
||||||
|
} else {
|
||||||
|
NetworkAccess::Restricted
|
||||||
|
},
|
||||||
|
},
|
||||||
SandboxPolicy::WorkspaceWrite {
|
SandboxPolicy::WorkspaceWrite {
|
||||||
writable_roots,
|
writable_roots,
|
||||||
read_only_access,
|
read_only_access,
|
||||||
@@ -297,6 +352,36 @@ fn sandbox_policy_with_additional_permissions(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn should_require_platform_sandbox(
|
||||||
|
file_system_policy: &FileSystemSandboxPolicy,
|
||||||
|
network_policy: NetworkSandboxPolicy,
|
||||||
|
has_managed_network_requirements: bool,
|
||||||
|
) -> bool {
|
||||||
|
if has_managed_network_requirements {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !network_policy.is_enabled() {
|
||||||
|
return !matches!(
|
||||||
|
file_system_policy.kind,
|
||||||
|
FileSystemSandboxKind::ExternalSandbox
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
match file_system_policy.kind {
|
||||||
|
FileSystemSandboxKind::Restricted => !file_system_policy.entries.iter().any(|entry| {
|
||||||
|
entry.access == FileSystemAccessMode::Write
|
||||||
|
&& matches!(
|
||||||
|
&entry.path,
|
||||||
|
FileSystemPath::Special { value }
|
||||||
|
if value.kind == FileSystemSpecialPathKind::Root
|
||||||
|
&& value.subpath.is_none()
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
FileSystemSandboxKind::Unrestricted | FileSystemSandboxKind::ExternalSandbox => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct SandboxManager;
|
pub struct SandboxManager;
|
||||||
|
|
||||||
@@ -307,7 +392,8 @@ impl SandboxManager {
|
|||||||
|
|
||||||
pub(crate) fn select_initial(
|
pub(crate) fn select_initial(
|
||||||
&self,
|
&self,
|
||||||
policy: &SandboxPolicy,
|
file_system_policy: &FileSystemSandboxPolicy,
|
||||||
|
network_policy: NetworkSandboxPolicy,
|
||||||
pref: SandboxablePreference,
|
pref: SandboxablePreference,
|
||||||
windows_sandbox_level: WindowsSandboxLevel,
|
windows_sandbox_level: WindowsSandboxLevel,
|
||||||
has_managed_network_requirements: bool,
|
has_managed_network_requirements: bool,
|
||||||
@@ -322,22 +408,20 @@ impl SandboxManager {
|
|||||||
)
|
)
|
||||||
.unwrap_or(SandboxType::None)
|
.unwrap_or(SandboxType::None)
|
||||||
}
|
}
|
||||||
SandboxablePreference::Auto => match policy {
|
SandboxablePreference::Auto => {
|
||||||
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => {
|
if should_require_platform_sandbox(
|
||||||
if has_managed_network_requirements {
|
file_system_policy,
|
||||||
crate::safety::get_platform_sandbox(
|
network_policy,
|
||||||
windows_sandbox_level != WindowsSandboxLevel::Disabled,
|
has_managed_network_requirements,
|
||||||
)
|
) {
|
||||||
.unwrap_or(SandboxType::None)
|
crate::safety::get_platform_sandbox(
|
||||||
} else {
|
windows_sandbox_level != WindowsSandboxLevel::Disabled,
|
||||||
SandboxType::None
|
)
|
||||||
}
|
.unwrap_or(SandboxType::None)
|
||||||
|
} else {
|
||||||
|
SandboxType::None
|
||||||
}
|
}
|
||||||
_ => crate::safety::get_platform_sandbox(
|
}
|
||||||
windows_sandbox_level != WindowsSandboxLevel::Disabled,
|
|
||||||
)
|
|
||||||
.unwrap_or(SandboxType::None),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,6 +432,8 @@ impl SandboxManager {
|
|||||||
let SandboxTransformRequest {
|
let SandboxTransformRequest {
|
||||||
mut spec,
|
mut spec,
|
||||||
policy,
|
policy,
|
||||||
|
file_system_policy,
|
||||||
|
network_policy,
|
||||||
sandbox,
|
sandbox,
|
||||||
enforce_managed_network,
|
enforce_managed_network,
|
||||||
network,
|
network,
|
||||||
@@ -360,16 +446,38 @@ impl SandboxManager {
|
|||||||
} = request;
|
} = request;
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
let macos_seatbelt_profile_extensions = None;
|
let macos_seatbelt_profile_extensions = None;
|
||||||
let effective_permissions = EffectiveSandboxPermissions::new(
|
let additional_permissions = spec.additional_permissions.take();
|
||||||
|
let EffectiveSandboxPermissions {
|
||||||
|
sandbox_policy: effective_policy,
|
||||||
|
macos_seatbelt_profile_extensions: effective_macos_seatbelt_profile_extensions,
|
||||||
|
} = EffectiveSandboxPermissions::new(
|
||||||
policy,
|
policy,
|
||||||
macos_seatbelt_profile_extensions,
|
macos_seatbelt_profile_extensions,
|
||||||
spec.additional_permissions.as_ref(),
|
additional_permissions.as_ref(),
|
||||||
);
|
);
|
||||||
|
let (effective_file_system_policy, effective_network_policy) =
|
||||||
|
if let Some(additional_permissions) = additional_permissions {
|
||||||
|
let (extra_reads, extra_writes) =
|
||||||
|
additional_permission_roots(&additional_permissions);
|
||||||
|
let file_system_sandbox_policy =
|
||||||
|
if extra_reads.is_empty() && extra_writes.is_empty() {
|
||||||
|
file_system_policy.clone()
|
||||||
|
} else {
|
||||||
|
match file_system_policy.kind {
|
||||||
|
FileSystemSandboxKind::Restricted => {
|
||||||
|
FileSystemSandboxPolicy::from(&effective_policy)
|
||||||
|
}
|
||||||
|
FileSystemSandboxKind::Unrestricted
|
||||||
|
| FileSystemSandboxKind::ExternalSandbox => file_system_policy.clone(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let network_sandbox_policy = NetworkSandboxPolicy::from(&effective_policy);
|
||||||
|
(file_system_sandbox_policy, network_sandbox_policy)
|
||||||
|
} else {
|
||||||
|
(file_system_policy.clone(), network_policy)
|
||||||
|
};
|
||||||
let mut env = spec.env;
|
let mut env = spec.env;
|
||||||
if !effective_permissions
|
if !effective_network_policy.is_enabled() {
|
||||||
.sandbox_policy
|
|
||||||
.has_full_network_access()
|
|
||||||
{
|
|
||||||
env.insert(
|
env.insert(
|
||||||
CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR.to_string(),
|
CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR.to_string(),
|
||||||
"1".to_string(),
|
"1".to_string(),
|
||||||
@@ -388,13 +496,11 @@ impl SandboxManager {
|
|||||||
seatbelt_env.insert(CODEX_SANDBOX_ENV_VAR.to_string(), "seatbelt".to_string());
|
seatbelt_env.insert(CODEX_SANDBOX_ENV_VAR.to_string(), "seatbelt".to_string());
|
||||||
let mut args = create_seatbelt_command_args_with_extensions(
|
let mut args = create_seatbelt_command_args_with_extensions(
|
||||||
command.clone(),
|
command.clone(),
|
||||||
&effective_permissions.sandbox_policy,
|
&effective_policy,
|
||||||
sandbox_policy_cwd,
|
sandbox_policy_cwd,
|
||||||
enforce_managed_network,
|
enforce_managed_network,
|
||||||
network,
|
network,
|
||||||
effective_permissions
|
effective_macos_seatbelt_profile_extensions.as_ref(),
|
||||||
.macos_seatbelt_profile_extensions
|
|
||||||
.as_ref(),
|
|
||||||
);
|
);
|
||||||
let mut full_command = Vec::with_capacity(1 + args.len());
|
let mut full_command = Vec::with_capacity(1 + args.len());
|
||||||
full_command.push(MACOS_PATH_TO_SEATBELT_EXECUTABLE.to_string());
|
full_command.push(MACOS_PATH_TO_SEATBELT_EXECUTABLE.to_string());
|
||||||
@@ -409,7 +515,7 @@ impl SandboxManager {
|
|||||||
let allow_proxy_network = allow_network_for_proxy(enforce_managed_network);
|
let allow_proxy_network = allow_network_for_proxy(enforce_managed_network);
|
||||||
let mut args = create_linux_sandbox_command_args(
|
let mut args = create_linux_sandbox_command_args(
|
||||||
command.clone(),
|
command.clone(),
|
||||||
&effective_permissions.sandbox_policy,
|
&effective_policy,
|
||||||
sandbox_policy_cwd,
|
sandbox_policy_cwd,
|
||||||
use_linux_sandbox_bwrap,
|
use_linux_sandbox_bwrap,
|
||||||
allow_proxy_network,
|
allow_proxy_network,
|
||||||
@@ -444,7 +550,9 @@ impl SandboxManager {
|
|||||||
sandbox,
|
sandbox,
|
||||||
windows_sandbox_level,
|
windows_sandbox_level,
|
||||||
sandbox_permissions: spec.sandbox_permissions,
|
sandbox_permissions: spec.sandbox_permissions,
|
||||||
sandbox_policy: effective_permissions.sandbox_policy,
|
sandbox_policy: effective_policy,
|
||||||
|
file_system_sandbox_policy: effective_file_system_policy,
|
||||||
|
network_sandbox_policy: effective_network_policy,
|
||||||
justification: spec.justification,
|
justification: spec.justification,
|
||||||
arg0: arg0_override,
|
arg0: arg0_override,
|
||||||
})
|
})
|
||||||
@@ -477,9 +585,19 @@ mod tests {
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use super::EffectiveSandboxPermissions;
|
use super::EffectiveSandboxPermissions;
|
||||||
use super::SandboxManager;
|
use super::SandboxManager;
|
||||||
|
use super::merge_file_system_policy_with_additional_permissions;
|
||||||
use super::normalize_additional_permissions;
|
use super::normalize_additional_permissions;
|
||||||
use super::sandbox_policy_with_additional_permissions;
|
use super::sandbox_policy_with_additional_permissions;
|
||||||
|
use super::should_require_platform_sandbox;
|
||||||
use crate::exec::SandboxType;
|
use crate::exec::SandboxType;
|
||||||
|
use crate::protocol::FileSystemAccessMode;
|
||||||
|
use crate::protocol::FileSystemPath;
|
||||||
|
use crate::protocol::FileSystemSandboxEntry;
|
||||||
|
use crate::protocol::FileSystemSandboxPolicy;
|
||||||
|
use crate::protocol::FileSystemSpecialPath;
|
||||||
|
use crate::protocol::FileSystemSpecialPathKind;
|
||||||
|
use crate::protocol::NetworkAccess;
|
||||||
|
use crate::protocol::NetworkSandboxPolicy;
|
||||||
use crate::protocol::ReadOnlyAccess;
|
use crate::protocol::ReadOnlyAccess;
|
||||||
use crate::protocol::SandboxPolicy;
|
use crate::protocol::SandboxPolicy;
|
||||||
use crate::tools::sandboxing::SandboxablePreference;
|
use crate::tools::sandboxing::SandboxablePreference;
|
||||||
@@ -496,13 +614,15 @@ mod tests {
|
|||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
use dunce::canonicalize;
|
use dunce::canonicalize;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
use std::collections::HashMap;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
#[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();
|
||||||
let sandbox = manager.select_initial(
|
let sandbox = manager.select_initial(
|
||||||
&SandboxPolicy::DangerFullAccess,
|
&FileSystemSandboxPolicy::unrestricted(),
|
||||||
|
NetworkSandboxPolicy::Enabled,
|
||||||
SandboxablePreference::Auto,
|
SandboxablePreference::Auto,
|
||||||
WindowsSandboxLevel::Disabled,
|
WindowsSandboxLevel::Disabled,
|
||||||
false,
|
false,
|
||||||
@@ -515,7 +635,8 @@ mod tests {
|
|||||||
let manager = SandboxManager::new();
|
let manager = SandboxManager::new();
|
||||||
let expected = crate::safety::get_platform_sandbox(false).unwrap_or(SandboxType::None);
|
let expected = crate::safety::get_platform_sandbox(false).unwrap_or(SandboxType::None);
|
||||||
let sandbox = manager.select_initial(
|
let sandbox = manager.select_initial(
|
||||||
&SandboxPolicy::DangerFullAccess,
|
&FileSystemSandboxPolicy::unrestricted(),
|
||||||
|
NetworkSandboxPolicy::Enabled,
|
||||||
SandboxablePreference::Auto,
|
SandboxablePreference::Auto,
|
||||||
WindowsSandboxLevel::Disabled,
|
WindowsSandboxLevel::Disabled,
|
||||||
true,
|
true,
|
||||||
@@ -523,6 +644,107 @@ mod tests {
|
|||||||
assert_eq!(sandbox, expected);
|
assert_eq!(sandbox, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn restricted_file_system_uses_platform_sandbox_without_managed_network() {
|
||||||
|
let manager = SandboxManager::new();
|
||||||
|
let expected = crate::safety::get_platform_sandbox(false).unwrap_or(SandboxType::None);
|
||||||
|
let sandbox = manager.select_initial(
|
||||||
|
&FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||||
|
path: FileSystemPath::Special {
|
||||||
|
value: FileSystemSpecialPath {
|
||||||
|
kind: FileSystemSpecialPathKind::Root,
|
||||||
|
subpath: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
access: FileSystemAccessMode::Read,
|
||||||
|
}]),
|
||||||
|
NetworkSandboxPolicy::Enabled,
|
||||||
|
SandboxablePreference::Auto,
|
||||||
|
WindowsSandboxLevel::Disabled,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
assert_eq!(sandbox, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn full_access_restricted_policy_skips_platform_sandbox_when_network_is_enabled() {
|
||||||
|
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||||
|
path: FileSystemPath::Special {
|
||||||
|
value: FileSystemSpecialPath {
|
||||||
|
kind: FileSystemSpecialPathKind::Root,
|
||||||
|
subpath: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
access: FileSystemAccessMode::Write,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
should_require_platform_sandbox(&policy, NetworkSandboxPolicy::Enabled, false),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn full_access_restricted_policy_still_uses_platform_sandbox_for_restricted_network() {
|
||||||
|
let policy = FileSystemSandboxPolicy::restricted(vec![FileSystemSandboxEntry {
|
||||||
|
path: FileSystemPath::Special {
|
||||||
|
value: FileSystemSpecialPath {
|
||||||
|
kind: FileSystemSpecialPathKind::Root,
|
||||||
|
subpath: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
access: FileSystemAccessMode::Write,
|
||||||
|
}]);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
should_require_platform_sandbox(&policy, NetworkSandboxPolicy::Restricted, false),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transform_preserves_unrestricted_file_system_policy_for_restricted_network() {
|
||||||
|
let manager = SandboxManager::new();
|
||||||
|
let cwd = std::env::current_dir().expect("current dir");
|
||||||
|
let exec_request = manager
|
||||||
|
.transform(super::SandboxTransformRequest {
|
||||||
|
spec: super::CommandSpec {
|
||||||
|
program: "true".to_string(),
|
||||||
|
args: Vec::new(),
|
||||||
|
cwd: cwd.clone(),
|
||||||
|
env: HashMap::new(),
|
||||||
|
expiration: crate::exec::ExecExpiration::DefaultTimeout,
|
||||||
|
sandbox_permissions: super::SandboxPermissions::UseDefault,
|
||||||
|
additional_permissions: None,
|
||||||
|
justification: None,
|
||||||
|
},
|
||||||
|
policy: &SandboxPolicy::ExternalSandbox {
|
||||||
|
network_access: crate::protocol::NetworkAccess::Restricted,
|
||||||
|
},
|
||||||
|
file_system_policy: &FileSystemSandboxPolicy::unrestricted(),
|
||||||
|
network_policy: NetworkSandboxPolicy::Restricted,
|
||||||
|
sandbox: SandboxType::None,
|
||||||
|
enforce_managed_network: false,
|
||||||
|
network: None,
|
||||||
|
sandbox_policy_cwd: cwd.as_path(),
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
macos_seatbelt_profile_extensions: None,
|
||||||
|
codex_linux_sandbox_exe: None,
|
||||||
|
use_linux_sandbox_bwrap: false,
|
||||||
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
|
})
|
||||||
|
.expect("transform");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
exec_request.file_system_sandbox_policy,
|
||||||
|
FileSystemSandboxPolicy::unrestricted()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
exec_request.network_sandbox_policy,
|
||||||
|
NetworkSandboxPolicy::Restricted
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn normalize_additional_permissions_preserves_network() {
|
fn normalize_additional_permissions_preserves_network() {
|
||||||
let temp_dir = TempDir::new().expect("create temp dir");
|
let temp_dir = TempDir::new().expect("create temp dir");
|
||||||
@@ -624,7 +846,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
#[test]
|
#[test]
|
||||||
fn effective_permissions_merge_macos_extensions_with_additional_permissions() {
|
fn effective_permissions_merge_macos_extensions_with_additional_permissions() {
|
||||||
@@ -679,4 +900,141 @@ mod tests {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn external_sandbox_additional_permissions_can_enable_network() {
|
||||||
|
let temp_dir = TempDir::new().expect("create temp dir");
|
||||||
|
let path = AbsolutePathBuf::from_absolute_path(
|
||||||
|
canonicalize(temp_dir.path()).expect("canonicalize temp dir"),
|
||||||
|
)
|
||||||
|
.expect("absolute temp dir");
|
||||||
|
let policy = sandbox_policy_with_additional_permissions(
|
||||||
|
&SandboxPolicy::ExternalSandbox {
|
||||||
|
network_access: NetworkAccess::Restricted,
|
||||||
|
},
|
||||||
|
&PermissionProfile {
|
||||||
|
network: Some(NetworkPermissions {
|
||||||
|
enabled: Some(true),
|
||||||
|
}),
|
||||||
|
file_system: Some(FileSystemPermissions {
|
||||||
|
read: Some(vec![path]),
|
||||||
|
write: Some(Vec::new()),
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
policy,
|
||||||
|
SandboxPolicy::ExternalSandbox {
|
||||||
|
network_access: NetworkAccess::Enabled,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn transform_additional_permissions_enable_network_for_external_sandbox() {
|
||||||
|
let manager = SandboxManager::new();
|
||||||
|
let cwd = std::env::current_dir().expect("current dir");
|
||||||
|
let temp_dir = TempDir::new().expect("create temp dir");
|
||||||
|
let path = AbsolutePathBuf::from_absolute_path(
|
||||||
|
canonicalize(temp_dir.path()).expect("canonicalize temp dir"),
|
||||||
|
)
|
||||||
|
.expect("absolute temp dir");
|
||||||
|
let exec_request = manager
|
||||||
|
.transform(super::SandboxTransformRequest {
|
||||||
|
spec: super::CommandSpec {
|
||||||
|
program: "true".to_string(),
|
||||||
|
args: Vec::new(),
|
||||||
|
cwd: cwd.clone(),
|
||||||
|
env: HashMap::new(),
|
||||||
|
expiration: crate::exec::ExecExpiration::DefaultTimeout,
|
||||||
|
sandbox_permissions: super::SandboxPermissions::WithAdditionalPermissions,
|
||||||
|
additional_permissions: Some(PermissionProfile {
|
||||||
|
network: Some(NetworkPermissions {
|
||||||
|
enabled: Some(true),
|
||||||
|
}),
|
||||||
|
file_system: Some(FileSystemPermissions {
|
||||||
|
read: Some(vec![path]),
|
||||||
|
write: Some(Vec::new()),
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
justification: None,
|
||||||
|
},
|
||||||
|
policy: &SandboxPolicy::ExternalSandbox {
|
||||||
|
network_access: NetworkAccess::Restricted,
|
||||||
|
},
|
||||||
|
file_system_policy: &FileSystemSandboxPolicy::unrestricted(),
|
||||||
|
network_policy: NetworkSandboxPolicy::Restricted,
|
||||||
|
sandbox: SandboxType::None,
|
||||||
|
enforce_managed_network: false,
|
||||||
|
network: None,
|
||||||
|
sandbox_policy_cwd: cwd.as_path(),
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
macos_seatbelt_profile_extensions: None,
|
||||||
|
codex_linux_sandbox_exe: None,
|
||||||
|
use_linux_sandbox_bwrap: false,
|
||||||
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
|
})
|
||||||
|
.expect("transform");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
exec_request.sandbox_policy,
|
||||||
|
SandboxPolicy::ExternalSandbox {
|
||||||
|
network_access: NetworkAccess::Enabled,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
exec_request.network_sandbox_policy,
|
||||||
|
NetworkSandboxPolicy::Enabled
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn merge_file_system_policy_with_additional_permissions_preserves_unreadable_roots() {
|
||||||
|
let temp_dir = TempDir::new().expect("create temp dir");
|
||||||
|
let cwd = AbsolutePathBuf::from_absolute_path(
|
||||||
|
canonicalize(temp_dir.path()).expect("canonicalize temp dir"),
|
||||||
|
)
|
||||||
|
.expect("absolute temp dir");
|
||||||
|
let allowed_path = cwd.join("allowed").expect("allowed path");
|
||||||
|
let denied_path = cwd.join("denied").expect("denied path");
|
||||||
|
let merged_policy = merge_file_system_policy_with_additional_permissions(
|
||||||
|
&FileSystemSandboxPolicy::restricted(vec![
|
||||||
|
FileSystemSandboxEntry {
|
||||||
|
path: FileSystemPath::Special {
|
||||||
|
value: FileSystemSpecialPath {
|
||||||
|
kind: FileSystemSpecialPathKind::Root,
|
||||||
|
subpath: None,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
access: FileSystemAccessMode::Read,
|
||||||
|
},
|
||||||
|
FileSystemSandboxEntry {
|
||||||
|
path: FileSystemPath::Path {
|
||||||
|
path: denied_path.clone(),
|
||||||
|
},
|
||||||
|
access: FileSystemAccessMode::None,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
vec![allowed_path.clone()],
|
||||||
|
Vec::new(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
merged_policy.entries.contains(&FileSystemSandboxEntry {
|
||||||
|
path: FileSystemPath::Path { path: denied_path },
|
||||||
|
access: FileSystemAccessMode::None,
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
merged_policy.entries.contains(&FileSystemSandboxEntry {
|
||||||
|
path: FileSystemPath::Path { path: allowed_path },
|
||||||
|
access: FileSystemAccessMode::Read,
|
||||||
|
}),
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use tokio::process::Child;
|
|||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::protocol::NetworkSandboxPolicy;
|
||||||
use crate::protocol::SandboxPolicy;
|
use crate::protocol::SandboxPolicy;
|
||||||
use crate::seatbelt_permissions::MacOsSeatbeltProfileExtensions;
|
use crate::seatbelt_permissions::MacOsSeatbeltProfileExtensions;
|
||||||
use crate::seatbelt_permissions::build_seatbelt_extensions;
|
use crate::seatbelt_permissions::build_seatbelt_extensions;
|
||||||
@@ -51,7 +52,7 @@ pub async fn spawn_command_under_seatbelt(
|
|||||||
args,
|
args,
|
||||||
arg0,
|
arg0,
|
||||||
cwd: command_cwd,
|
cwd: command_cwd,
|
||||||
sandbox_policy,
|
network_sandbox_policy: NetworkSandboxPolicy::from(sandbox_policy),
|
||||||
network,
|
network,
|
||||||
stdio_policy,
|
stdio_policy,
|
||||||
env,
|
env,
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ use tokio::process::Child;
|
|||||||
use tokio::process::Command;
|
use tokio::process::Command;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use crate::protocol::SandboxPolicy;
|
use crate::protocol::NetworkSandboxPolicy;
|
||||||
|
|
||||||
/// Experimental environment variable that will be set to some non-empty value
|
/// Experimental environment variable that will be set to some non-empty value
|
||||||
/// if both of the following are true:
|
/// if both of the following are true:
|
||||||
///
|
///
|
||||||
/// 1. The process was spawned by Codex as part of a shell tool call.
|
/// 1. The process was spawned by Codex as part of a shell tool call.
|
||||||
/// 2. SandboxPolicy.has_full_network_access() was false for the tool call.
|
/// 2. NetworkSandboxPolicy is restricted for the tool call.
|
||||||
///
|
///
|
||||||
/// We may try to have just one environment variable for all sandboxing
|
/// We may try to have just one environment variable for all sandboxing
|
||||||
/// attributes, so this may change in the future.
|
/// attributes, so this may change in the future.
|
||||||
@@ -33,15 +33,15 @@ pub enum StdioPolicy {
|
|||||||
/// ensuring the args and environment variables used to create the `Command`
|
/// ensuring the args and environment variables used to create the `Command`
|
||||||
/// (and `Child`) honor the configuration.
|
/// (and `Child`) honor the configuration.
|
||||||
///
|
///
|
||||||
/// For now, we take `SandboxPolicy` as a parameter to spawn_child() because
|
/// For now, we take `NetworkSandboxPolicy` as a parameter to spawn_child()
|
||||||
/// we need to determine whether to set the
|
/// because we need to determine whether to set the
|
||||||
/// `CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR` environment variable.
|
/// `CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR` environment variable.
|
||||||
pub(crate) struct SpawnChildRequest<'a> {
|
pub(crate) struct SpawnChildRequest<'a> {
|
||||||
pub program: PathBuf,
|
pub program: PathBuf,
|
||||||
pub args: Vec<String>,
|
pub args: Vec<String>,
|
||||||
pub arg0: Option<&'a str>,
|
pub arg0: Option<&'a str>,
|
||||||
pub cwd: PathBuf,
|
pub cwd: PathBuf,
|
||||||
pub sandbox_policy: &'a SandboxPolicy,
|
pub network_sandbox_policy: NetworkSandboxPolicy,
|
||||||
pub network: Option<&'a NetworkProxy>,
|
pub network: Option<&'a NetworkProxy>,
|
||||||
pub stdio_policy: StdioPolicy,
|
pub stdio_policy: StdioPolicy,
|
||||||
pub env: HashMap<String, String>,
|
pub env: HashMap<String, String>,
|
||||||
@@ -53,14 +53,14 @@ pub(crate) async fn spawn_child_async(request: SpawnChildRequest<'_>) -> std::io
|
|||||||
args,
|
args,
|
||||||
arg0,
|
arg0,
|
||||||
cwd,
|
cwd,
|
||||||
sandbox_policy,
|
network_sandbox_policy,
|
||||||
network,
|
network,
|
||||||
stdio_policy,
|
stdio_policy,
|
||||||
mut env,
|
mut env,
|
||||||
} = request;
|
} = request;
|
||||||
|
|
||||||
trace!(
|
trace!(
|
||||||
"spawn_child_async: {program:?} {args:?} {arg0:?} {cwd:?} {sandbox_policy:?} {stdio_policy:?} {env:?}"
|
"spawn_child_async: {program:?} {args:?} {arg0:?} {cwd:?} {network_sandbox_policy:?} {stdio_policy:?} {env:?}"
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut cmd = Command::new(&program);
|
let mut cmd = Command::new(&program);
|
||||||
@@ -74,7 +74,7 @@ pub(crate) async fn spawn_child_async(request: SpawnChildRequest<'_>) -> std::io
|
|||||||
cmd.env_clear();
|
cmd.env_clear();
|
||||||
cmd.envs(env);
|
cmd.envs(env);
|
||||||
|
|
||||||
if !sandbox_policy.has_full_network_access() {
|
if !network_sandbox_policy.is_enabled() {
|
||||||
cmd.env(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR, "1");
|
cmd.env(CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR, "1");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ use crate::protocol::ExecCommandBeginEvent;
|
|||||||
use crate::protocol::ExecCommandEndEvent;
|
use crate::protocol::ExecCommandEndEvent;
|
||||||
use crate::protocol::ExecCommandSource;
|
use crate::protocol::ExecCommandSource;
|
||||||
use crate::protocol::ExecCommandStatus;
|
use crate::protocol::ExecCommandStatus;
|
||||||
|
use crate::protocol::FileSystemSandboxPolicy;
|
||||||
|
use crate::protocol::NetworkSandboxPolicy;
|
||||||
use crate::protocol::SandboxPolicy;
|
use crate::protocol::SandboxPolicy;
|
||||||
use crate::protocol::TurnStartedEvent;
|
use crate::protocol::TurnStartedEvent;
|
||||||
use crate::sandboxing::ExecRequest;
|
use crate::sandboxing::ExecRequest;
|
||||||
@@ -167,6 +169,8 @@ pub(crate) async fn execute_user_shell_command(
|
|||||||
windows_sandbox_level: turn_context.windows_sandbox_level,
|
windows_sandbox_level: turn_context.windows_sandbox_level,
|
||||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||||
sandbox_policy: sandbox_policy.clone(),
|
sandbox_policy: sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: FileSystemSandboxPolicy::from(&sandbox_policy),
|
||||||
|
network_sandbox_policy: NetworkSandboxPolicy::from(&sandbox_policy),
|
||||||
justification: None,
|
justification: None,
|
||||||
arg0: None,
|
arg0: None,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -852,7 +852,8 @@ impl JsReplManager {
|
|||||||
.network
|
.network
|
||||||
.is_some();
|
.is_some();
|
||||||
let sandbox_type = sandbox.select_initial(
|
let sandbox_type = sandbox.select_initial(
|
||||||
&turn.sandbox_policy,
|
&turn.file_system_sandbox_policy,
|
||||||
|
turn.network_sandbox_policy,
|
||||||
SandboxablePreference::Auto,
|
SandboxablePreference::Auto,
|
||||||
turn.windows_sandbox_level,
|
turn.windows_sandbox_level,
|
||||||
has_managed_network_requirements,
|
has_managed_network_requirements,
|
||||||
@@ -861,6 +862,8 @@ impl JsReplManager {
|
|||||||
.transform(crate::sandboxing::SandboxTransformRequest {
|
.transform(crate::sandboxing::SandboxTransformRequest {
|
||||||
spec,
|
spec,
|
||||||
policy: &turn.sandbox_policy,
|
policy: &turn.sandbox_policy,
|
||||||
|
file_system_policy: &turn.file_system_sandbox_policy,
|
||||||
|
network_policy: turn.network_sandbox_policy,
|
||||||
sandbox: sandbox_type,
|
sandbox: sandbox_type,
|
||||||
enforce_managed_network: has_managed_network_requirements,
|
enforce_managed_network: has_managed_network_requirements,
|
||||||
network: None,
|
network: None,
|
||||||
|
|||||||
@@ -169,7 +169,8 @@ impl ToolOrchestrator {
|
|||||||
let initial_sandbox = match tool.sandbox_mode_for_first_attempt(req) {
|
let initial_sandbox = match tool.sandbox_mode_for_first_attempt(req) {
|
||||||
SandboxOverride::BypassSandboxFirstAttempt => crate::exec::SandboxType::None,
|
SandboxOverride::BypassSandboxFirstAttempt => crate::exec::SandboxType::None,
|
||||||
SandboxOverride::NoOverride => self.sandbox.select_initial(
|
SandboxOverride::NoOverride => self.sandbox.select_initial(
|
||||||
&turn_ctx.sandbox_policy,
|
&turn_ctx.file_system_sandbox_policy,
|
||||||
|
turn_ctx.network_sandbox_policy,
|
||||||
tool.sandbox_preference(),
|
tool.sandbox_preference(),
|
||||||
turn_ctx.windows_sandbox_level,
|
turn_ctx.windows_sandbox_level,
|
||||||
has_managed_network_requirements,
|
has_managed_network_requirements,
|
||||||
@@ -182,6 +183,8 @@ impl ToolOrchestrator {
|
|||||||
let initial_attempt = SandboxAttempt {
|
let initial_attempt = SandboxAttempt {
|
||||||
sandbox: initial_sandbox,
|
sandbox: initial_sandbox,
|
||||||
policy: &turn_ctx.sandbox_policy,
|
policy: &turn_ctx.sandbox_policy,
|
||||||
|
file_system_policy: &turn_ctx.file_system_sandbox_policy,
|
||||||
|
network_policy: turn_ctx.network_sandbox_policy,
|
||||||
enforce_managed_network: has_managed_network_requirements,
|
enforce_managed_network: has_managed_network_requirements,
|
||||||
manager: &self.sandbox,
|
manager: &self.sandbox,
|
||||||
sandbox_cwd: &turn_ctx.cwd,
|
sandbox_cwd: &turn_ctx.cwd,
|
||||||
@@ -296,6 +299,8 @@ impl ToolOrchestrator {
|
|||||||
let escalated_attempt = SandboxAttempt {
|
let escalated_attempt = SandboxAttempt {
|
||||||
sandbox: crate::exec::SandboxType::None,
|
sandbox: crate::exec::SandboxType::None,
|
||||||
policy: &turn_ctx.sandbox_policy,
|
policy: &turn_ctx.sandbox_policy,
|
||||||
|
file_system_policy: &turn_ctx.file_system_sandbox_policy,
|
||||||
|
network_policy: turn_ctx.network_sandbox_policy,
|
||||||
enforce_managed_network: has_managed_network_requirements,
|
enforce_managed_network: has_managed_network_requirements,
|
||||||
manager: &self.sandbox,
|
manager: &self.sandbox,
|
||||||
sandbox_cwd: &turn_ctx.cwd,
|
sandbox_cwd: &turn_ctx.cwd,
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ use codex_protocol::config_types::WindowsSandboxLevel;
|
|||||||
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
|
use codex_protocol::models::MacOsSeatbeltProfileExtensions;
|
||||||
use codex_protocol::models::PermissionProfile;
|
use codex_protocol::models::PermissionProfile;
|
||||||
use codex_protocol::protocol::AskForApproval;
|
use codex_protocol::protocol::AskForApproval;
|
||||||
|
use codex_protocol::protocol::FileSystemSandboxPolicy;
|
||||||
use codex_protocol::protocol::NetworkPolicyRuleAction;
|
use codex_protocol::protocol::NetworkPolicyRuleAction;
|
||||||
|
use codex_protocol::protocol::NetworkSandboxPolicy;
|
||||||
use codex_protocol::protocol::RejectConfig;
|
use codex_protocol::protocol::RejectConfig;
|
||||||
use codex_protocol::protocol::ReviewDecision;
|
use codex_protocol::protocol::ReviewDecision;
|
||||||
use codex_protocol::protocol::SandboxPolicy;
|
use codex_protocol::protocol::SandboxPolicy;
|
||||||
@@ -98,6 +100,8 @@ pub(super) async fn try_run_zsh_fork(
|
|||||||
windows_sandbox_level,
|
windows_sandbox_level,
|
||||||
sandbox_permissions,
|
sandbox_permissions,
|
||||||
sandbox_policy,
|
sandbox_policy,
|
||||||
|
file_system_sandbox_policy,
|
||||||
|
network_sandbox_policy,
|
||||||
justification,
|
justification,
|
||||||
arg0,
|
arg0,
|
||||||
} = sandbox_exec_request;
|
} = sandbox_exec_request;
|
||||||
@@ -113,6 +117,8 @@ pub(super) async fn try_run_zsh_fork(
|
|||||||
command,
|
command,
|
||||||
cwd: sandbox_cwd,
|
cwd: sandbox_cwd,
|
||||||
sandbox_policy,
|
sandbox_policy,
|
||||||
|
file_system_sandbox_policy,
|
||||||
|
network_sandbox_policy,
|
||||||
sandbox,
|
sandbox,
|
||||||
env: sandbox_env,
|
env: sandbox_env,
|
||||||
network: sandbox_network,
|
network: sandbox_network,
|
||||||
@@ -220,6 +226,8 @@ pub(crate) async fn prepare_unified_exec_zsh_fork(
|
|||||||
command: exec_request.command.clone(),
|
command: exec_request.command.clone(),
|
||||||
cwd: exec_request.cwd.clone(),
|
cwd: exec_request.cwd.clone(),
|
||||||
sandbox_policy: exec_request.sandbox_policy.clone(),
|
sandbox_policy: exec_request.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: exec_request.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: exec_request.network_sandbox_policy,
|
||||||
sandbox: exec_request.sandbox,
|
sandbox: exec_request.sandbox,
|
||||||
env: exec_request.env.clone(),
|
env: exec_request.env.clone(),
|
||||||
network: exec_request.network.clone(),
|
network: exec_request.network.clone(),
|
||||||
@@ -728,6 +736,8 @@ struct CoreShellCommandExecutor {
|
|||||||
command: Vec<String>,
|
command: Vec<String>,
|
||||||
cwd: PathBuf,
|
cwd: PathBuf,
|
||||||
sandbox_policy: SandboxPolicy,
|
sandbox_policy: SandboxPolicy,
|
||||||
|
file_system_sandbox_policy: FileSystemSandboxPolicy,
|
||||||
|
network_sandbox_policy: NetworkSandboxPolicy,
|
||||||
sandbox: SandboxType,
|
sandbox: SandboxType,
|
||||||
env: HashMap<String, String>,
|
env: HashMap<String, String>,
|
||||||
network: Option<codex_network_proxy::NetworkProxy>,
|
network: Option<codex_network_proxy::NetworkProxy>,
|
||||||
@@ -747,6 +757,8 @@ struct PrepareSandboxedExecParams<'a> {
|
|||||||
workdir: &'a AbsolutePathBuf,
|
workdir: &'a AbsolutePathBuf,
|
||||||
env: HashMap<String, String>,
|
env: HashMap<String, String>,
|
||||||
sandbox_policy: &'a SandboxPolicy,
|
sandbox_policy: &'a SandboxPolicy,
|
||||||
|
file_system_sandbox_policy: &'a FileSystemSandboxPolicy,
|
||||||
|
network_sandbox_policy: NetworkSandboxPolicy,
|
||||||
additional_permissions: Option<PermissionProfile>,
|
additional_permissions: Option<PermissionProfile>,
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
macos_seatbelt_profile_extensions: Option<&'a MacOsSeatbeltProfileExtensions>,
|
macos_seatbelt_profile_extensions: Option<&'a MacOsSeatbeltProfileExtensions>,
|
||||||
@@ -782,6 +794,8 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
|
|||||||
windows_sandbox_level: self.windows_sandbox_level,
|
windows_sandbox_level: self.windows_sandbox_level,
|
||||||
sandbox_permissions: self.sandbox_permissions,
|
sandbox_permissions: self.sandbox_permissions,
|
||||||
sandbox_policy: self.sandbox_policy.clone(),
|
sandbox_policy: self.sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: self.file_system_sandbox_policy.clone(),
|
||||||
|
network_sandbox_policy: self.network_sandbox_policy,
|
||||||
justification: self.justification.clone(),
|
justification: self.justification.clone(),
|
||||||
arg0: self.arg0.clone(),
|
arg0: self.arg0.clone(),
|
||||||
},
|
},
|
||||||
@@ -828,6 +842,8 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
|
|||||||
workdir,
|
workdir,
|
||||||
env,
|
env,
|
||||||
sandbox_policy: &self.sandbox_policy,
|
sandbox_policy: &self.sandbox_policy,
|
||||||
|
file_system_sandbox_policy: &self.file_system_sandbox_policy,
|
||||||
|
network_sandbox_policy: self.network_sandbox_policy,
|
||||||
additional_permissions: None,
|
additional_permissions: None,
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
macos_seatbelt_profile_extensions: self
|
macos_seatbelt_profile_extensions: self
|
||||||
@@ -845,6 +861,8 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
|
|||||||
workdir,
|
workdir,
|
||||||
env,
|
env,
|
||||||
sandbox_policy: &self.sandbox_policy,
|
sandbox_policy: &self.sandbox_policy,
|
||||||
|
file_system_sandbox_policy: &self.file_system_sandbox_policy,
|
||||||
|
network_sandbox_policy: self.network_sandbox_policy,
|
||||||
additional_permissions: Some(permission_profile),
|
additional_permissions: Some(permission_profile),
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
macos_seatbelt_profile_extensions: self
|
macos_seatbelt_profile_extensions: self
|
||||||
@@ -854,11 +872,17 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
|
|||||||
}
|
}
|
||||||
EscalationExecution::Permissions(EscalationPermissions::Permissions(permissions)) => {
|
EscalationExecution::Permissions(EscalationPermissions::Permissions(permissions)) => {
|
||||||
// Use a fully specified sandbox policy instead of merging into the turn policy.
|
// Use a fully specified sandbox policy instead of merging into the turn policy.
|
||||||
|
let file_system_sandbox_policy =
|
||||||
|
FileSystemSandboxPolicy::from(&permissions.sandbox_policy);
|
||||||
|
let network_sandbox_policy =
|
||||||
|
NetworkSandboxPolicy::from(&permissions.sandbox_policy);
|
||||||
self.prepare_sandboxed_exec(PrepareSandboxedExecParams {
|
self.prepare_sandboxed_exec(PrepareSandboxedExecParams {
|
||||||
command,
|
command,
|
||||||
workdir,
|
workdir,
|
||||||
env,
|
env,
|
||||||
sandbox_policy: &permissions.sandbox_policy,
|
sandbox_policy: &permissions.sandbox_policy,
|
||||||
|
file_system_sandbox_policy: &file_system_sandbox_policy,
|
||||||
|
network_sandbox_policy,
|
||||||
additional_permissions: None,
|
additional_permissions: None,
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
macos_seatbelt_profile_extensions: permissions
|
macos_seatbelt_profile_extensions: permissions
|
||||||
@@ -873,6 +897,7 @@ impl ShellCommandExecutor for CoreShellCommandExecutor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CoreShellCommandExecutor {
|
impl CoreShellCommandExecutor {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn prepare_sandboxed_exec(
|
fn prepare_sandboxed_exec(
|
||||||
&self,
|
&self,
|
||||||
params: PrepareSandboxedExecParams<'_>,
|
params: PrepareSandboxedExecParams<'_>,
|
||||||
@@ -882,6 +907,8 @@ impl CoreShellCommandExecutor {
|
|||||||
workdir,
|
workdir,
|
||||||
env,
|
env,
|
||||||
sandbox_policy,
|
sandbox_policy,
|
||||||
|
file_system_sandbox_policy,
|
||||||
|
network_sandbox_policy,
|
||||||
additional_permissions,
|
additional_permissions,
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
macos_seatbelt_profile_extensions,
|
macos_seatbelt_profile_extensions,
|
||||||
@@ -891,7 +918,8 @@ impl CoreShellCommandExecutor {
|
|||||||
.ok_or_else(|| anyhow::anyhow!("prepared command must not be empty"))?;
|
.ok_or_else(|| anyhow::anyhow!("prepared command must not be empty"))?;
|
||||||
let sandbox_manager = crate::sandboxing::SandboxManager::new();
|
let sandbox_manager = crate::sandboxing::SandboxManager::new();
|
||||||
let sandbox = sandbox_manager.select_initial(
|
let sandbox = sandbox_manager.select_initial(
|
||||||
sandbox_policy,
|
file_system_sandbox_policy,
|
||||||
|
network_sandbox_policy,
|
||||||
SandboxablePreference::Auto,
|
SandboxablePreference::Auto,
|
||||||
self.windows_sandbox_level,
|
self.windows_sandbox_level,
|
||||||
self.network.is_some(),
|
self.network.is_some(),
|
||||||
@@ -913,6 +941,8 @@ impl CoreShellCommandExecutor {
|
|||||||
justification: self.justification.clone(),
|
justification: self.justification.clone(),
|
||||||
},
|
},
|
||||||
policy: sandbox_policy,
|
policy: sandbox_policy,
|
||||||
|
file_system_policy: file_system_sandbox_policy,
|
||||||
|
network_policy: network_sandbox_policy,
|
||||||
sandbox,
|
sandbox,
|
||||||
enforce_managed_network: self.network.is_some(),
|
enforce_managed_network: self.network.is_some(),
|
||||||
network: self.network.as_ref(),
|
network: self.network.as_ref(),
|
||||||
|
|||||||
@@ -478,6 +478,10 @@ async fn prepare_escalated_exec_turn_default_preserves_macos_seatbelt_extensions
|
|||||||
network: None,
|
network: None,
|
||||||
sandbox: SandboxType::None,
|
sandbox: SandboxType::None,
|
||||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
||||||
|
file_system_sandbox_policy: FileSystemSandboxPolicy::from(
|
||||||
|
&SandboxPolicy::new_read_only_policy(),
|
||||||
|
),
|
||||||
|
network_sandbox_policy: NetworkSandboxPolicy::Restricted,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||||
justification: None,
|
justification: None,
|
||||||
@@ -528,6 +532,8 @@ async fn prepare_escalated_exec_permissions_preserve_macos_seatbelt_extensions()
|
|||||||
network: None,
|
network: None,
|
||||||
sandbox: SandboxType::None,
|
sandbox: SandboxType::None,
|
||||||
sandbox_policy: SandboxPolicy::DangerFullAccess,
|
sandbox_policy: SandboxPolicy::DangerFullAccess,
|
||||||
|
file_system_sandbox_policy: FileSystemSandboxPolicy::from(&SandboxPolicy::DangerFullAccess),
|
||||||
|
network_sandbox_policy: NetworkSandboxPolicy::Enabled,
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||||
justification: None,
|
justification: None,
|
||||||
@@ -592,13 +598,16 @@ async fn prepare_escalated_exec_permissions_preserve_macos_seatbelt_extensions()
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn prepare_escalated_exec_permission_profile_unions_turn_and_requested_macos_extensions() {
|
async fn prepare_escalated_exec_permission_profile_unions_turn_and_requested_macos_extensions() {
|
||||||
let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir()).unwrap();
|
let cwd = AbsolutePathBuf::from_absolute_path(std::env::temp_dir()).unwrap();
|
||||||
|
let sandbox_policy = SandboxPolicy::new_read_only_policy();
|
||||||
let executor = CoreShellCommandExecutor {
|
let executor = CoreShellCommandExecutor {
|
||||||
command: vec!["echo".to_string(), "ok".to_string()],
|
command: vec!["echo".to_string(), "ok".to_string()],
|
||||||
cwd: cwd.to_path_buf(),
|
cwd: cwd.to_path_buf(),
|
||||||
env: HashMap::new(),
|
env: HashMap::new(),
|
||||||
network: None,
|
network: None,
|
||||||
sandbox: SandboxType::None,
|
sandbox: SandboxType::None,
|
||||||
sandbox_policy: SandboxPolicy::new_read_only_policy(),
|
sandbox_policy: sandbox_policy.clone(),
|
||||||
|
file_system_sandbox_policy: FileSystemSandboxPolicy::from(&sandbox_policy),
|
||||||
|
network_sandbox_policy: NetworkSandboxPolicy::from(&sandbox_policy),
|
||||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||||
justification: None,
|
justification: None,
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
use crate::codex::Session;
|
use crate::codex::Session;
|
||||||
use crate::codex::TurnContext;
|
use crate::codex::TurnContext;
|
||||||
use crate::error::CodexErr;
|
use crate::error::CodexErr;
|
||||||
|
use crate::protocol::FileSystemSandboxPolicy;
|
||||||
|
use crate::protocol::NetworkSandboxPolicy;
|
||||||
use crate::protocol::SandboxPolicy;
|
use crate::protocol::SandboxPolicy;
|
||||||
use crate::sandboxing::CommandSpec;
|
use crate::sandboxing::CommandSpec;
|
||||||
use crate::sandboxing::SandboxManager;
|
use crate::sandboxing::SandboxManager;
|
||||||
@@ -318,6 +320,8 @@ pub(crate) trait ToolRuntime<Req, Out>: Approvable<Req> + Sandboxable {
|
|||||||
pub(crate) struct SandboxAttempt<'a> {
|
pub(crate) struct SandboxAttempt<'a> {
|
||||||
pub sandbox: crate::exec::SandboxType,
|
pub sandbox: crate::exec::SandboxType,
|
||||||
pub policy: &'a crate::protocol::SandboxPolicy,
|
pub policy: &'a crate::protocol::SandboxPolicy,
|
||||||
|
pub file_system_policy: &'a FileSystemSandboxPolicy,
|
||||||
|
pub network_policy: NetworkSandboxPolicy,
|
||||||
pub enforce_managed_network: bool,
|
pub enforce_managed_network: bool,
|
||||||
pub(crate) manager: &'a SandboxManager,
|
pub(crate) manager: &'a SandboxManager,
|
||||||
pub(crate) sandbox_cwd: &'a Path,
|
pub(crate) sandbox_cwd: &'a Path,
|
||||||
@@ -336,6 +340,8 @@ impl<'a> SandboxAttempt<'a> {
|
|||||||
.transform(crate::sandboxing::SandboxTransformRequest {
|
.transform(crate::sandboxing::SandboxTransformRequest {
|
||||||
spec,
|
spec,
|
||||||
policy: self.policy,
|
policy: self.policy,
|
||||||
|
file_system_policy: self.file_system_policy,
|
||||||
|
network_policy: self.network_policy,
|
||||||
sandbox: self.sandbox,
|
sandbox: self.sandbox,
|
||||||
enforce_managed_network: self.enforce_managed_network,
|
enforce_managed_network: self.enforce_managed_network,
|
||||||
network,
|
network,
|
||||||
|
|||||||
@@ -205,6 +205,10 @@ mod tests {
|
|||||||
turn.sandbox_policy
|
turn.sandbox_policy
|
||||||
.set(SandboxPolicy::DangerFullAccess)
|
.set(SandboxPolicy::DangerFullAccess)
|
||||||
.expect("test setup should allow updating sandbox policy");
|
.expect("test setup should allow updating sandbox policy");
|
||||||
|
turn.file_system_sandbox_policy =
|
||||||
|
crate::protocol::FileSystemSandboxPolicy::from(turn.sandbox_policy.get());
|
||||||
|
turn.network_sandbox_policy =
|
||||||
|
crate::protocol::NetworkSandboxPolicy::from(turn.sandbox_policy.get());
|
||||||
(Arc::new(session), Arc::new(turn))
|
(Arc::new(session), Arc::new(turn))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ use codex_core::exec::process_exec_tool_call;
|
|||||||
use codex_core::sandboxing::SandboxPermissions;
|
use codex_core::sandboxing::SandboxPermissions;
|
||||||
use codex_core::spawn::CODEX_SANDBOX_ENV_VAR;
|
use codex_core::spawn::CODEX_SANDBOX_ENV_VAR;
|
||||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||||
|
use codex_protocol::protocol::FileSystemSandboxPolicy;
|
||||||
|
use codex_protocol::protocol::NetworkSandboxPolicy;
|
||||||
use codex_protocol::protocol::SandboxPolicy;
|
use codex_protocol::protocol::SandboxPolicy;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
@@ -45,7 +47,17 @@ async fn run_test_cmd(tmp: TempDir, cmd: Vec<&str>) -> Result<ExecToolCallOutput
|
|||||||
|
|
||||||
let policy = SandboxPolicy::new_read_only_policy();
|
let policy = SandboxPolicy::new_read_only_policy();
|
||||||
|
|
||||||
process_exec_tool_call(params, &policy, tmp.path(), &None, false, None).await
|
process_exec_tool_call(
|
||||||
|
params,
|
||||||
|
&policy,
|
||||||
|
&FileSystemSandboxPolicy::from(&policy),
|
||||||
|
NetworkSandboxPolicy::from(&policy),
|
||||||
|
tmp.path(),
|
||||||
|
&None,
|
||||||
|
false,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Command succeeds with exit code 0 normally
|
/// Command succeeds with exit code 0 normally
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use codex_protocol::protocol::EventMsg;
|
|||||||
use codex_protocol::protocol::ExecCommandEndEvent;
|
use codex_protocol::protocol::ExecCommandEndEvent;
|
||||||
use codex_protocol::protocol::ExecCommandSource;
|
use codex_protocol::protocol::ExecCommandSource;
|
||||||
use codex_protocol::protocol::ExecOutputStream;
|
use codex_protocol::protocol::ExecOutputStream;
|
||||||
|
use codex_protocol::protocol::NetworkSandboxPolicy;
|
||||||
use codex_protocol::protocol::Op;
|
use codex_protocol::protocol::Op;
|
||||||
use codex_protocol::protocol::SandboxPolicy;
|
use codex_protocol::protocol::SandboxPolicy;
|
||||||
use codex_protocol::protocol::TurnAbortReason;
|
use codex_protocol::protocol::TurnAbortReason;
|
||||||
@@ -23,6 +24,7 @@ use core_test_support::test_codex::test_codex;
|
|||||||
use core_test_support::wait_for_event;
|
use core_test_support::wait_for_event;
|
||||||
use core_test_support::wait_for_event_match;
|
use core_test_support::wait_for_event_match;
|
||||||
use core_test_support::wait_for_event_with_timeout;
|
use core_test_support::wait_for_event_with_timeout;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
use regex_lite::escape;
|
use regex_lite::escape;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
@@ -328,6 +330,35 @@ async fn user_shell_command_history_is_persisted_and_shared_with_model() -> anyh
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn user_shell_command_does_not_set_network_sandbox_env_var() -> anyhow::Result<()> {
|
||||||
|
let server = responses::start_mock_server().await;
|
||||||
|
let mut builder = core_test_support::test_codex::test_codex().with_config(|config| {
|
||||||
|
config.permissions.network_sandbox_policy = NetworkSandboxPolicy::Restricted;
|
||||||
|
});
|
||||||
|
let test = builder.build(&server).await?;
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
let command = r#"$val = $env:CODEX_SANDBOX_NETWORK_DISABLED; if ([string]::IsNullOrEmpty($val)) { $val = 'not-set' } ; [System.Console]::Write($val)"#.to_string();
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let command =
|
||||||
|
r#"sh -c "printf '%s' \"${CODEX_SANDBOX_NETWORK_DISABLED:-not-set}\"""#.to_string();
|
||||||
|
|
||||||
|
test.codex
|
||||||
|
.submit(Op::RunUserShellCommand { command })
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let end_event = wait_for_event_match(&test.codex, |ev| match ev {
|
||||||
|
EventMsg::ExecCommandEnd(event) => Some(event.clone()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
assert_eq!(end_event.exit_code, 0);
|
||||||
|
assert_eq!(end_event.stdout.trim(), "not-set");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
#[cfg(not(target_os = "windows"))] // TODO: unignore on windows
|
#[cfg(not(target_os = "windows"))] // TODO: unignore on windows
|
||||||
async fn user_shell_command_output_is_truncated_in_history() -> anyhow::Result<()> {
|
async fn user_shell_command_output_is_truncated_in_history() -> anyhow::Result<()> {
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use codex_core::exec::process_exec_tool_call;
|
|||||||
use codex_core::exec_env::create_env;
|
use codex_core::exec_env::create_env;
|
||||||
use codex_core::sandboxing::SandboxPermissions;
|
use codex_core::sandboxing::SandboxPermissions;
|
||||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||||
|
use codex_protocol::protocol::FileSystemSandboxPolicy;
|
||||||
|
use codex_protocol::protocol::NetworkSandboxPolicy;
|
||||||
use codex_protocol::protocol::SandboxPolicy;
|
use codex_protocol::protocol::SandboxPolicy;
|
||||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
@@ -102,6 +104,8 @@ async fn run_cmd_result_with_writable_roots(
|
|||||||
process_exec_tool_call(
|
process_exec_tool_call(
|
||||||
params,
|
params,
|
||||||
&sandbox_policy,
|
&sandbox_policy,
|
||||||
|
&FileSystemSandboxPolicy::from(&sandbox_policy),
|
||||||
|
NetworkSandboxPolicy::from(&sandbox_policy),
|
||||||
sandbox_cwd.as_path(),
|
sandbox_cwd.as_path(),
|
||||||
&codex_linux_sandbox_exe,
|
&codex_linux_sandbox_exe,
|
||||||
use_bwrap_sandbox,
|
use_bwrap_sandbox,
|
||||||
@@ -333,6 +337,8 @@ async fn assert_network_blocked(cmd: &[&str]) {
|
|||||||
let result = process_exec_tool_call(
|
let result = process_exec_tool_call(
|
||||||
params,
|
params,
|
||||||
&sandbox_policy,
|
&sandbox_policy,
|
||||||
|
&FileSystemSandboxPolicy::from(&sandbox_policy),
|
||||||
|
NetworkSandboxPolicy::from(&sandbox_policy),
|
||||||
sandbox_cwd.as_path(),
|
sandbox_cwd.as_path(),
|
||||||
&codex_linux_sandbox_exe,
|
&codex_linux_sandbox_exe,
|
||||||
false,
|
false,
|
||||||
|
|||||||
Reference in New Issue
Block a user