remove sandbox globals. (#9797)
Threads sandbox updates through OverrideTurnContext for active turn Passes computed sandbox type into safety/exec
This commit is contained in:
parent
894923ed5d
commit
c40ad65bd8
35 changed files with 339 additions and 132 deletions
|
|
@ -169,6 +169,7 @@ use codex_core::read_head_for_summary;
|
|||
use codex_core::read_session_meta_line;
|
||||
use codex_core::rollout_date_parts;
|
||||
use codex_core::sandboxing::SandboxPermissions;
|
||||
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_feedback::CodexFeedback;
|
||||
use codex_login::ServerOptions as LoginServerOptions;
|
||||
use codex_login::ShutdownHandle;
|
||||
|
|
@ -176,6 +177,7 @@ use codex_login::run_login_server;
|
|||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::dynamic_tools::DynamicToolSpec as CoreDynamicToolSpec;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
|
|
@ -1259,12 +1261,14 @@ impl CodexMessageProcessor {
|
|||
let timeout_ms = params
|
||||
.timeout_ms
|
||||
.and_then(|timeout_ms| u64::try_from(timeout_ms).ok());
|
||||
let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config);
|
||||
let exec_params = ExecParams {
|
||||
command: params.command,
|
||||
cwd,
|
||||
expiration: timeout_ms.into(),
|
||||
env,
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
windows_sandbox_level,
|
||||
justification: None,
|
||||
arg0: None,
|
||||
};
|
||||
|
|
@ -3887,6 +3891,7 @@ impl CodexMessageProcessor {
|
|||
cwd: params.cwd,
|
||||
approval_policy: params.approval_policy.map(AskForApproval::to_core),
|
||||
sandbox_policy: params.sandbox_policy.map(|p| p.to_core()),
|
||||
windows_sandbox_level: None,
|
||||
model: params.model,
|
||||
effort: params.effort.map(Some),
|
||||
summary: params.summary,
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ pub(crate) async fn apply_patch(
|
|||
turn_context.approval_policy,
|
||||
&turn_context.sandbox_policy,
|
||||
&turn_context.cwd,
|
||||
turn_context.windows_sandbox_level,
|
||||
) {
|
||||
SafetyCheck::AutoApprove {
|
||||
user_explicitly_approved,
|
||||
|
|
|
|||
|
|
@ -174,11 +174,13 @@ use crate::turn_diff_tracker::TurnDiffTracker;
|
|||
use crate::unified_exec::UnifiedExecProcessManager;
|
||||
use crate::user_notification::UserNotification;
|
||||
use crate::util::backoff;
|
||||
use crate::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_async_utils::OrCancelExt;
|
||||
use codex_otel::OtelManager;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::DeveloperInstructions;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
|
|
@ -325,6 +327,7 @@ impl Codex {
|
|||
compact_prompt: config.compact_prompt.clone(),
|
||||
approval_policy: config.approval_policy.clone(),
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
session_source,
|
||||
|
|
@ -445,6 +448,7 @@ pub(crate) struct TurnContext {
|
|||
pub(crate) personality: Option<Personality>,
|
||||
pub(crate) approval_policy: AskForApproval,
|
||||
pub(crate) sandbox_policy: SandboxPolicy,
|
||||
pub(crate) windows_sandbox_level: WindowsSandboxLevel,
|
||||
pub(crate) shell_environment_policy: ShellEnvironmentPolicy,
|
||||
pub(crate) tools_config: ToolsConfig,
|
||||
pub(crate) ghost_snapshot: GhostSnapshotConfig,
|
||||
|
|
@ -496,6 +500,7 @@ pub(crate) struct SessionConfiguration {
|
|||
approval_policy: Constrained<AskForApproval>,
|
||||
/// How to sandbox commands executed in the system
|
||||
sandbox_policy: Constrained<SandboxPolicy>,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
|
||||
/// Working directory that should be treated as the *root* of the
|
||||
/// session. All relative paths supplied by the model as well as the
|
||||
|
|
@ -544,6 +549,9 @@ impl SessionConfiguration {
|
|||
if let Some(sandbox_policy) = updates.sandbox_policy.clone() {
|
||||
next_configuration.sandbox_policy.set(sandbox_policy)?;
|
||||
}
|
||||
if let Some(windows_sandbox_level) = updates.windows_sandbox_level {
|
||||
next_configuration.windows_sandbox_level = windows_sandbox_level;
|
||||
}
|
||||
if let Some(cwd) = updates.cwd.clone() {
|
||||
next_configuration.cwd = cwd;
|
||||
}
|
||||
|
|
@ -556,6 +564,7 @@ pub(crate) struct SessionSettingsUpdate {
|
|||
pub(crate) cwd: Option<PathBuf>,
|
||||
pub(crate) approval_policy: Option<AskForApproval>,
|
||||
pub(crate) sandbox_policy: Option<SandboxPolicy>,
|
||||
pub(crate) windows_sandbox_level: Option<WindowsSandboxLevel>,
|
||||
pub(crate) collaboration_mode: Option<CollaborationMode>,
|
||||
pub(crate) reasoning_summary: Option<ReasoningSummaryConfig>,
|
||||
pub(crate) final_output_json_schema: Option<Option<Value>>,
|
||||
|
|
@ -620,6 +629,7 @@ impl Session {
|
|||
personality: session_configuration.personality,
|
||||
approval_policy: session_configuration.approval_policy.value(),
|
||||
sandbox_policy: session_configuration.sandbox_policy.get().clone(),
|
||||
windows_sandbox_level: session_configuration.windows_sandbox_level,
|
||||
shell_environment_policy: per_turn_config.shell_environment_policy.clone(),
|
||||
tools_config,
|
||||
ghost_snapshot: per_turn_config.ghost_snapshot.clone(),
|
||||
|
|
@ -2191,6 +2201,7 @@ async fn submission_loop(sess: Arc<Session>, config: Arc<Config>, rx_sub: Receiv
|
|||
cwd,
|
||||
approval_policy,
|
||||
sandbox_policy,
|
||||
windows_sandbox_level,
|
||||
model,
|
||||
effort,
|
||||
summary,
|
||||
|
|
@ -2214,6 +2225,7 @@ async fn submission_loop(sess: Arc<Session>, config: Arc<Config>, rx_sub: Receiv
|
|||
cwd,
|
||||
approval_policy,
|
||||
sandbox_policy,
|
||||
windows_sandbox_level,
|
||||
collaboration_mode: Some(collaboration_mode),
|
||||
reasoning_summary: summary,
|
||||
personality,
|
||||
|
|
@ -2430,6 +2442,7 @@ mod handlers {
|
|||
cwd: Some(cwd),
|
||||
approval_policy: Some(approval_policy),
|
||||
sandbox_policy: Some(sandbox_policy),
|
||||
windows_sandbox_level: None,
|
||||
collaboration_mode,
|
||||
reasoning_summary: Some(summary),
|
||||
final_output_json_schema: Some(final_output_json_schema),
|
||||
|
|
@ -2918,6 +2931,7 @@ async fn spawn_review_thread(
|
|||
personality: parent_turn_context.personality,
|
||||
approval_policy: parent_turn_context.approval_policy,
|
||||
sandbox_policy: parent_turn_context.sandbox_policy.clone(),
|
||||
windows_sandbox_level: parent_turn_context.windows_sandbox_level,
|
||||
shell_environment_policy: parent_turn_context.shell_environment_policy.clone(),
|
||||
cwd: parent_turn_context.cwd.clone(),
|
||||
final_output_json_schema: None,
|
||||
|
|
@ -4062,6 +4076,7 @@ mod tests {
|
|||
compact_prompt: config.compact_prompt.clone(),
|
||||
approval_policy: config.approval_policy.clone(),
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
session_source: SessionSource::Exec,
|
||||
|
|
@ -4142,6 +4157,7 @@ mod tests {
|
|||
compact_prompt: config.compact_prompt.clone(),
|
||||
approval_policy: config.approval_policy.clone(),
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
session_source: SessionSource::Exec,
|
||||
|
|
@ -4406,6 +4422,7 @@ mod tests {
|
|||
compact_prompt: config.compact_prompt.clone(),
|
||||
approval_policy: config.approval_policy.clone(),
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
session_source: SessionSource::Exec,
|
||||
|
|
@ -4516,6 +4533,7 @@ mod tests {
|
|||
compact_prompt: config.compact_prompt.clone(),
|
||||
approval_policy: config.approval_policy.clone(),
|
||||
sandbox_policy: config.sandbox_policy.clone(),
|
||||
windows_sandbox_level: WindowsSandboxLevel::from_config(&config),
|
||||
cwd: config.cwd.clone(),
|
||||
original_config_do_not_use: Arc::clone(&config),
|
||||
session_source: SessionSource::Exec,
|
||||
|
|
@ -5023,6 +5041,7 @@ mod tests {
|
|||
expiration: timeout_ms.into(),
|
||||
env: HashMap::new(),
|
||||
sandbox_permissions,
|
||||
windows_sandbox_level: turn_context.windows_sandbox_level,
|
||||
justification: Some("test".to_string()),
|
||||
arg0: None,
|
||||
};
|
||||
|
|
@ -5033,6 +5052,7 @@ mod tests {
|
|||
cwd: params.cwd.clone(),
|
||||
expiration: timeout_ms.into(),
|
||||
env: HashMap::new(),
|
||||
windows_sandbox_level: turn_context.windows_sandbox_level,
|
||||
justification: params.justification.clone(),
|
||||
arg0: None,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ use crate::project_doc::DEFAULT_PROJECT_DOC_FILENAME;
|
|||
use crate::project_doc::LOCAL_PROJECT_DOC_FILENAME;
|
||||
use crate::protocol::AskForApproval;
|
||||
use crate::protocol::SandboxPolicy;
|
||||
use crate::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_app_server_protocol::Tools;
|
||||
use codex_app_server_protocol::UserSavedConfig;
|
||||
use codex_protocol::config_types::AltScreenMode;
|
||||
|
|
@ -49,6 +50,7 @@ use codex_protocol::config_types::SandboxMode;
|
|||
use codex_protocol::config_types::TrustLevel;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_rmcp_client::OAuthCredentialsStoreMode;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
|
@ -1056,6 +1058,7 @@ impl ConfigToml {
|
|||
&self,
|
||||
sandbox_mode_override: Option<SandboxMode>,
|
||||
profile_sandbox_mode: Option<SandboxMode>,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
resolved_cwd: &Path,
|
||||
) -> SandboxPolicyResolution {
|
||||
let resolved_sandbox_mode = sandbox_mode_override
|
||||
|
|
@ -1094,7 +1097,7 @@ impl ConfigToml {
|
|||
if cfg!(target_os = "windows")
|
||||
&& matches!(resolved_sandbox_mode, SandboxMode::WorkspaceWrite)
|
||||
// If the experimental Windows sandbox is enabled, do not force a downgrade.
|
||||
&& crate::safety::get_platform_sandbox().is_none()
|
||||
&& windows_sandbox_level == codex_protocol::config_types::WindowsSandboxLevel::Disabled
|
||||
{
|
||||
sandbox_policy = SandboxPolicy::new_read_only_policy();
|
||||
forced_auto_mode_downgraded_on_windows = true;
|
||||
|
|
@ -1285,16 +1288,6 @@ impl Config {
|
|||
|
||||
let features = Features::from_config(&cfg, &config_profile, feature_overrides);
|
||||
let web_search_mode = resolve_web_search_mode(&cfg, &config_profile, &features);
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
// Base flag controls sandbox on/off; elevated only applies when base is enabled.
|
||||
let sandbox_enabled = features.enabled(Feature::WindowsSandbox);
|
||||
crate::safety::set_windows_sandbox_enabled(sandbox_enabled);
|
||||
let elevated_enabled =
|
||||
sandbox_enabled && features.enabled(Feature::WindowsSandboxElevated);
|
||||
crate::safety::set_windows_elevated_sandbox_enabled(elevated_enabled);
|
||||
}
|
||||
|
||||
let resolved_cwd = {
|
||||
use std::env;
|
||||
|
||||
|
|
@ -1321,10 +1314,16 @@ impl Config {
|
|||
.get_active_project(&resolved_cwd)
|
||||
.unwrap_or(ProjectConfig { trust_level: None });
|
||||
|
||||
let windows_sandbox_level = WindowsSandboxLevel::from_features(&features);
|
||||
let SandboxPolicyResolution {
|
||||
policy: mut sandbox_policy,
|
||||
forced_auto_mode_downgraded_on_windows,
|
||||
} = cfg.derive_sandbox_policy(sandbox_mode, config_profile.sandbox_mode, &resolved_cwd);
|
||||
} = cfg.derive_sandbox_policy(
|
||||
sandbox_mode,
|
||||
config_profile.sandbox_mode,
|
||||
windows_sandbox_level,
|
||||
&resolved_cwd,
|
||||
);
|
||||
if let SandboxPolicy::WorkspaceWrite { writable_roots, .. } = &mut sandbox_policy {
|
||||
for path in additional_writable_roots {
|
||||
if !writable_roots.iter().any(|existing| existing == &path) {
|
||||
|
|
@ -1667,7 +1666,6 @@ impl Config {
|
|||
}
|
||||
|
||||
pub fn set_windows_sandbox_globally(&mut self, value: bool) {
|
||||
crate::safety::set_windows_sandbox_enabled(value);
|
||||
if value {
|
||||
self.features.enable(Feature::WindowsSandbox);
|
||||
} else {
|
||||
|
|
@ -1677,7 +1675,6 @@ impl Config {
|
|||
}
|
||||
|
||||
pub fn set_windows_elevated_sandbox_globally(&mut self, value: bool) {
|
||||
crate::safety::set_windows_elevated_sandbox_enabled(value);
|
||||
if value {
|
||||
self.features.enable(Feature::WindowsSandboxElevated);
|
||||
} else {
|
||||
|
|
@ -1871,6 +1868,7 @@ network_access = false # This should be ignored.
|
|||
let resolution = sandbox_full_access_cfg.derive_sandbox_policy(
|
||||
sandbox_mode_override,
|
||||
None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&PathBuf::from("/tmp/test"),
|
||||
);
|
||||
assert_eq!(
|
||||
|
|
@ -1894,6 +1892,7 @@ network_access = true # This should be ignored.
|
|||
let resolution = sandbox_read_only_cfg.derive_sandbox_policy(
|
||||
sandbox_mode_override,
|
||||
None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&PathBuf::from("/tmp/test"),
|
||||
);
|
||||
assert_eq!(
|
||||
|
|
@ -1925,6 +1924,7 @@ exclude_slash_tmp = true
|
|||
let resolution = sandbox_workspace_write_cfg.derive_sandbox_policy(
|
||||
sandbox_mode_override,
|
||||
None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&PathBuf::from("/tmp/test"),
|
||||
);
|
||||
if cfg!(target_os = "windows") {
|
||||
|
|
@ -1973,6 +1973,7 @@ trust_level = "trusted"
|
|||
let resolution = sandbox_workspace_write_cfg.derive_sandbox_policy(
|
||||
sandbox_mode_override,
|
||||
None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&PathBuf::from("/tmp/test"),
|
||||
);
|
||||
if cfg!(target_os = "windows") {
|
||||
|
|
@ -4190,7 +4191,12 @@ trust_level = "untrusted"
|
|||
let cfg = toml::from_str::<ConfigToml>(config_with_untrusted)
|
||||
.expect("TOML deserialization should succeed");
|
||||
|
||||
let resolution = cfg.derive_sandbox_policy(None, None, &PathBuf::from("/tmp/test"));
|
||||
let resolution = cfg.derive_sandbox_policy(
|
||||
None,
|
||||
None,
|
||||
WindowsSandboxLevel::Disabled,
|
||||
&PathBuf::from("/tmp/test"),
|
||||
);
|
||||
|
||||
// Verify that untrusted projects get WorkspaceWrite (or ReadOnly on Windows due to downgrade)
|
||||
if cfg!(target_os = "windows") {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ pub struct ExecParams {
|
|||
pub expiration: ExecExpiration,
|
||||
pub env: HashMap<String, String>,
|
||||
pub sandbox_permissions: SandboxPermissions,
|
||||
pub windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel,
|
||||
pub justification: Option<String>,
|
||||
pub arg0: Option<String>,
|
||||
}
|
||||
|
|
@ -141,11 +142,15 @@ pub async fn process_exec_tool_call(
|
|||
codex_linux_sandbox_exe: &Option<PathBuf>,
|
||||
stdout_stream: Option<StdoutStream>,
|
||||
) -> Result<ExecToolCallOutput> {
|
||||
let windows_sandbox_level = params.windows_sandbox_level;
|
||||
let sandbox_type = match &sandbox_policy {
|
||||
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => {
|
||||
SandboxType::None
|
||||
}
|
||||
_ => get_platform_sandbox().unwrap_or(SandboxType::None),
|
||||
_ => get_platform_sandbox(
|
||||
windows_sandbox_level != codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
)
|
||||
.unwrap_or(SandboxType::None),
|
||||
};
|
||||
tracing::debug!("Sandbox type: {sandbox_type:?}");
|
||||
|
||||
|
|
@ -155,6 +160,7 @@ pub async fn process_exec_tool_call(
|
|||
expiration,
|
||||
env,
|
||||
sandbox_permissions,
|
||||
windows_sandbox_level,
|
||||
justification,
|
||||
arg0: _,
|
||||
} = params;
|
||||
|
|
@ -184,6 +190,7 @@ pub async fn process_exec_tool_call(
|
|||
sandbox_type,
|
||||
sandbox_cwd,
|
||||
codex_linux_sandbox_exe.as_ref(),
|
||||
windows_sandbox_level,
|
||||
)
|
||||
.map_err(CodexErr::from)?;
|
||||
|
||||
|
|
@ -202,6 +209,7 @@ pub(crate) async fn execute_exec_env(
|
|||
env,
|
||||
expiration,
|
||||
sandbox,
|
||||
windows_sandbox_level,
|
||||
sandbox_permissions,
|
||||
justification,
|
||||
arg0,
|
||||
|
|
@ -213,6 +221,7 @@ pub(crate) async fn execute_exec_env(
|
|||
expiration,
|
||||
env,
|
||||
sandbox_permissions,
|
||||
windows_sandbox_level,
|
||||
justification,
|
||||
arg0,
|
||||
};
|
||||
|
|
@ -229,7 +238,7 @@ async fn exec_windows_sandbox(
|
|||
sandbox_policy: &SandboxPolicy,
|
||||
) -> Result<RawExecToolCallOutput> {
|
||||
use crate::config::find_codex_home;
|
||||
use crate::safety::is_windows_elevated_sandbox_enabled;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_windows_sandbox::run_windows_sandbox_capture;
|
||||
use codex_windows_sandbox::run_windows_sandbox_capture_elevated;
|
||||
|
||||
|
|
@ -238,6 +247,7 @@ async fn exec_windows_sandbox(
|
|||
cwd,
|
||||
env,
|
||||
expiration,
|
||||
windows_sandbox_level,
|
||||
..
|
||||
} = params;
|
||||
// TODO(iceweasel-oai): run_windows_sandbox_capture should support all
|
||||
|
|
@ -255,7 +265,7 @@ async fn exec_windows_sandbox(
|
|||
"windows sandbox: failed to resolve codex_home: {err}"
|
||||
)))
|
||||
})?;
|
||||
let use_elevated = is_windows_elevated_sandbox_enabled();
|
||||
let use_elevated = matches!(windows_sandbox_level, WindowsSandboxLevel::Elevated);
|
||||
let spawn_res = tokio::task::spawn_blocking(move || {
|
||||
if use_elevated {
|
||||
run_windows_sandbox_capture_elevated(
|
||||
|
|
@ -584,6 +594,7 @@ async fn exec(
|
|||
env,
|
||||
arg0,
|
||||
expiration,
|
||||
windows_sandbox_level: _,
|
||||
..
|
||||
} = params;
|
||||
|
||||
|
|
@ -965,6 +976,7 @@ mod tests {
|
|||
expiration: 500.into(),
|
||||
env,
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
justification: None,
|
||||
arg0: None,
|
||||
};
|
||||
|
|
@ -1010,6 +1022,7 @@ mod tests {
|
|||
expiration: ExecExpiration::Cancellation(cancel_token),
|
||||
env,
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel::Disabled,
|
||||
justification: None,
|
||||
arg0: None,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -126,9 +126,6 @@ pub use exec_policy::ExecPolicyError;
|
|||
pub use exec_policy::check_execpolicy_for_warnings;
|
||||
pub use exec_policy::load_exec_policy;
|
||||
pub use safety::get_platform_sandbox;
|
||||
pub use safety::is_windows_elevated_sandbox_enabled;
|
||||
pub use safety::set_windows_elevated_sandbox_enabled;
|
||||
pub use safety::set_windows_sandbox_enabled;
|
||||
pub use tools::spec::parse_tool_input_schema;
|
||||
// Re-export the protocol types from the standalone `codex-protocol` crate so existing
|
||||
// `codex_core::protocol::...` references continue to work across the workspace.
|
||||
|
|
|
|||
|
|
@ -10,45 +10,7 @@ use crate::util::resolve_path;
|
|||
|
||||
use crate::protocol::AskForApproval;
|
||||
use crate::protocol::SandboxPolicy;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::sync::atomic::AtomicBool;
|
||||
#[cfg(target_os = "windows")]
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
static WINDOWS_SANDBOX_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
#[cfg(target_os = "windows")]
|
||||
static WINDOWS_ELEVATED_SANDBOX_ENABLED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn set_windows_sandbox_enabled(enabled: bool) {
|
||||
WINDOWS_SANDBOX_ENABLED.store(enabled, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_windows_sandbox_enabled(_enabled: bool) {}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn set_windows_elevated_sandbox_enabled(enabled: bool) {
|
||||
WINDOWS_ELEVATED_SANDBOX_ENABLED.store(enabled, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[allow(dead_code)]
|
||||
pub fn set_windows_elevated_sandbox_enabled(_enabled: bool) {}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn is_windows_elevated_sandbox_enabled() -> bool {
|
||||
WINDOWS_ELEVATED_SANDBOX_ENABLED.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[allow(dead_code)]
|
||||
pub fn is_windows_elevated_sandbox_enabled() -> bool {
|
||||
false
|
||||
}
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum SafetyCheck {
|
||||
|
|
@ -67,6 +29,7 @@ pub fn assess_patch_safety(
|
|||
policy: AskForApproval,
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
cwd: &Path,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
) -> SafetyCheck {
|
||||
if action.is_empty() {
|
||||
return SafetyCheck::Reject {
|
||||
|
|
@ -104,7 +67,7 @@ pub fn assess_patch_safety(
|
|||
// Only auto‑approve when we can actually enforce a sandbox. Otherwise
|
||||
// fall back to asking the user because the patch may touch arbitrary
|
||||
// paths outside the project.
|
||||
match get_platform_sandbox() {
|
||||
match get_platform_sandbox(windows_sandbox_level != WindowsSandboxLevel::Disabled) {
|
||||
Some(sandbox_type) => SafetyCheck::AutoApprove {
|
||||
sandbox_type,
|
||||
user_explicitly_approved: false,
|
||||
|
|
@ -122,19 +85,17 @@ pub fn assess_patch_safety(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_platform_sandbox() -> Option<SandboxType> {
|
||||
pub fn get_platform_sandbox(windows_sandbox_enabled: bool) -> Option<SandboxType> {
|
||||
if cfg!(target_os = "macos") {
|
||||
Some(SandboxType::MacosSeatbelt)
|
||||
} else if cfg!(target_os = "linux") {
|
||||
Some(SandboxType::LinuxSeccomp)
|
||||
} else if cfg!(target_os = "windows") {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if WINDOWS_SANDBOX_ENABLED.load(Ordering::Relaxed) {
|
||||
return Some(SandboxType::WindowsRestrictedToken);
|
||||
}
|
||||
if windows_sandbox_enabled {
|
||||
Some(SandboxType::WindowsRestrictedToken)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
None
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
@ -277,7 +238,13 @@ mod tests {
|
|||
};
|
||||
|
||||
assert_eq!(
|
||||
assess_patch_safety(&add_inside, AskForApproval::OnRequest, &policy, &cwd),
|
||||
assess_patch_safety(
|
||||
&add_inside,
|
||||
AskForApproval::OnRequest,
|
||||
&policy,
|
||||
&cwd,
|
||||
WindowsSandboxLevel::Disabled
|
||||
),
|
||||
SafetyCheck::AutoApprove {
|
||||
sandbox_type: SandboxType::None,
|
||||
user_explicitly_approved: false,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use crate::seatbelt::create_seatbelt_command_args;
|
|||
use crate::spawn::CODEX_SANDBOX_ENV_VAR;
|
||||
use crate::spawn::CODEX_SANDBOX_NETWORK_DISABLED_ENV_VAR;
|
||||
use crate::tools::sandboxing::SandboxablePreference;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
pub use codex_protocol::models::SandboxPermissions;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
|
@ -44,6 +45,7 @@ pub struct ExecEnv {
|
|||
pub env: HashMap<String, String>,
|
||||
pub expiration: ExecExpiration,
|
||||
pub sandbox: SandboxType,
|
||||
pub windows_sandbox_level: WindowsSandboxLevel,
|
||||
pub sandbox_permissions: SandboxPermissions,
|
||||
pub justification: Option<String>,
|
||||
pub arg0: Option<String>,
|
||||
|
|
@ -76,19 +78,26 @@ impl SandboxManager {
|
|||
&self,
|
||||
policy: &SandboxPolicy,
|
||||
pref: SandboxablePreference,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
) -> SandboxType {
|
||||
match pref {
|
||||
SandboxablePreference::Forbid => SandboxType::None,
|
||||
SandboxablePreference::Require => {
|
||||
// Require a platform sandbox when available; on Windows this
|
||||
// respects the experimental_windows_sandbox feature.
|
||||
crate::safety::get_platform_sandbox().unwrap_or(SandboxType::None)
|
||||
crate::safety::get_platform_sandbox(
|
||||
windows_sandbox_level != WindowsSandboxLevel::Disabled,
|
||||
)
|
||||
.unwrap_or(SandboxType::None)
|
||||
}
|
||||
SandboxablePreference::Auto => match policy {
|
||||
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => {
|
||||
SandboxType::None
|
||||
}
|
||||
_ => crate::safety::get_platform_sandbox().unwrap_or(SandboxType::None),
|
||||
_ => crate::safety::get_platform_sandbox(
|
||||
windows_sandbox_level != WindowsSandboxLevel::Disabled,
|
||||
)
|
||||
.unwrap_or(SandboxType::None),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
@ -100,6 +109,7 @@ impl SandboxManager {
|
|||
sandbox: SandboxType,
|
||||
sandbox_policy_cwd: &Path,
|
||||
codex_linux_sandbox_exe: Option<&PathBuf>,
|
||||
windows_sandbox_level: WindowsSandboxLevel,
|
||||
) -> Result<ExecEnv, SandboxTransformError> {
|
||||
let mut env = spec.env;
|
||||
if !policy.has_full_network_access() {
|
||||
|
|
@ -160,6 +170,7 @@ impl SandboxManager {
|
|||
env,
|
||||
expiration: spec.expiration,
|
||||
sandbox,
|
||||
windows_sandbox_level,
|
||||
sandbox_permissions: spec.sandbox_permissions,
|
||||
justification: spec.justification,
|
||||
arg0: arg0_override,
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ impl SessionTask for UserShellCommandTask {
|
|||
// should use that instead of an "arbitrarily large" timeout here.
|
||||
expiration: USER_SHELL_TIMEOUT_MS.into(),
|
||||
sandbox: SandboxType::None,
|
||||
windows_sandbox_level: turn_context.windows_sandbox_level,
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
justification: None,
|
||||
arg0: None,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ impl ShellHandler {
|
|||
expiration: params.timeout_ms.into(),
|
||||
env: create_env(&turn_context.shell_environment_policy),
|
||||
sandbox_permissions: params.sandbox_permissions.unwrap_or_default(),
|
||||
windows_sandbox_level: turn_context.windows_sandbox_level,
|
||||
justification: params.justification,
|
||||
arg0: None,
|
||||
}
|
||||
|
|
@ -62,6 +63,7 @@ impl ShellCommandHandler {
|
|||
expiration: params.timeout_ms.into(),
|
||||
env: create_env(&turn_context.shell_environment_policy),
|
||||
sandbox_permissions: params.sandbox_permissions.unwrap_or_default(),
|
||||
windows_sandbox_level: turn_context.windows_sandbox_level,
|
||||
justification: params.justification,
|
||||
arg0: None,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,19 +88,22 @@ impl ToolOrchestrator {
|
|||
// 2) First attempt under the selected sandbox.
|
||||
let initial_sandbox = match tool.sandbox_mode_for_first_attempt(req) {
|
||||
SandboxOverride::BypassSandboxFirstAttempt => crate::exec::SandboxType::None,
|
||||
SandboxOverride::NoOverride => self
|
||||
.sandbox
|
||||
.select_initial(&turn_ctx.sandbox_policy, tool.sandbox_preference()),
|
||||
SandboxOverride::NoOverride => self.sandbox.select_initial(
|
||||
&turn_ctx.sandbox_policy,
|
||||
tool.sandbox_preference(),
|
||||
turn_ctx.windows_sandbox_level,
|
||||
),
|
||||
};
|
||||
|
||||
// Platform-specific flag gating is handled by SandboxManager::select_initial
|
||||
// via crate::safety::get_platform_sandbox().
|
||||
// via crate::safety::get_platform_sandbox(..).
|
||||
let initial_attempt = SandboxAttempt {
|
||||
sandbox: initial_sandbox,
|
||||
policy: &turn_ctx.sandbox_policy,
|
||||
manager: &self.sandbox,
|
||||
sandbox_cwd: &turn_ctx.cwd,
|
||||
codex_linux_sandbox_exe: turn_ctx.codex_linux_sandbox_exe.as_ref(),
|
||||
windows_sandbox_level: turn_ctx.windows_sandbox_level,
|
||||
};
|
||||
|
||||
match tool.run(req, &initial_attempt, tool_ctx).await {
|
||||
|
|
@ -151,6 +154,7 @@ impl ToolOrchestrator {
|
|||
manager: &self.sandbox,
|
||||
sandbox_cwd: &turn_ctx.cwd,
|
||||
codex_linux_sandbox_exe: None,
|
||||
windows_sandbox_level: turn_ctx.windows_sandbox_level,
|
||||
};
|
||||
|
||||
// Second attempt.
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ pub(crate) struct SandboxAttempt<'a> {
|
|||
pub(crate) manager: &'a SandboxManager,
|
||||
pub(crate) sandbox_cwd: &'a Path,
|
||||
pub codex_linux_sandbox_exe: Option<&'a std::path::PathBuf>,
|
||||
pub windows_sandbox_level: codex_protocol::config_types::WindowsSandboxLevel,
|
||||
}
|
||||
|
||||
impl<'a> SandboxAttempt<'a> {
|
||||
|
|
@ -287,6 +288,7 @@ impl<'a> SandboxAttempt<'a> {
|
|||
self.sandbox,
|
||||
self.sandbox_cwd,
|
||||
self.codex_linux_sandbox_exe,
|
||||
self.windows_sandbox_level,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
use crate::config::Config;
|
||||
use crate::features::Feature;
|
||||
use crate::features::Features;
|
||||
use crate::protocol::SandboxPolicy;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
|
|
@ -8,6 +12,36 @@ use std::path::Path;
|
|||
/// prompts users to enable the legacy sandbox feature.
|
||||
pub const ELEVATED_SANDBOX_NUX_ENABLED: bool = true;
|
||||
|
||||
pub trait WindowsSandboxLevelExt {
|
||||
fn from_config(config: &Config) -> WindowsSandboxLevel;
|
||||
fn from_features(features: &Features) -> WindowsSandboxLevel;
|
||||
}
|
||||
|
||||
impl WindowsSandboxLevelExt for WindowsSandboxLevel {
|
||||
fn from_config(config: &Config) -> WindowsSandboxLevel {
|
||||
Self::from_features(&config.features)
|
||||
}
|
||||
|
||||
fn from_features(features: &Features) -> WindowsSandboxLevel {
|
||||
if !features.enabled(Feature::WindowsSandbox) {
|
||||
return WindowsSandboxLevel::Disabled;
|
||||
}
|
||||
if features.enabled(Feature::WindowsSandboxElevated) {
|
||||
WindowsSandboxLevel::Elevated
|
||||
} else {
|
||||
WindowsSandboxLevel::RestrictedToken
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn windows_sandbox_level_from_config(config: &Config) -> WindowsSandboxLevel {
|
||||
WindowsSandboxLevel::from_config(config)
|
||||
}
|
||||
|
||||
pub fn windows_sandbox_level_from_features(features: &Features) -> WindowsSandboxLevel {
|
||||
WindowsSandboxLevel::from_features(features)
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn sandbox_setup_is_complete(codex_home: &Path) -> bool {
|
||||
codex_windows_sandbox::sandbox_setup_is_complete(codex_home)
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@ async fn user_input_includes_collaboration_instructions_after_override() -> Resu
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -185,6 +186,7 @@ async fn override_then_user_turn_uses_updated_collaboration_instructions() -> Re
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -238,6 +240,7 @@ async fn user_turn_overrides_collaboration_instructions_after_override() -> Resu
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -292,6 +295,7 @@ async fn collaboration_mode_update_emits_new_instruction_message() -> Result<()>
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -316,6 +320,7 @@ async fn collaboration_mode_update_emits_new_instruction_message() -> Result<()>
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -361,6 +366,7 @@ async fn collaboration_mode_update_noop_does_not_append() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -385,6 +391,7 @@ async fn collaboration_mode_update_noop_does_not_append() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -436,6 +443,7 @@ async fn resume_replays_collaboration_instructions() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -491,6 +499,7 @@ async fn empty_collaboration_instructions_are_ignored() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use codex_core::exec::process_exec_tool_call;
|
|||
use codex_core::protocol::SandboxPolicy;
|
||||
use codex_core::sandboxing::SandboxPermissions;
|
||||
use codex_core::spawn::CODEX_SANDBOX_ENV_VAR;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use tempfile::TempDir;
|
||||
|
||||
use codex_core::error::Result;
|
||||
|
|
@ -27,7 +28,7 @@ fn skip_test() -> bool {
|
|||
|
||||
#[expect(clippy::expect_used)]
|
||||
async fn run_test_cmd(tmp: TempDir, cmd: Vec<&str>) -> Result<ExecToolCallOutput> {
|
||||
let sandbox_type = get_platform_sandbox().expect("should be able to get sandbox type");
|
||||
let sandbox_type = get_platform_sandbox(false).expect("should be able to get sandbox type");
|
||||
assert_eq!(sandbox_type, SandboxType::MacosSeatbelt);
|
||||
|
||||
let params = ExecParams {
|
||||
|
|
@ -36,6 +37,7 @@ async fn run_test_cmd(tmp: TempDir, cmd: Vec<&str>) -> Result<ExecToolCallOutput
|
|||
expiration: 1000.into(),
|
||||
env: HashMap::new(),
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
justification: None,
|
||||
arg0: None,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ async fn override_turn_context_does_not_persist_when_config_exists() {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: Some("o3".to_string()),
|
||||
effort: Some(Some(ReasoningEffort::High)),
|
||||
summary: None,
|
||||
|
|
@ -64,6 +65,7 @@ async fn override_turn_context_does_not_create_config_file() {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: Some("o3".to_string()),
|
||||
effort: Some(Some(ReasoningEffort::Medium)),
|
||||
summary: None,
|
||||
|
|
|
|||
|
|
@ -118,6 +118,7 @@ async fn override_turn_context_records_permissions_update() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: Some(AskForApproval::Never),
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -161,6 +162,7 @@ async fn override_turn_context_records_environment_update() -> Result<()> {
|
|||
cwd: Some(new_cwd.path().to_path_buf()),
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -198,6 +200,7 @@ async fn override_turn_context_records_collaboration_update() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
|
|||
|
|
@ -106,6 +106,7 @@ async fn permissions_message_added_on_override_change() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: Some(AskForApproval::Never),
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -227,6 +228,7 @@ async fn resume_replays_permissions_messages() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: Some(AskForApproval::Never),
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -309,6 +311,7 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: Some(AskForApproval::Never),
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@ async fn user_turn_personality_some_adds_update_message() -> anyhow::Result<()>
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -362,6 +363,7 @@ async fn user_turn_personality_remote_model_template_includes_update_message() -
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: Some(remote_slug.to_string()),
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
|
|||
|
|
@ -350,6 +350,7 @@ async fn overrides_turn_context_but_keeps_cached_prefix_and_key_constant() -> an
|
|||
cwd: None,
|
||||
approval_policy: Some(AskForApproval::Never),
|
||||
sandbox_policy: Some(new_policy.clone()),
|
||||
windows_sandbox_level: None,
|
||||
model: Some("o3".to_string()),
|
||||
effort: Some(Some(ReasoningEffort::High)),
|
||||
summary: Some(ReasoningSummary::Detailed),
|
||||
|
|
@ -427,6 +428,7 @@ async fn override_before_first_turn_emits_environment_context() -> anyhow::Resul
|
|||
cwd: None,
|
||||
approval_policy: Some(AskForApproval::Never),
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: Some("gpt-5.1-codex".to_string()),
|
||||
effort: Some(Some(ReasoningEffort::Low)),
|
||||
summary: None,
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ async fn remote_models_remote_model_uses_unified_exec() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: Some(REMOTE_MODEL_SLUG.to_string()),
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -367,6 +368,7 @@ async fn remote_models_apply_remote_base_instructions() -> Result<()> {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: Some(model.to_string()),
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
|
|||
|
|
@ -819,6 +819,7 @@ async fn review_uses_overridden_cwd_for_base_branch_merge_base() {
|
|||
cwd: Some(repo_path.to_path_buf()),
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use path_absolutize::Absolutize as _;
|
|||
|
||||
use codex_core::SandboxState;
|
||||
use codex_core::exec::process_exec_tool_call;
|
||||
use codex_core::protocol_config_types::WindowsSandboxLevel;
|
||||
use codex_core::sandboxing::SandboxPermissions;
|
||||
use tokio::process::Command;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
|
@ -87,6 +88,7 @@ impl EscalateServer {
|
|||
expiration: ExecExpiration::Cancellation(cancel_rx),
|
||||
env,
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
justification: None,
|
||||
arg0: None,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use codex_core::exec::ExecParams;
|
|||
use codex_core::exec::process_exec_tool_call;
|
||||
use codex_core::exec_env::create_env;
|
||||
use codex_core::protocol::SandboxPolicy;
|
||||
use codex_core::protocol_config_types::WindowsSandboxLevel;
|
||||
use codex_core::sandboxing::SandboxPermissions;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
|
@ -60,6 +61,7 @@ async fn run_cmd_output(
|
|||
expiration: timeout_ms.into(),
|
||||
env: create_env_from_core_vars(),
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
justification: None,
|
||||
arg0: None,
|
||||
};
|
||||
|
|
@ -177,6 +179,7 @@ async fn assert_network_blocked(cmd: &[&str]) {
|
|||
expiration: NETWORK_TIMEOUT_MS.into(),
|
||||
env: create_env_from_core_vars(),
|
||||
sandbox_permissions: SandboxPermissions::UseDefault,
|
||||
windows_sandbox_level: WindowsSandboxLevel::Disabled,
|
||||
justification: None,
|
||||
arg0: None,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,6 +66,18 @@ pub enum SandboxMode {
|
|||
DangerFullAccess,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Display, JsonSchema, TS,
|
||||
)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum WindowsSandboxLevel {
|
||||
#[default]
|
||||
Disabled,
|
||||
RestrictedToken,
|
||||
Elevated,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Serialize,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use crate::approvals::ElicitationRequestEvent;
|
|||
use crate::config_types::CollaborationMode;
|
||||
use crate::config_types::Personality;
|
||||
use crate::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||
use crate::config_types::WindowsSandboxLevel;
|
||||
use crate::custom_prompts::CustomPrompt;
|
||||
use crate::dynamic_tools::DynamicToolCallRequest;
|
||||
use crate::dynamic_tools::DynamicToolResponse;
|
||||
|
|
@ -158,6 +159,10 @@ pub enum Op {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
sandbox_policy: Option<SandboxPolicy>,
|
||||
|
||||
/// Updated Windows sandbox mode for tool execution.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
windows_sandbox_level: Option<WindowsSandboxLevel>,
|
||||
|
||||
/// Updated model slug. When set, the model info is derived
|
||||
/// automatically.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ use codex_core::config::ConfigOverrides;
|
|||
use codex_core::config::edit::ConfigEdit;
|
||||
use codex_core::config::edit::ConfigEditsBuilder;
|
||||
use codex_core::config_loader::ConfigLayerStackOrdering;
|
||||
#[cfg(target_os = "windows")]
|
||||
use codex_core::features::Feature;
|
||||
use codex_core::models_manager::manager::RefreshStrategy;
|
||||
use codex_core::models_manager::model_presets::HIDE_GPT_5_1_CODEX_MAX_MIGRATION_PROMPT_CONFIG;
|
||||
|
|
@ -58,9 +57,13 @@ use codex_core::protocol::SandboxPolicy;
|
|||
use codex_core::protocol::SessionSource;
|
||||
use codex_core::protocol::SkillErrorInfo;
|
||||
use codex_core::protocol::TokenUsage;
|
||||
#[cfg(target_os = "windows")]
|
||||
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_otel::OtelManager;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::Personality;
|
||||
#[cfg(target_os = "windows")]
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::items::TurnItem;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::openai_models::ModelUpgrade;
|
||||
|
|
@ -1088,7 +1091,8 @@ impl App {
|
|||
// On startup, if Agent mode (workspace-write) or ReadOnly is active, warn about world-writable dirs on Windows.
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let should_check = codex_core::get_platform_sandbox().is_some()
|
||||
let should_check = WindowsSandboxLevel::from_config(&app.config)
|
||||
!= WindowsSandboxLevel::Disabled
|
||||
&& matches!(
|
||||
app.config.sandbox_policy.get(),
|
||||
codex_core::protocol::SandboxPolicy::WorkspaceWrite { .. }
|
||||
|
|
@ -1684,9 +1688,24 @@ impl App {
|
|||
elevated_enabled,
|
||||
);
|
||||
self.chat_widget.clear_forced_auto_mode_downgrade();
|
||||
let windows_sandbox_level =
|
||||
WindowsSandboxLevel::from_config(&self.config);
|
||||
if let Some((sample_paths, extra_count, failed_scan)) =
|
||||
self.chat_widget.world_writable_warning_details()
|
||||
{
|
||||
self.app_event_tx.send(AppEvent::CodexOp(
|
||||
Op::OverrideTurnContext {
|
||||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: Some(windows_sandbox_level),
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
collaboration_mode: None,
|
||||
personality: None,
|
||||
},
|
||||
));
|
||||
self.app_event_tx.send(
|
||||
AppEvent::OpenWorldWritableWarningConfirmation {
|
||||
preset: Some(preset.clone()),
|
||||
|
|
@ -1701,6 +1720,7 @@ impl App {
|
|||
cwd: None,
|
||||
approval_policy: Some(preset.approval),
|
||||
sandbox_policy: Some(preset.sandbox.clone()),
|
||||
windows_sandbox_level: Some(windows_sandbox_level),
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -1839,7 +1859,8 @@ impl App {
|
|||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
if !matches!(&policy, codex_core::protocol::SandboxPolicy::ReadOnly)
|
||||
|| codex_core::get_platform_sandbox().is_some()
|
||||
|| WindowsSandboxLevel::from_config(&self.config)
|
||||
!= WindowsSandboxLevel::Disabled
|
||||
{
|
||||
self.config.forced_auto_mode_downgraded_on_windows = false;
|
||||
}
|
||||
|
|
@ -1861,7 +1882,8 @@ impl App {
|
|||
return Ok(AppRunControl::Continue);
|
||||
}
|
||||
|
||||
let should_check = codex_core::get_platform_sandbox().is_some()
|
||||
let should_check = WindowsSandboxLevel::from_config(&self.config)
|
||||
!= WindowsSandboxLevel::Disabled
|
||||
&& policy_is_workspace_write_or_ro
|
||||
&& !self.chat_widget.world_writable_warning_hidden();
|
||||
if should_check {
|
||||
|
|
@ -1885,6 +1907,12 @@ impl App {
|
|||
if updates.is_empty() {
|
||||
return Ok(AppRunControl::Continue);
|
||||
}
|
||||
let windows_sandbox_changed = updates.iter().any(|(feature, _)| {
|
||||
matches!(
|
||||
feature,
|
||||
Feature::WindowsSandbox | Feature::WindowsSandboxElevated
|
||||
)
|
||||
});
|
||||
let mut builder = ConfigEditsBuilder::new(&self.config.codex_home)
|
||||
.with_profile(self.active_profile.as_deref());
|
||||
for (feature, enabled) in &updates {
|
||||
|
|
@ -1910,6 +1938,24 @@ impl App {
|
|||
}
|
||||
}
|
||||
}
|
||||
if windows_sandbox_changed {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config);
|
||||
self.app_event_tx
|
||||
.send(AppEvent::CodexOp(Op::OverrideTurnContext {
|
||||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: Some(windows_sandbox_level),
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
collaboration_mode: None,
|
||||
personality: None,
|
||||
}));
|
||||
}
|
||||
}
|
||||
if let Err(err) = builder.apply().await {
|
||||
tracing::error!(error = %err, "failed to persist feature flags");
|
||||
self.chat_widget.add_error_message(format!(
|
||||
|
|
|
|||
|
|
@ -178,6 +178,9 @@ pub(crate) enum AppEvent {
|
|||
mode: WindowsSandboxEnableMode,
|
||||
},
|
||||
|
||||
/// Update the Windows sandbox feature mode without changing approval presets.
|
||||
#[cfg_attr(not(target_os = "windows"), allow(dead_code))]
|
||||
|
||||
/// Update the current approval policy in the running app and widget.
|
||||
UpdateAskForApprovalPolicy(AskForApproval),
|
||||
|
||||
|
|
|
|||
|
|
@ -273,6 +273,7 @@ pub(crate) struct ChatComposer {
|
|||
config: ChatComposerConfig,
|
||||
collaboration_mode_indicator: Option<CollaborationModeIndicator>,
|
||||
personality_command_enabled: bool,
|
||||
windows_degraded_sandbox_active: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
|
@ -358,6 +359,7 @@ impl ChatComposer {
|
|||
config,
|
||||
collaboration_mode_indicator: None,
|
||||
personality_command_enabled: false,
|
||||
windows_degraded_sandbox_active: false,
|
||||
};
|
||||
// Apply configuration via the setter to keep side-effects centralized.
|
||||
this.set_disable_paste_burst(disable_paste_burst);
|
||||
|
|
@ -405,6 +407,10 @@ impl ChatComposer {
|
|||
fn image_paste_enabled(&self) -> bool {
|
||||
self.config.image_paste_enabled
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn set_windows_degraded_sandbox_active(&mut self, enabled: bool) {
|
||||
self.windows_degraded_sandbox_active = enabled;
|
||||
}
|
||||
fn layout_areas(&self, area: Rect) -> [Rect; 3] {
|
||||
let footer_props = self.footer_props();
|
||||
let footer_hint_height = self
|
||||
|
|
@ -1728,6 +1734,7 @@ impl ChatComposer {
|
|||
name,
|
||||
self.collaboration_modes_enabled,
|
||||
self.personality_command_enabled,
|
||||
self.windows_degraded_sandbox_active,
|
||||
)
|
||||
.is_some();
|
||||
let prompt_prefix = format!("{PROMPTS_CMD_PREFIX}:");
|
||||
|
|
@ -1904,6 +1911,7 @@ impl ChatComposer {
|
|||
name,
|
||||
self.collaboration_modes_enabled,
|
||||
self.personality_command_enabled,
|
||||
self.windows_degraded_sandbox_active,
|
||||
)
|
||||
{
|
||||
self.textarea.set_text_clearing_elements("");
|
||||
|
|
@ -1931,6 +1939,7 @@ impl ChatComposer {
|
|||
name,
|
||||
self.collaboration_modes_enabled,
|
||||
self.personality_command_enabled,
|
||||
self.windows_degraded_sandbox_active,
|
||||
)
|
||||
&& cmd == SlashCommand::Review
|
||||
{
|
||||
|
|
@ -2391,6 +2400,7 @@ impl ChatComposer {
|
|||
name,
|
||||
self.collaboration_modes_enabled,
|
||||
self.personality_command_enabled,
|
||||
self.windows_degraded_sandbox_active,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2447,6 +2457,7 @@ impl ChatComposer {
|
|||
CommandPopupFlags {
|
||||
collaboration_modes_enabled,
|
||||
personality_command_enabled,
|
||||
windows_degraded_sandbox_active: self.windows_degraded_sandbox_active,
|
||||
},
|
||||
);
|
||||
command_popup.on_composer_text_change(first_line.to_string());
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ pub(crate) struct CommandPopup {
|
|||
pub(crate) struct CommandPopupFlags {
|
||||
pub(crate) collaboration_modes_enabled: bool,
|
||||
pub(crate) personality_command_enabled: bool,
|
||||
pub(crate) windows_degraded_sandbox_active: bool,
|
||||
}
|
||||
|
||||
impl CommandPopup {
|
||||
|
|
@ -41,6 +42,7 @@ impl CommandPopup {
|
|||
let builtins = slash_commands::builtins_for_input(
|
||||
flags.collaboration_modes_enabled,
|
||||
flags.personality_command_enabled,
|
||||
flags.windows_degraded_sandbox_active,
|
||||
);
|
||||
// Exclude prompts that collide with builtin command names and sort by name.
|
||||
let exclude: HashSet<String> = builtins.iter().map(|(n, _)| (*n).to_string()).collect();
|
||||
|
|
@ -461,6 +463,7 @@ mod tests {
|
|||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
personality_command_enabled: true,
|
||||
windows_degraded_sandbox_active: false,
|
||||
},
|
||||
);
|
||||
popup.on_composer_text_change("/collab".to_string());
|
||||
|
|
@ -478,6 +481,7 @@ mod tests {
|
|||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
personality_command_enabled: false,
|
||||
windows_degraded_sandbox_active: false,
|
||||
},
|
||||
);
|
||||
popup.on_composer_text_change("/pers".to_string());
|
||||
|
|
@ -503,6 +507,7 @@ mod tests {
|
|||
CommandPopupFlags {
|
||||
collaboration_modes_enabled: true,
|
||||
personality_command_enabled: true,
|
||||
windows_degraded_sandbox_active: false,
|
||||
},
|
||||
);
|
||||
popup.on_composer_text_change("/personality".to_string());
|
||||
|
|
|
|||
|
|
@ -213,6 +213,12 @@ impl BottomPane {
|
|||
self.request_redraw();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn set_windows_degraded_sandbox_active(&mut self, enabled: bool) {
|
||||
self.composer.set_windows_degraded_sandbox_active(enabled);
|
||||
self.request_redraw();
|
||||
}
|
||||
|
||||
pub fn set_collaboration_mode_indicator(
|
||||
&mut self,
|
||||
indicator: Option<CollaborationModeIndicator>,
|
||||
|
|
|
|||
|
|
@ -8,20 +8,12 @@ use codex_common::fuzzy_match::fuzzy_match;
|
|||
use crate::slash_command::SlashCommand;
|
||||
use crate::slash_command::built_in_slash_commands;
|
||||
|
||||
/// Whether the Windows degraded-sandbox elevation flow is currently allowed.
|
||||
pub(crate) fn windows_degraded_sandbox_active() -> bool {
|
||||
cfg!(target_os = "windows")
|
||||
&& codex_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
|
||||
&& codex_core::get_platform_sandbox().is_some()
|
||||
&& !codex_core::is_windows_elevated_sandbox_enabled()
|
||||
}
|
||||
|
||||
/// Return the built-ins that should be visible/usable for the current input.
|
||||
pub(crate) fn builtins_for_input(
|
||||
collaboration_modes_enabled: bool,
|
||||
personality_command_enabled: bool,
|
||||
allow_elevate_sandbox: bool,
|
||||
) -> Vec<(&'static str, SlashCommand)> {
|
||||
let allow_elevate_sandbox = windows_degraded_sandbox_active();
|
||||
built_in_slash_commands()
|
||||
.into_iter()
|
||||
.filter(|(_, cmd)| allow_elevate_sandbox || *cmd != SlashCommand::ElevateSandbox)
|
||||
|
|
@ -35,11 +27,16 @@ pub(crate) fn find_builtin_command(
|
|||
name: &str,
|
||||
collaboration_modes_enabled: bool,
|
||||
personality_command_enabled: bool,
|
||||
allow_elevate_sandbox: bool,
|
||||
) -> Option<SlashCommand> {
|
||||
builtins_for_input(collaboration_modes_enabled, personality_command_enabled)
|
||||
.into_iter()
|
||||
.find(|(command_name, _)| *command_name == name)
|
||||
.map(|(_, cmd)| cmd)
|
||||
builtins_for_input(
|
||||
collaboration_modes_enabled,
|
||||
personality_command_enabled,
|
||||
allow_elevate_sandbox,
|
||||
)
|
||||
.into_iter()
|
||||
.find(|(command_name, _)| *command_name == name)
|
||||
.map(|(_, cmd)| cmd)
|
||||
}
|
||||
|
||||
/// Whether any visible built-in fuzzily matches the provided prefix.
|
||||
|
|
@ -47,8 +44,13 @@ pub(crate) fn has_builtin_prefix(
|
|||
name: &str,
|
||||
collaboration_modes_enabled: bool,
|
||||
personality_command_enabled: bool,
|
||||
allow_elevate_sandbox: bool,
|
||||
) -> bool {
|
||||
builtins_for_input(collaboration_modes_enabled, personality_command_enabled)
|
||||
.into_iter()
|
||||
.any(|(command_name, _)| fuzzy_match(command_name, name).is_some())
|
||||
builtins_for_input(
|
||||
collaboration_modes_enabled,
|
||||
personality_command_enabled,
|
||||
allow_elevate_sandbox,
|
||||
)
|
||||
.into_iter()
|
||||
.any(|(command_name, _)| fuzzy_match(command_name, name).is_some())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ use codex_core::protocol::WarningEvent;
|
|||
use codex_core::protocol::WebSearchBeginEvent;
|
||||
use codex_core::protocol::WebSearchEndEvent;
|
||||
use codex_core::skills::model::SkillMetadata;
|
||||
#[cfg(target_os = "windows")]
|
||||
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_otel::OtelManager;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::account::PlanType;
|
||||
|
|
@ -97,6 +99,8 @@ use codex_protocol::config_types::CollaborationModeMask;
|
|||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::Settings;
|
||||
#[cfg(target_os = "windows")]
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::models::local_image_label_text;
|
||||
use codex_protocol::parse_command::ParsedCommand;
|
||||
use codex_protocol::request_user_input::RequestUserInputEvent;
|
||||
|
|
@ -2057,6 +2061,14 @@ impl ChatWidget {
|
|||
widget.config.features.enabled(Feature::CollaborationModes),
|
||||
);
|
||||
widget.sync_personality_command_enabled();
|
||||
#[cfg(target_os = "windows")]
|
||||
widget.bottom_pane.set_windows_degraded_sandbox_active(
|
||||
codex_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
|
||||
&& matches!(
|
||||
WindowsSandboxLevel::from_config(&widget.config),
|
||||
WindowsSandboxLevel::RestrictedToken
|
||||
),
|
||||
);
|
||||
widget.update_collaboration_mode_indicator();
|
||||
|
||||
widget
|
||||
|
|
@ -2308,6 +2320,14 @@ impl ChatWidget {
|
|||
widget.config.features.enabled(Feature::CollaborationModes),
|
||||
);
|
||||
widget.sync_personality_command_enabled();
|
||||
#[cfg(target_os = "windows")]
|
||||
widget.bottom_pane.set_windows_degraded_sandbox_active(
|
||||
codex_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
|
||||
&& matches!(
|
||||
WindowsSandboxLevel::from_config(&widget.config),
|
||||
WindowsSandboxLevel::RestrictedToken
|
||||
),
|
||||
);
|
||||
widget.update_collaboration_mode_indicator();
|
||||
|
||||
widget
|
||||
|
|
@ -2563,9 +2583,9 @@ impl ChatWidget {
|
|||
SlashCommand::ElevateSandbox => {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let windows_degraded_sandbox_enabled = codex_core::get_platform_sandbox()
|
||||
.is_some()
|
||||
&& !codex_core::is_windows_elevated_sandbox_enabled();
|
||||
let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config);
|
||||
let windows_degraded_sandbox_enabled =
|
||||
matches!(windows_sandbox_level, WindowsSandboxLevel::RestrictedToken);
|
||||
if !windows_degraded_sandbox_enabled
|
||||
|| !codex_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
|
||||
{
|
||||
|
|
@ -3340,6 +3360,7 @@ impl ChatWidget {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: Some(switch_model.clone()),
|
||||
effort: Some(Some(default_effort)),
|
||||
summary: None,
|
||||
|
|
@ -3462,6 +3483,7 @@ impl ChatWidget {
|
|||
effort: None,
|
||||
summary: None,
|
||||
collaboration_mode: None,
|
||||
windows_sandbox_level: None,
|
||||
personality: Some(personality),
|
||||
}));
|
||||
tx.send(AppEvent::UpdatePersonality(personality));
|
||||
|
|
@ -3731,6 +3753,7 @@ impl ChatWidget {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: Some(model_for_action.clone()),
|
||||
effort: Some(effort_for_action),
|
||||
summary: None,
|
||||
|
|
@ -3904,6 +3927,7 @@ impl ChatWidget {
|
|||
cwd: None,
|
||||
approval_policy: None,
|
||||
sandbox_policy: None,
|
||||
windows_sandbox_level: None,
|
||||
model: Some(model.clone()),
|
||||
effort: Some(effort),
|
||||
summary: None,
|
||||
|
|
@ -3944,8 +3968,10 @@ impl ChatWidget {
|
|||
let presets: Vec<ApprovalPreset> = builtin_approval_presets();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let windows_degraded_sandbox_enabled = codex_core::get_platform_sandbox().is_some()
|
||||
&& !codex_core::is_windows_elevated_sandbox_enabled();
|
||||
let windows_sandbox_level = WindowsSandboxLevel::from_config(&self.config);
|
||||
#[cfg(target_os = "windows")]
|
||||
let windows_degraded_sandbox_enabled =
|
||||
matches!(windows_sandbox_level, WindowsSandboxLevel::RestrictedToken);
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let windows_degraded_sandbox_enabled = false;
|
||||
|
||||
|
|
@ -3986,7 +4012,9 @@ impl ChatWidget {
|
|||
} else if preset.id == "auto" {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if codex_core::get_platform_sandbox().is_none() {
|
||||
if WindowsSandboxLevel::from_config(&self.config)
|
||||
== WindowsSandboxLevel::Disabled
|
||||
{
|
||||
let preset_clone = preset.clone();
|
||||
if codex_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
|
||||
&& codex_core::windows_sandbox::sandbox_setup_is_complete(
|
||||
|
|
@ -4088,6 +4116,7 @@ impl ChatWidget {
|
|||
cwd: None,
|
||||
approval_policy: Some(approval),
|
||||
sandbox_policy: Some(sandbox_clone.clone()),
|
||||
windows_sandbox_level: None,
|
||||
model: None,
|
||||
effort: None,
|
||||
summary: None,
|
||||
|
|
@ -4591,7 +4620,7 @@ impl ChatWidget {
|
|||
#[cfg(target_os = "windows")]
|
||||
pub(crate) fn maybe_prompt_windows_sandbox_enable(&mut self) {
|
||||
if self.config.forced_auto_mode_downgraded_on_windows
|
||||
&& codex_core::get_platform_sandbox().is_none()
|
||||
&& WindowsSandboxLevel::from_config(&self.config) == WindowsSandboxLevel::Disabled
|
||||
&& let Some(preset) = builtin_approval_presets()
|
||||
.into_iter()
|
||||
.find(|preset| preset.id == "auto")
|
||||
|
|
@ -4651,7 +4680,7 @@ impl ChatWidget {
|
|||
pub(crate) fn set_sandbox_policy(&mut self, policy: SandboxPolicy) -> ConstraintResult<()> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let should_clear_downgrade = !matches!(&policy, SandboxPolicy::ReadOnly)
|
||||
|| codex_core::get_platform_sandbox().is_some();
|
||||
|| WindowsSandboxLevel::from_config(&self.config) != WindowsSandboxLevel::Disabled;
|
||||
|
||||
self.config.sandbox_policy.set(policy)?;
|
||||
|
||||
|
|
@ -4685,6 +4714,19 @@ impl ChatWidget {
|
|||
self.refresh_model_display();
|
||||
self.request_redraw();
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
if matches!(
|
||||
feature,
|
||||
Feature::WindowsSandbox | Feature::WindowsSandboxElevated
|
||||
) {
|
||||
self.bottom_pane.set_windows_degraded_sandbox_active(
|
||||
codex_core::windows_sandbox::ELEVATED_SANDBOX_NUX_ENABLED
|
||||
&& matches!(
|
||||
WindowsSandboxLevel::from_config(&self.config),
|
||||
WindowsSandboxLevel::RestrictedToken
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn set_full_access_warning_acknowledged(&mut self, acknowledged: bool) {
|
||||
|
|
|
|||
|
|
@ -92,16 +92,6 @@ use tokio::sync::mpsc::error::TryRecvError;
|
|||
use tokio::sync::mpsc::unbounded_channel;
|
||||
use toml::Value as TomlValue;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn set_windows_sandbox_enabled(enabled: bool) {
|
||||
codex_core::set_windows_sandbox_enabled(enabled);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
fn set_windows_elevated_sandbox_enabled(enabled: bool) {
|
||||
codex_core::set_windows_elevated_sandbox_enabled(enabled);
|
||||
}
|
||||
|
||||
async fn test_config() -> Config {
|
||||
// Use base defaults to avoid depending on host state.
|
||||
let codex_home = std::env::temp_dir();
|
||||
|
|
@ -3050,16 +3040,9 @@ async fn approvals_selection_popup_snapshot() {
|
|||
async fn approvals_selection_popup_snapshot_windows_degraded_sandbox() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
let was_sandbox_enabled = codex_core::get_platform_sandbox().is_some();
|
||||
let was_elevated_enabled = codex_core::is_windows_elevated_sandbox_enabled();
|
||||
|
||||
chat.config.notices.hide_full_access_warning = None;
|
||||
chat.config.features.enable(Feature::WindowsSandbox);
|
||||
chat.config
|
||||
.features
|
||||
.disable(Feature::WindowsSandboxElevated);
|
||||
set_windows_sandbox_enabled(true);
|
||||
set_windows_elevated_sandbox_enabled(false);
|
||||
chat.set_feature_enabled(Feature::WindowsSandbox, true);
|
||||
chat.set_feature_enabled(Feature::WindowsSandboxElevated, false);
|
||||
|
||||
chat.open_approvals_popup();
|
||||
|
||||
|
|
@ -3067,10 +3050,6 @@ async fn approvals_selection_popup_snapshot_windows_degraded_sandbox() {
|
|||
insta::with_settings!({ snapshot_suffix => "windows_degraded" }, {
|
||||
assert_snapshot!("approvals_selection_popup", popup);
|
||||
});
|
||||
|
||||
// Avoid leaking sandbox global state into other tests.
|
||||
set_windows_sandbox_enabled(was_sandbox_enabled);
|
||||
set_windows_elevated_sandbox_enabled(was_elevated_enabled);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
@ -3133,7 +3112,8 @@ async fn windows_auto_mode_prompt_requests_enabling_sandbox_feature() {
|
|||
async fn startup_prompts_for_windows_sandbox_when_agent_requested() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(None).await;
|
||||
|
||||
set_windows_sandbox_enabled(false);
|
||||
chat.set_feature_enabled(Feature::WindowsSandbox, false);
|
||||
chat.set_feature_enabled(Feature::WindowsSandboxElevated, false);
|
||||
chat.config.forced_auto_mode_downgraded_on_windows = true;
|
||||
|
||||
chat.maybe_prompt_windows_sandbox_enable();
|
||||
|
|
@ -3151,8 +3131,6 @@ async fn startup_prompts_for_windows_sandbox_when_agent_requested() {
|
|||
popup.contains("Stay in"),
|
||||
"expected startup prompt to offer staying in current mode: {popup}"
|
||||
);
|
||||
|
||||
set_windows_sandbox_enabled(true);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
|||
|
|
@ -26,13 +26,14 @@ use codex_core::config::resolve_oss_provider;
|
|||
use codex_core::config_loader::ConfigLoadError;
|
||||
use codex_core::config_loader::format_config_error_with_source;
|
||||
use codex_core::find_thread_path_by_id_str;
|
||||
use codex_core::get_platform_sandbox;
|
||||
use codex_core::path_utils;
|
||||
use codex_core::protocol::AskForApproval;
|
||||
use codex_core::read_session_meta_line;
|
||||
use codex_core::terminal::Multiplexer;
|
||||
use codex_core::windows_sandbox::WindowsSandboxLevelExt;
|
||||
use codex_protocol::config_types::AltScreenMode;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::protocol::RolloutItem;
|
||||
use codex_protocol::protocol::RolloutLine;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
|
@ -816,7 +817,9 @@ async fn load_config_or_exit_with_fallback_cwd(
|
|||
/// or if the current cwd project is already trusted. If not, we need to
|
||||
/// show the trust screen.
|
||||
fn should_show_trust_screen(config: &Config) -> bool {
|
||||
if cfg!(target_os = "windows") && get_platform_sandbox().is_none() {
|
||||
if cfg!(target_os = "windows")
|
||||
&& WindowsSandboxLevel::from_config(config) == WindowsSandboxLevel::Disabled
|
||||
{
|
||||
// If the experimental sandbox is not enabled, Native Windows cannot enforce sandboxed write access; skip the trust prompt entirely.
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue