permissions: migrate approval and sandbox consumers to profiles (#19393)

## Why

Runtime decisions should not infer permissions from the lossy legacy
sandbox projection once `PermissionProfile` is available. In particular,
`Disabled` and `External` need to remain distinct, and managed profiles
with split filesystem or deny-read rules should not be collapsed before
approval, network, safety, or analytics code makes decisions.

## What Changed

- Changes managed network proxy setup and network approval logic to use
`PermissionProfile` when deciding whether a managed sandbox is active.
- Migrates patch safety, Guardian/user-shell approval paths, Landlock
helper setup, analytics sandbox classification, and selected
turn/session code to profile-backed permissions.
- Validates command-level profile overrides against the constrained
`PermissionProfile` rather than a strict `SandboxPolicy` round trip.
- Preserves configured deny-read restrictions when command profiles are
narrowed.
- Adds coverage for profile-backed trust, network proxy/approval
behavior, patch safety, analytics classification, and command-profile
narrowing.

## Verification

- `cargo test -p codex-core direct_write_roots`
- `cargo test -p codex-core runtime_roots_to_legacy_projection`
- `cargo test -p codex-app-server
requested_permissions_trust_project_uses_permission_profile_intent`




































































---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/openai/codex/pull/19393).
* #19395
* #19394
* __->__ #19393
This commit is contained in:
Michael Bolin
2026-04-26 15:30:40 -07:00
committed by GitHub
parent 9c3abcd46c
commit dda8199b73
24 changed files with 367 additions and 164 deletions
+4 -8
View File
@@ -21,11 +21,11 @@ use codex_network_proxy::NetworkProxy;
use codex_protocol::approvals::NetworkApprovalContext;
use codex_protocol::approvals::NetworkApprovalProtocol;
use codex_protocol::approvals::NetworkPolicyRuleAction;
use codex_protocol::models::PermissionProfile;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::Event;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::ReviewDecision;
use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::WarningEvent;
use indexmap::IndexMap;
use std::collections::HashMap;
@@ -127,11 +127,8 @@ fn allows_network_approval_flow(policy: AskForApproval) -> bool {
!matches!(policy, AskForApproval::Never)
}
fn sandbox_policy_allows_network_approval_flow(policy: &SandboxPolicy) -> bool {
matches!(
policy,
SandboxPolicy::ReadOnly { .. } | SandboxPolicy::WorkspaceWrite { .. }
)
fn permission_profile_allows_network_approval_flow(permission_profile: &PermissionProfile) -> bool {
matches!(permission_profile, PermissionProfile::Managed { .. })
}
impl PendingApprovalDecision {
@@ -359,8 +356,7 @@ impl NetworkApprovalService {
.await;
return NetworkDecision::deny(REASON_NOT_ALLOWED);
};
let sandbox_policy = turn_context.sandbox_policy();
if !sandbox_policy_allows_network_approval_flow(&sandbox_policy) {
if !permission_profile_allows_network_approval_flow(&turn_context.permission_profile()) {
pending.set_decision(PendingApprovalDecision::Deny).await;
self.pending_host_approvals.lock().await.remove(&key);
self.record_outcome_for_single_active_call(NetworkApprovalOutcome::DeniedByPolicy(
@@ -1,6 +1,8 @@
use super::*;
use crate::sandboxing::SandboxPermissions;
use codex_network_proxy::BlockedRequestArgs;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::SandboxPolicy;
use core_test_support::PathBufExt;
@@ -185,14 +187,19 @@ fn only_never_policy_disables_network_approval_flow() {
#[test]
fn network_approval_flow_is_limited_to_restricted_sandbox_modes() {
assert!(sandbox_policy_allows_network_approval_flow(
&SandboxPolicy::new_read_only_policy()
assert!(permission_profile_allows_network_approval_flow(
&PermissionProfile::from_legacy_sandbox_policy(&SandboxPolicy::new_read_only_policy())
));
assert!(sandbox_policy_allows_network_approval_flow(
&SandboxPolicy::new_workspace_write_policy()
assert!(permission_profile_allows_network_approval_flow(
&PermissionProfile::from_legacy_sandbox_policy(&SandboxPolicy::new_workspace_write_policy())
));
assert!(!sandbox_policy_allows_network_approval_flow(
&SandboxPolicy::DangerFullAccess
assert!(!permission_profile_allows_network_approval_flow(
&PermissionProfile::Disabled
));
assert!(!permission_profile_allows_network_approval_flow(
&PermissionProfile::External {
network: NetworkSandboxPolicy::Restricted,
}
));
}