Preserve executor workspace permissions for apply_patch (#36133)

## What changed

Build the `apply_patch` filesystem sandbox context from the executor's canonical permission profile instead of the execution attempt's materialized profile. Continue merging any additional permissions requested by the patch while keeping workspace roots scoped separately to the attempt.

## Testing

- Update the sandbox-context test to verify that executor `workspace-write` permissions are preserved when attempt workspace roots and additional file permissions are present.

GitOrigin-RevId: 2a9a8c2e1b1945e300283c400d0f1e573225a553
This commit is contained in:
jif
2026-07-30 11:12:21 +00:00
committed by copyberry
parent c126f206da
commit bdda5da56c
2 changed files with 11 additions and 10 deletions
@@ -91,8 +91,10 @@ impl ApplyPatchRuntime {
return None;
}
let permissions =
effective_permission_profile(attempt.permissions, req.additional_permissions.as_ref());
let permissions = effective_permission_profile(
attempt.exec_server_permissions,
req.additional_permissions.as_ref(),
);
Some(FileSystemSandboxContext {
permissions: permissions.into(),
cwd: Some(attempt.sandbox_cwd.clone()),
@@ -4,7 +4,6 @@ use codex_protocol::config_types::WindowsSandboxLevel;
use codex_protocol::models::AdditionalPermissionProfile;
use codex_protocol::models::FileSystemPermissions;
use codex_protocol::models::PermissionProfile;
use codex_protocol::permissions::FileSystemSandboxPolicy;
use codex_protocol::permissions::NetworkSandboxPolicy;
use codex_protocol::protocol::GranularApprovalConfig;
use codex_sandboxing::SandboxManager;
@@ -179,7 +178,7 @@ async fn sandbox_cwd_uses_patch_action_cwd() {
}
#[tokio::test]
async fn file_system_sandbox_context_uses_active_attempt() {
async fn file_system_sandbox_context_preserves_executor_workspace_permissions() {
let path = std::env::temp_dir()
.join("apply-patch-runtime-attempt.txt")
.abs();
@@ -205,18 +204,18 @@ async fn file_system_sandbox_context_uses_active_attempt() {
additional_permissions: Some(additional_permissions.clone()),
permissions_preapproved: false,
};
let file_system_policy = FileSystemSandboxPolicy::default();
let permissions = PermissionProfile::from_runtime_permissions(
&file_system_policy,
NetworkSandboxPolicy::Restricted,
);
let exec_server_permissions = PermissionProfile::workspace_write();
let file_system_policy = exec_server_permissions.file_system_sandbox_policy();
let permissions = exec_server_permissions
.clone()
.materialize_project_roots_with_workspace_roots(std::slice::from_ref(&path));
let manager = SandboxManager::new();
let sandbox_policy_cwd = PathUri::from_abs_path(&path);
let attempt = SandboxAttempt {
sandbox: SandboxType::MacosSeatbelt,
sandbox_requested: true,
permissions: &permissions,
exec_server_permissions: &permissions,
exec_server_permissions: &exec_server_permissions,
enforce_managed_network: false,
manager: &manager,
sandbox_cwd: &sandbox_policy_cwd,