permissions: store only constrained permission profiles (#19735)

This commit is contained in:
Michael Bolin
2026-04-26 20:59:58 -07:00
committed by GitHub
Unverified
parent 8033b6a449
commit 0ccd659b4b
32 changed files with 242 additions and 215 deletions
+5 -18
View File
@@ -1634,11 +1634,7 @@ async fn update_feature_flags_enabling_guardian_selects_auto_review() -> Result<
auto_review.approval_policy
);
assert_eq!(
app.chat_widget
.config_ref()
.permissions
.sandbox_policy
.get(),
&app.chat_widget.config_ref().legacy_sandbox_policy(),
&auto_review.sandbox_policy
);
assert_eq!(
@@ -1714,9 +1710,7 @@ async fn update_feature_flags_disabling_guardian_clears_review_policy_and_restor
.approval_policy
.set(AskForApproval::OnRequest)?;
app.config
.permissions
.sandbox_policy
.set(SandboxPolicy::new_workspace_write_policy())?;
.set_legacy_sandbox_policy(SandboxPolicy::new_workspace_write_policy())?;
app.chat_widget
.set_approval_policy(AskForApproval::OnRequest);
app.chat_widget
@@ -1815,11 +1809,7 @@ async fn update_feature_flags_enabling_guardian_overrides_explicit_manual_review
auto_review.approval_policy
);
assert_eq!(
app.chat_widget
.config_ref()
.permissions
.sandbox_policy
.get(),
&app.chat_widget.config_ref().legacy_sandbox_policy(),
&auto_review.sandbox_policy
);
assert_eq!(
@@ -2933,7 +2923,7 @@ async fn side_fork_config_is_ephemeral_and_appends_developer_guardrails() {
let mut app = make_test_app().await;
app.config.developer_instructions = Some("Existing developer policy.".to_string());
let original_approval_policy = app.config.permissions.approval_policy.value();
let original_sandbox_policy = app.config.permissions.sandbox_policy.get().clone();
let original_sandbox_policy = app.config.legacy_sandbox_policy();
let fork_config = app.side_fork_config();
@@ -2942,10 +2932,7 @@ async fn side_fork_config_is_ephemeral_and_appends_developer_guardrails() {
fork_config.permissions.approval_policy.value(),
original_approval_policy
);
assert_eq!(
fork_config.permissions.sandbox_policy.get(),
&original_sandbox_policy
);
assert_eq!(fork_config.legacy_sandbox_policy(), original_sandbox_policy);
let developer_instructions = fork_config
.developer_instructions
.as_deref()
+2 -3
View File
@@ -192,9 +192,8 @@ mod tests {
.set_sandbox_policy(expected_sandbox_policy.clone())
.expect("set widget sandbox policy");
app.config
.permissions
.set_legacy_sandbox_policy(expected_sandbox_policy.clone(), app.config.cwd.as_path())
.expect("set app sandbox policy");
.set_legacy_sandbox_policy(expected_sandbox_policy.clone())
.expect("set sandbox policy");
app.sync_active_thread_permission_settings_to_cached_session()
.await;
+14 -5
View File
@@ -141,8 +141,12 @@ use codex_protocol::items::AgentMessageContent;
use codex_protocol::items::AgentMessageItem;
use codex_protocol::items::UserMessageItem;
use codex_protocol::models::MessagePhase;
use codex_protocol::models::PermissionProfile;
use codex_protocol::models::SandboxEnforcement;
use codex_protocol::models::local_image_label_text;
use codex_protocol::parse_command::ParsedCommand;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::plan_tool::PlanItemArg as UpdatePlanItemArg;
use codex_protocol::plan_tool::StepStatus as UpdatePlanItemStatus;
#[cfg(test)]
@@ -2376,7 +2380,7 @@ impl ChatWidget {
Some(permission_profile) => self
.config
.permissions
.set_permission_profile(permission_profile, event.cwd.as_path()),
.set_permission_profile(permission_profile),
None => self
.config
.permissions
@@ -2384,11 +2388,16 @@ impl ChatWidget {
};
if let Err(err) = permission_sync {
tracing::warn!(%err, "failed to sync permissions from SessionConfigured");
self.config.permissions.sandbox_policy =
Constrained::allow_only(event.sandbox_policy.clone());
let permission_profile = event.permission_profile.clone().unwrap_or_else(|| {
codex_protocol::models::PermissionProfile::from_legacy_sandbox_policy(
&event.sandbox_policy,
let file_system_sandbox_policy =
FileSystemSandboxPolicy::from_legacy_sandbox_policy_for_cwd(
&event.sandbox_policy,
event.cwd.as_path(),
);
PermissionProfile::from_runtime_permissions_with_enforcement(
SandboxEnforcement::from_legacy_sandbox_policy(&event.sandbox_policy),
&file_system_sandbox_policy,
NetworkSandboxPolicy::from(&event.sandbox_policy),
)
});
self.config.permissions.permission_profile =
@@ -252,9 +252,7 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
.set(AskForApproval::OnRequest)
.expect("set approval policy");
chat.config
.permissions
.sandbox_policy
.set(SandboxPolicy::new_workspace_write_policy())
.set_legacy_sandbox_policy(SandboxPolicy::new_workspace_write_policy())
.expect("set sandbox policy");
chat.config.cwd = test_path_buf("/home/user/main").abs();
@@ -312,7 +310,7 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() {
AskForApproval::Never
);
assert_eq!(
chat.config_ref().permissions.sandbox_policy.get(),
&chat.config_ref().legacy_sandbox_policy(),
&expected_sandbox
);
assert_eq!(
@@ -374,7 +372,7 @@ async fn session_configured_external_sandbox_keeps_external_runtime_policy() {
});
assert_eq!(
chat.config_ref().permissions.sandbox_policy.get(),
&chat.config_ref().legacy_sandbox_policy(),
&expected_sandbox
);
assert_eq!(
@@ -1,13 +1,6 @@
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;
@@ -354,7 +347,9 @@ async fn permissions_selection_history_snapshot_full_access_to_default() {
.approval_policy
.set(AskForApproval::Never)
.expect("set approval policy");
set_legacy_sandbox_policy(&mut chat, SandboxPolicy::DangerFullAccess);
chat.config
.set_legacy_sandbox_policy(SandboxPolicy::DangerFullAccess)
.expect("set sandbox policy");
chat.open_permissions_popup();
let popup = render_bottom_popup(&chat, /*width*/ 120);
@@ -393,7 +388,9 @@ async fn permissions_selection_emits_history_cell_when_current_is_selected() {
.approval_policy
.set(AskForApproval::OnRequest)
.expect("set approval policy");
set_legacy_sandbox_policy(&mut chat, SandboxPolicy::new_workspace_write_policy());
chat.config
.set_legacy_sandbox_policy(SandboxPolicy::new_workspace_write_policy())
.expect("set sandbox policy");
chat.open_permissions_popup();
chat.handle_key_event(KeyEvent::from(KeyCode::Enter));
@@ -448,7 +445,9 @@ async fn permissions_selection_hides_auto_review_when_feature_disabled_even_if_a
.approval_policy
.set(AskForApproval::OnRequest)
.expect("set approval policy");
set_legacy_sandbox_policy(&mut chat, SandboxPolicy::new_workspace_write_policy());
chat.config
.set_legacy_sandbox_policy(SandboxPolicy::new_workspace_write_policy())
.expect("set sandbox policy");
chat.open_permissions_popup();
let popup = render_bottom_popup(&chat, /*width*/ 120);
@@ -573,7 +572,9 @@ async fn permissions_selection_can_disable_auto_review() {
.approval_policy
.set(AskForApproval::OnRequest)
.expect("set approval policy");
set_legacy_sandbox_policy(&mut chat, SandboxPolicy::new_workspace_write_policy());
chat.config
.set_legacy_sandbox_policy(SandboxPolicy::new_workspace_write_policy())
.expect("set sandbox policy");
chat.open_permissions_popup();
chat.handle_key_event(KeyEvent::from(KeyCode::Up));
@@ -610,7 +611,9 @@ async fn permissions_selection_sends_approvals_reviewer_in_override_turn_context
.approval_policy
.set(AskForApproval::OnRequest)
.expect("set approval policy");
set_legacy_sandbox_policy(&mut chat, SandboxPolicy::new_workspace_write_policy());
chat.config
.set_legacy_sandbox_policy(SandboxPolicy::new_workspace_write_policy())
.expect("set sandbox policy");
chat.set_approvals_reviewer(ApprovalsReviewer::User);
chat.open_permissions_popup();
+12 -20
View File
@@ -99,16 +99,12 @@ async fn status_snapshot_includes_reasoning_details() {
config.model_reasoning_summary = Some(ReasoningSummary::Detailed);
config.cwd = test_path_buf("/workspace/tests").abs();
config
.permissions
.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(),
)
.set_legacy_sandbox_policy(SandboxPolicy::WorkspaceWrite {
writable_roots: Vec::new(),
network_access: false,
exclude_tmpdir_env_var: false,
exclude_slash_tmp: false,
})
.expect("set sandbox policy");
let account_display = test_status_account_display();
@@ -185,16 +181,12 @@ async fn status_permissions_non_default_workspace_write_is_custom() {
.expect("set approval policy");
config.cwd = test_path_buf("/workspace/tests").abs();
config
.permissions
.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(),
)
.set_legacy_sandbox_policy(SandboxPolicy::WorkspaceWrite {
writable_roots: Vec::new(),
network_access: true,
exclude_tmpdir_env_var: false,
exclude_slash_tmp: false,
})
.expect("set sandbox policy");
let account_display = test_status_account_display();