mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
permissions: centralize legacy sandbox projection (#19734)
## Why The remaining migration work still needs `SandboxPolicy` at a few compatibility boundaries, but those projections should come from one canonical path. Keeping ad hoc legacy projections scattered through app-server, CLI, and config code makes it easy for behavior to drift as `PermissionProfile` gains fidelity that the legacy enum cannot represent. ## What Changed - Adds `Permissions::legacy_sandbox_policy(cwd)` and `Config::legacy_sandbox_policy()` as the compatibility projection from the canonical `PermissionProfile`. - Adds `Permissions::can_set_legacy_sandbox_policy()` so legacy inputs are checked after they are converted into profile semantics. - Updates app-server command handling, Windows sandbox setup, session configuration, and sandbox summaries to use the centralized projection helper. - Leaves `SandboxPolicy` in place only for boundary inputs/outputs that still speak the legacy abstraction. ## Verification - `cargo check -p codex-config -p codex-core -p codex-sandboxing -p codex-app-server -p codex-cli -p codex-tui` - `cargo test -p codex-tui permissions_selection_history_snapshot_full_access_to_default -- --nocapture` - `cargo test -p codex-tui permissions_selection_sends_approvals_reviewer_in_override_turn_context -- --nocapture` - `bazel test //codex-rs/tui:tui-unit-tests-bin --test_arg=permissions_selection_history_snapshot_full_access_to_default --test_output=errors` - `bazel test //codex-rs/tui:tui-unit-tests-bin --test_arg=permissions_selection_sends_approvals_reviewer_in_override_turn_context --test_output=errors` --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19734). * #19737 * #19736 * #19735 * __->__ #19734
This commit is contained in:
committed by
GitHub
Unverified
parent
c3e60849e5
commit
0d8cdc0510
@@ -2307,7 +2307,11 @@ impl CodexMessageProcessor {
|
||||
}
|
||||
}
|
||||
} else if let Some(policy) = sandbox_policy.map(|policy| policy.to_core()) {
|
||||
match self.config.permissions.sandbox_policy.can_set(&policy) {
|
||||
match self
|
||||
.config
|
||||
.permissions
|
||||
.can_set_legacy_sandbox_policy(&policy, &sandbox_cwd)
|
||||
{
|
||||
Ok(()) => {
|
||||
let file_system_sandbox_policy =
|
||||
codex_protocol::permissions::FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&policy, &sandbox_cwd);
|
||||
@@ -8705,7 +8709,9 @@ impl CodexMessageProcessor {
|
||||
Ok(config) => {
|
||||
let setup_request = WindowsSandboxSetupRequest {
|
||||
mode,
|
||||
policy: config.permissions.sandbox_policy.get().clone(),
|
||||
policy: config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
policy_cwd: config.cwd.to_path_buf(),
|
||||
command_cwd,
|
||||
env_map: std::env::vars().collect(),
|
||||
|
||||
@@ -227,7 +227,9 @@ async fn run_command_under_sandbox(
|
||||
let args = create_linux_sandbox_command_args_for_policies(
|
||||
command,
|
||||
cwd.as_path(),
|
||||
config.permissions.sandbox_policy.get(),
|
||||
&config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(sandbox_policy_cwd.as_path()),
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
sandbox_policy_cwd.as_path(),
|
||||
@@ -290,7 +292,10 @@ async fn run_command_under_windows_session(
|
||||
use codex_windows_sandbox::spawn_windows_sandbox_session_elevated;
|
||||
use codex_windows_sandbox::spawn_windows_sandbox_session_legacy;
|
||||
|
||||
let policy_str = match serde_json::to_string(config.permissions.sandbox_policy.get()) {
|
||||
let sandbox_policy = config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(sandbox_policy_cwd.as_path());
|
||||
let policy_str = match serde_json::to_string(&sandbox_policy) {
|
||||
Ok(policy_str) => policy_str,
|
||||
Err(err) => {
|
||||
eprintln!("windows sandbox failed to serialize policy: {err}");
|
||||
|
||||
@@ -237,6 +237,37 @@ impl Permissions {
|
||||
self.permission_profile.get().network_sandbox_policy()
|
||||
}
|
||||
|
||||
/// Legacy compatibility projection derived from the canonical profile.
|
||||
pub fn legacy_sandbox_policy(&self, cwd: &Path) -> SandboxPolicy {
|
||||
let permission_profile = self.permission_profile.get();
|
||||
let file_system_sandbox_policy = permission_profile.file_system_sandbox_policy();
|
||||
compatibility_sandbox_policy_for_permission_profile(
|
||||
permission_profile,
|
||||
&file_system_sandbox_policy,
|
||||
permission_profile.network_sandbox_policy(),
|
||||
cwd,
|
||||
)
|
||||
}
|
||||
|
||||
/// Check whether a legacy sandbox policy can be applied to this permission
|
||||
/// set under both legacy and canonical profile constraints.
|
||||
pub fn can_set_legacy_sandbox_policy(
|
||||
&self,
|
||||
sandbox_policy: &SandboxPolicy,
|
||||
cwd: &Path,
|
||||
) -> ConstraintResult<()> {
|
||||
self.sandbox_policy.can_set(sandbox_policy)?;
|
||||
let file_system_sandbox_policy =
|
||||
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(sandbox_policy, cwd);
|
||||
let network_sandbox_policy = NetworkSandboxPolicy::from(sandbox_policy);
|
||||
let permission_profile = PermissionProfile::from_runtime_permissions_with_enforcement(
|
||||
SandboxEnforcement::from_legacy_sandbox_policy(sandbox_policy),
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
);
|
||||
self.permission_profile.can_set(&permission_profile)
|
||||
}
|
||||
|
||||
/// Replace permissions from a legacy sandbox policy and keep every
|
||||
/// permission projection in sync.
|
||||
pub fn set_legacy_sandbox_policy(
|
||||
@@ -244,7 +275,7 @@ impl Permissions {
|
||||
sandbox_policy: SandboxPolicy,
|
||||
cwd: &Path,
|
||||
) -> ConstraintResult<()> {
|
||||
self.sandbox_policy.can_set(&sandbox_policy)?;
|
||||
self.can_set_legacy_sandbox_policy(&sandbox_policy, cwd)?;
|
||||
let file_system_sandbox_policy =
|
||||
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(&sandbox_policy, cwd);
|
||||
let network_sandbox_policy = NetworkSandboxPolicy::from(&sandbox_policy);
|
||||
@@ -253,7 +284,6 @@ impl Permissions {
|
||||
&file_system_sandbox_policy,
|
||||
network_sandbox_policy,
|
||||
);
|
||||
self.permission_profile.can_set(&permission_profile)?;
|
||||
|
||||
self.sandbox_policy.set(sandbox_policy)?;
|
||||
self.permission_profile.set(permission_profile)?;
|
||||
|
||||
@@ -634,7 +634,9 @@ impl Session {
|
||||
config.model_context_window,
|
||||
config.model_auto_compact_token_limit,
|
||||
config.permissions.approval_policy.value(),
|
||||
config.permissions.sandbox_policy.get().clone(),
|
||||
config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(session_configuration.cwd.as_path()),
|
||||
mcp_servers.keys().map(String::as_str).collect(),
|
||||
config.active_profile.clone(),
|
||||
);
|
||||
|
||||
@@ -939,10 +939,14 @@ 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 startup_sandbox_policy = app
|
||||
.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(app.config.cwd.as_path());
|
||||
let should_check = WindowsSandboxLevel::from_config(&app.config)
|
||||
!= WindowsSandboxLevel::Disabled
|
||||
&& matches!(
|
||||
app.config.permissions.sandbox_policy.get(),
|
||||
&startup_sandbox_policy,
|
||||
codex_protocol::protocol::SandboxPolicy::WorkspaceWrite { .. }
|
||||
| codex_protocol::protocol::SandboxPolicy::ReadOnly { .. }
|
||||
)
|
||||
@@ -956,7 +960,7 @@ impl App {
|
||||
let env_map: std::collections::HashMap<String, String> = std::env::vars().collect();
|
||||
let tx = app.app_event_tx.clone();
|
||||
let logs_base_dir = app.config.codex_home.clone();
|
||||
let sandbox_policy = app.config.permissions.sandbox_policy.get().clone();
|
||||
let sandbox_policy = startup_sandbox_policy;
|
||||
Self::spawn_world_writable_scan(cwd, env_map, logs_base_dir, sandbox_policy, tx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,9 +300,11 @@ impl App {
|
||||
.set_approval_policy(self.config.permissions.approval_policy.value());
|
||||
}
|
||||
if sandbox_policy_override.is_some()
|
||||
&& let Err(err) = self
|
||||
.chat_widget
|
||||
.set_sandbox_policy(self.config.permissions.sandbox_policy.get().clone())
|
||||
&& let Err(err) = self.chat_widget.set_sandbox_policy(
|
||||
self.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path()),
|
||||
)
|
||||
{
|
||||
tracing::error!(
|
||||
error = %err,
|
||||
@@ -312,8 +314,11 @@ impl App {
|
||||
.add_error_message(format!("Failed to enable Auto-review: {err}"));
|
||||
}
|
||||
if sandbox_policy_override.is_some() {
|
||||
self.runtime_sandbox_policy_override =
|
||||
Some(self.config.permissions.sandbox_policy.get().clone());
|
||||
self.runtime_sandbox_policy_override = Some(
|
||||
self.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path()),
|
||||
);
|
||||
}
|
||||
|
||||
if approval_policy_override.is_some()
|
||||
|
||||
@@ -834,7 +834,10 @@ impl App {
|
||||
/*hint*/ None,
|
||||
));
|
||||
|
||||
let policy = self.config.permissions.sandbox_policy.get().clone();
|
||||
let policy = self
|
||||
.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path());
|
||||
let policy_cwd = self.config.cwd.clone();
|
||||
let command_cwd = self.config.cwd.clone();
|
||||
let env_map: std::collections::HashMap<String, String> =
|
||||
@@ -1245,8 +1248,11 @@ impl App {
|
||||
.add_error_message(format!("Failed to set sandbox policy: {err}"));
|
||||
return Ok(AppRunControl::Continue);
|
||||
}
|
||||
self.runtime_sandbox_policy_override =
|
||||
Some(self.config.permissions.sandbox_policy.get().clone());
|
||||
self.runtime_sandbox_policy_override = Some(
|
||||
self.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path()),
|
||||
);
|
||||
self.sync_active_thread_permission_settings_to_cached_session()
|
||||
.await;
|
||||
|
||||
@@ -1269,7 +1275,10 @@ impl App {
|
||||
std::env::vars().collect();
|
||||
let tx = self.app_event_tx.clone();
|
||||
let logs_base_dir = self.config.codex_home.clone();
|
||||
let sandbox_policy = self.config.permissions.sandbox_policy.get().clone();
|
||||
let sandbox_policy = self
|
||||
.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path());
|
||||
Self::spawn_world_writable_scan(
|
||||
cwd,
|
||||
env_map,
|
||||
|
||||
@@ -12,7 +12,10 @@ impl App {
|
||||
|
||||
let approval_policy = self.config.permissions.approval_policy.value();
|
||||
let approvals_reviewer = self.config.approvals_reviewer;
|
||||
let sandbox_policy = self.config.permissions.sandbox_policy.get().clone();
|
||||
let sandbox_policy = self
|
||||
.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path());
|
||||
let permission_profile = Some(
|
||||
self.chat_widget
|
||||
.config_ref()
|
||||
@@ -45,7 +48,10 @@ impl App {
|
||||
thread_id: ThreadId,
|
||||
thread: &Thread,
|
||||
) -> ThreadSessionState {
|
||||
let sandbox_policy = self.config.permissions.sandbox_policy.get().clone();
|
||||
let sandbox_policy = self
|
||||
.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path());
|
||||
let mut session = self
|
||||
.primary_session_configured
|
||||
.clone()
|
||||
@@ -185,8 +191,10 @@ mod tests {
|
||||
app.chat_widget
|
||||
.set_sandbox_policy(expected_sandbox_policy.clone())
|
||||
.expect("set widget sandbox policy");
|
||||
app.config.permissions.sandbox_policy =
|
||||
codex_config::Constrained::allow_any(expected_sandbox_policy.clone());
|
||||
app.config
|
||||
.permissions
|
||||
.set_legacy_sandbox_policy(expected_sandbox_policy.clone(), app.config.cwd.as_path())
|
||||
.expect("set app sandbox policy");
|
||||
|
||||
app.sync_active_thread_permission_settings_to_cached_session()
|
||||
.await;
|
||||
|
||||
@@ -1143,7 +1143,13 @@ fn thread_start_params_from_config(
|
||||
let permission_profile = permission_profile_override_from_config(config, thread_params_mode);
|
||||
let sandbox = permission_profile
|
||||
.is_none()
|
||||
.then(|| sandbox_mode_from_policy(config.permissions.sandbox_policy.get().clone()))
|
||||
.then(|| {
|
||||
sandbox_mode_from_policy(
|
||||
config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
)
|
||||
})
|
||||
.flatten();
|
||||
ThreadStartParams {
|
||||
model: config.model.clone(),
|
||||
@@ -1170,7 +1176,13 @@ fn thread_resume_params_from_config(
|
||||
let permission_profile = permission_profile_override_from_config(&config, thread_params_mode);
|
||||
let sandbox = permission_profile
|
||||
.is_none()
|
||||
.then(|| sandbox_mode_from_policy(config.permissions.sandbox_policy.get().clone()))
|
||||
.then(|| {
|
||||
sandbox_mode_from_policy(
|
||||
config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
)
|
||||
})
|
||||
.flatten();
|
||||
ThreadResumeParams {
|
||||
thread_id: thread_id.to_string(),
|
||||
@@ -1196,7 +1208,13 @@ fn thread_fork_params_from_config(
|
||||
let permission_profile = permission_profile_override_from_config(&config, thread_params_mode);
|
||||
let sandbox = permission_profile
|
||||
.is_none()
|
||||
.then(|| sandbox_mode_from_policy(config.permissions.sandbox_policy.get().clone()))
|
||||
.then(|| {
|
||||
sandbox_mode_from_policy(
|
||||
config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
)
|
||||
})
|
||||
.flatten();
|
||||
ThreadForkParams {
|
||||
thread_id: thread_id.to_string(),
|
||||
@@ -1522,8 +1540,11 @@ mod tests {
|
||||
let temp_dir = tempfile::tempdir().expect("tempdir");
|
||||
let config = build_config(&temp_dir).await;
|
||||
let thread_id = ThreadId::new();
|
||||
let expected_sandbox =
|
||||
sandbox_mode_from_policy(config.permissions.sandbox_policy.get().clone());
|
||||
let expected_sandbox = sandbox_mode_from_policy(
|
||||
config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
);
|
||||
|
||||
let start = thread_start_params_from_config(
|
||||
&config,
|
||||
@@ -1564,8 +1585,11 @@ mod tests {
|
||||
let config = build_config(&temp_dir).await;
|
||||
let thread_id = ThreadId::new();
|
||||
let remote_cwd = PathBuf::from("repo/on/server");
|
||||
let expected_sandbox =
|
||||
sandbox_mode_from_policy(config.permissions.sandbox_policy.get().clone());
|
||||
let expected_sandbox = sandbox_mode_from_policy(
|
||||
config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
);
|
||||
|
||||
let start = thread_start_params_from_config(
|
||||
&config,
|
||||
|
||||
@@ -6415,7 +6415,9 @@ impl ChatWidget {
|
||||
items,
|
||||
self.config.cwd.to_path_buf(),
|
||||
self.config.permissions.approval_policy.value(),
|
||||
self.config.permissions.sandbox_policy.get().clone(),
|
||||
self.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path()),
|
||||
permission_profile,
|
||||
effective_mode.model().to_string(),
|
||||
effective_mode.reasoning_effort(),
|
||||
@@ -9466,7 +9468,10 @@ impl ChatWidget {
|
||||
pub(crate) fn open_permissions_popup(&mut self) {
|
||||
let include_read_only = cfg!(target_os = "windows");
|
||||
let current_approval = self.config.permissions.approval_policy.value();
|
||||
let current_sandbox = self.config.permissions.sandbox_policy.get();
|
||||
let current_sandbox = self
|
||||
.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path());
|
||||
let guardian_approval_enabled = self.config.features.enabled(Feature::GuardianApproval);
|
||||
let current_review_policy = self.config.approvals_reviewer;
|
||||
let mut items: Vec<SelectionItem> = Vec::new();
|
||||
@@ -9600,7 +9605,11 @@ impl ChatWidget {
|
||||
name: base_name.clone(),
|
||||
description: base_description.clone(),
|
||||
is_current: current_review_policy == ApprovalsReviewer::User
|
||||
&& Self::preset_matches_current(current_approval, current_sandbox, &preset),
|
||||
&& Self::preset_matches_current(
|
||||
current_approval,
|
||||
¤t_sandbox,
|
||||
&preset,
|
||||
),
|
||||
actions: default_actions,
|
||||
dismiss_on_select: true,
|
||||
disabled_reason: default_disabled_reason,
|
||||
@@ -9617,7 +9626,7 @@ impl ChatWidget {
|
||||
is_current: current_review_policy == ApprovalsReviewer::AutoReview
|
||||
&& Self::preset_matches_current(
|
||||
current_approval,
|
||||
current_sandbox,
|
||||
¤t_sandbox,
|
||||
&preset,
|
||||
),
|
||||
actions: Self::approval_preset_actions(
|
||||
@@ -9638,7 +9647,7 @@ impl ChatWidget {
|
||||
description: base_description,
|
||||
is_current: Self::preset_matches_current(
|
||||
current_approval,
|
||||
current_sandbox,
|
||||
¤t_sandbox,
|
||||
&preset,
|
||||
),
|
||||
actions: default_actions,
|
||||
@@ -9774,7 +9783,10 @@ impl ChatWidget {
|
||||
self.config.codex_home.as_path(),
|
||||
cwd.as_path(),
|
||||
&env_map,
|
||||
self.config.permissions.sandbox_policy.get(),
|
||||
&self
|
||||
.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path()),
|
||||
Some(self.config.codex_home.as_path()),
|
||||
) {
|
||||
Ok(_) => None,
|
||||
@@ -9892,7 +9904,14 @@ impl ChatWidget {
|
||||
let mode_label = preset
|
||||
.as_ref()
|
||||
.map(|p| describe_policy(&p.sandbox))
|
||||
.unwrap_or_else(|| describe_policy(self.config.permissions.sandbox_policy.get()));
|
||||
.unwrap_or_else(|| {
|
||||
describe_policy(
|
||||
&self
|
||||
.config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(self.config.cwd.as_path()),
|
||||
)
|
||||
});
|
||||
let info_line = if failed_scan {
|
||||
Line::from(vec![
|
||||
"We couldn't complete the world-writable scan, so protections cannot be verified. "
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
use super::*;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
fn set_legacy_sandbox_policy(chat: &mut ChatWidget, sandbox_policy: SandboxPolicy) {
|
||||
chat.config
|
||||
.permissions
|
||||
.set_legacy_sandbox_policy(sandbox_policy, chat.config.cwd.as_path())
|
||||
.expect("set sandbox policy");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn approvals_selection_popup_snapshot() {
|
||||
let (mut chat, _rx, _op_rx) = make_chatwidget_manual(/*model_override*/ None).await;
|
||||
@@ -347,8 +354,7 @@ async fn permissions_selection_history_snapshot_full_access_to_default() {
|
||||
.approval_policy
|
||||
.set(AskForApproval::Never)
|
||||
.expect("set approval policy");
|
||||
chat.config.permissions.sandbox_policy =
|
||||
Constrained::allow_any(SandboxPolicy::DangerFullAccess);
|
||||
set_legacy_sandbox_policy(&mut chat, SandboxPolicy::DangerFullAccess);
|
||||
|
||||
chat.open_permissions_popup();
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 120);
|
||||
@@ -387,11 +393,7 @@ async fn permissions_selection_emits_history_cell_when_current_is_selected() {
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
chat.config
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::new_workspace_write_policy())
|
||||
.expect("set sandbox policy");
|
||||
set_legacy_sandbox_policy(&mut chat, SandboxPolicy::new_workspace_write_policy());
|
||||
|
||||
chat.open_permissions_popup();
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
|
||||
@@ -446,11 +448,7 @@ async fn permissions_selection_hides_auto_review_when_feature_disabled_even_if_a
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
chat.config
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::new_workspace_write_policy())
|
||||
.expect("set sandbox policy");
|
||||
set_legacy_sandbox_policy(&mut chat, SandboxPolicy::new_workspace_write_policy());
|
||||
|
||||
chat.open_permissions_popup();
|
||||
let popup = render_bottom_popup(&chat, /*width*/ 120);
|
||||
@@ -575,11 +573,7 @@ async fn permissions_selection_can_disable_auto_review() {
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
chat.config
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::new_workspace_write_policy())
|
||||
.expect("set sandbox policy");
|
||||
set_legacy_sandbox_policy(&mut chat, SandboxPolicy::new_workspace_write_policy());
|
||||
|
||||
chat.open_permissions_popup();
|
||||
chat.handle_key_event(KeyEvent::from(KeyCode::Up));
|
||||
@@ -616,11 +610,7 @@ async fn permissions_selection_sends_approvals_reviewer_in_override_turn_context
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
chat.config
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::new_workspace_write_policy())
|
||||
.expect("set sandbox policy");
|
||||
set_legacy_sandbox_policy(&mut chat, SandboxPolicy::new_workspace_write_policy());
|
||||
chat.set_approvals_reviewer(ApprovalsReviewer::User);
|
||||
|
||||
chat.open_permissions_popup();
|
||||
|
||||
@@ -1313,7 +1313,9 @@ pub(crate) fn new_session_info(
|
||||
pub(crate) fn is_yolo_mode(config: &Config) -> bool {
|
||||
has_yolo_permissions(
|
||||
config.permissions.approval_policy.value(),
|
||||
config.permissions.sandbox_policy.get(),
|
||||
&config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+11
-4
@@ -874,9 +874,12 @@ pub async fn run_main(
|
||||
|
||||
set_default_client_residency_requirement(config.enforce_residency.value());
|
||||
|
||||
if let Some(warning) =
|
||||
add_dir_warning_message(&cli.add_dir, config.permissions.sandbox_policy.get())
|
||||
{
|
||||
if let Some(warning) = add_dir_warning_message(
|
||||
&cli.add_dir,
|
||||
&config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
) {
|
||||
#[allow(clippy::print_stderr)]
|
||||
{
|
||||
eprintln!("Error adding directories: {warning}");
|
||||
@@ -2205,7 +2208,9 @@ mod tests {
|
||||
current_date: None,
|
||||
timezone: None,
|
||||
approval_policy: config.permissions.approval_policy.value(),
|
||||
sandbox_policy: config.permissions.sandbox_policy.get().clone(),
|
||||
sandbox_policy: config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
permission_profile: None,
|
||||
network: None,
|
||||
file_system_sandbox_policy: None,
|
||||
@@ -2328,6 +2333,7 @@ trust_level = "untrusted"
|
||||
..Default::default()
|
||||
};
|
||||
let trusted_config = ConfigBuilder::default()
|
||||
.loader_overrides(LoaderOverrides::without_managed_config_for_tests())
|
||||
.codex_home(codex_home.clone())
|
||||
.harness_overrides(trusted_overrides.clone())
|
||||
.build()
|
||||
@@ -2342,6 +2348,7 @@ trust_level = "untrusted"
|
||||
..trusted_overrides
|
||||
};
|
||||
let untrusted_config = ConfigBuilder::default()
|
||||
.loader_overrides(LoaderOverrides::without_managed_config_for_tests())
|
||||
.codex_home(codex_home)
|
||||
.harness_overrides(untrusted_overrides)
|
||||
.build()
|
||||
|
||||
@@ -254,7 +254,11 @@ impl StatusHistoryCell {
|
||||
),
|
||||
(
|
||||
"sandbox",
|
||||
summarize_sandbox_policy(config.permissions.sandbox_policy.get()),
|
||||
summarize_sandbox_policy(
|
||||
&config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
),
|
||||
),
|
||||
];
|
||||
if config.model_provider.wire_api == WireApi::Responses {
|
||||
@@ -277,7 +281,10 @@ impl StatusHistoryCell {
|
||||
.find(|(k, _)| *k == "approval")
|
||||
.map(|(_, v)| v.clone())
|
||||
.unwrap_or_else(|| "<unknown>".to_string());
|
||||
let sandbox = match config.permissions.sandbox_policy.get() {
|
||||
let sandbox_policy = config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path());
|
||||
let sandbox = match &sandbox_policy {
|
||||
SandboxPolicy::DangerFullAccess => "danger-full-access".to_string(),
|
||||
SandboxPolicy::ReadOnly { .. } => "read-only".to_string(),
|
||||
SandboxPolicy::WorkspaceWrite {
|
||||
@@ -294,12 +301,11 @@ impl StatusHistoryCell {
|
||||
}
|
||||
};
|
||||
let permissions = if config.permissions.approval_policy.value() == AskForApproval::OnRequest
|
||||
&& *config.permissions.sandbox_policy.get()
|
||||
== SandboxPolicy::new_workspace_write_policy()
|
||||
&& sandbox_policy == SandboxPolicy::new_workspace_write_policy()
|
||||
{
|
||||
"Default".to_string()
|
||||
} else if config.permissions.approval_policy.value() == AskForApproval::Never
|
||||
&& *config.permissions.sandbox_policy.get() == SandboxPolicy::DangerFullAccess
|
||||
&& sandbox_policy == SandboxPolicy::DangerFullAccess
|
||||
{
|
||||
"Full Access".to_string()
|
||||
} else {
|
||||
|
||||
@@ -97,19 +97,20 @@ async fn status_snapshot_includes_reasoning_details() {
|
||||
config.model = Some("gpt-5.1-codex-max".to_string());
|
||||
config.model_provider_id = "openai".to_string();
|
||||
config.model_reasoning_summary = Some(ReasoningSummary::Detailed);
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
config
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: Vec::new(),
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
})
|
||||
.set_legacy_sandbox_policy(
|
||||
SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: Vec::new(),
|
||||
network_access: false,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
},
|
||||
config.cwd.as_path(),
|
||||
)
|
||||
.expect("set sandbox policy");
|
||||
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage {
|
||||
input_tokens: 1_200,
|
||||
@@ -182,17 +183,19 @@ async fn status_permissions_non_default_workspace_write_is_custom() {
|
||||
.approval_policy
|
||||
.set(AskForApproval::OnRequest)
|
||||
.expect("set approval policy");
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
config
|
||||
.permissions
|
||||
.sandbox_policy
|
||||
.set(SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: Vec::new(),
|
||||
network_access: true,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
})
|
||||
.set_legacy_sandbox_policy(
|
||||
SandboxPolicy::WorkspaceWrite {
|
||||
writable_roots: Vec::new(),
|
||||
network_access: true,
|
||||
exclude_tmpdir_env_var: false,
|
||||
exclude_slash_tmp: false,
|
||||
},
|
||||
config.cwd.as_path(),
|
||||
)
|
||||
.expect("set sandbox policy");
|
||||
config.cwd = test_path_buf("/workspace/tests").abs();
|
||||
|
||||
let account_display = test_status_account_display();
|
||||
let usage = TokenUsage::default();
|
||||
|
||||
@@ -15,7 +15,11 @@ pub fn create_config_summary_entries(config: &Config, model: &str) -> Vec<(&'sta
|
||||
),
|
||||
(
|
||||
"sandbox",
|
||||
summarize_sandbox_policy(config.permissions.sandbox_policy.get()),
|
||||
summarize_sandbox_policy(
|
||||
&config
|
||||
.permissions
|
||||
.legacy_sandbox_policy(config.cwd.as_path()),
|
||||
),
|
||||
),
|
||||
];
|
||||
if config.model_provider.wire_api == WireApi::Responses {
|
||||
|
||||
Reference in New Issue
Block a user