Clarify sandbox permission override helper semantics (#13703)

## Summary
Today `SandboxPermissions::requires_additional_permissions()` does not
actually mean "is `WithAdditionalPermissions`". It returns `true` for
any non-default sandbox override, including `RequireEscalated`. That
broad behavior is relied on in multiple `main` callsites.

The naming is security-sensitive because `SandboxPermissions` is used on
shell-like tool calls to tell the executor how a single command should
relate to the turn sandbox:
- `UseDefault`: run with the turn sandbox unchanged
- `RequireEscalated`: request execution outside the sandbox
- `WithAdditionalPermissions`: stay sandboxed but widen permissions for
that command only

## Problem
The old helper name reads as if it only applies to the
`WithAdditionalPermissions` variant. In practice it means "this command
requested any explicit sandbox override."

That ambiguity made it easy to read production checks incorrectly and
made the guardian change look like a standalone `main` fix when it is
not.

On `main` today:
- `shell` and `unified_exec` intentionally reject any explicit
`sandbox_permissions` request unless approval policy is `OnRequest`
- `exec_policy` intentionally treats any explicit sandbox override as
prompt-worthy in restricted sandboxes
- tests intentionally serialize both `RequireEscalated` and
`WithAdditionalPermissions` as explicit sandbox override requests

So changing those callsites from the broad helper to a narrow
`WithAdditionalPermissions` check would be a behavior change, not a pure
cleanup.

## What This PR Does
- documents `SandboxPermissions` as a per-command sandbox override, not
a generic permissions bag
- adds `requests_sandbox_override()` for the broad meaning: anything
except `UseDefault`
- adds `uses_additional_permissions()` for the narrow meaning: only
`WithAdditionalPermissions`
- keeps `requires_additional_permissions()` as a compatibility alias to
the broad meaning for now
- updates the current broad callsites to use the accurately named broad
helper
- adds unit coverage that locks in the semantics of all three helpers

## What This PR Does Not Do
This PR does not change runtime behavior. That is intentional.

---------

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Charley Cunningham
2026-03-06 09:57:48 -08:00
committed by GitHub
Unverified
parent c8f4b5bc1e
commit cb1a182bbe
5 changed files with 55 additions and 14 deletions
+2 -2
View File
@@ -538,7 +538,7 @@ pub fn render_decision_for_unmatched_command(
// In restricted sandboxes (ReadOnly/WorkspaceWrite), do not prompt for
// nonescalated, nondangerous commands — let the sandbox enforce
// restrictions (e.g., block network/write) without a user prompt.
if sandbox_permissions.requires_additional_permissions() {
if sandbox_permissions.requests_sandbox_override() {
Decision::Prompt
} else {
Decision::Allow
@@ -553,7 +553,7 @@ pub fn render_decision_for_unmatched_command(
Decision::Allow
}
SandboxPolicy::ReadOnly { .. } | SandboxPolicy::WorkspaceWrite { .. } => {
if sandbox_permissions.requires_additional_permissions() {
if sandbox_permissions.requests_sandbox_override() {
Decision::Prompt
} else {
Decision::Allow
+1 -3
View File
@@ -342,9 +342,7 @@ impl ShellHandler {
.map_err(FunctionCallError::RespondToModel)?;
// Approval policy guard for explicit escalation in non-OnRequest modes.
if exec_params
.sandbox_permissions
.requires_additional_permissions()
if exec_params.sandbox_permissions.requests_sandbox_override()
&& !matches!(
turn.approval_policy.value(),
codex_protocol::protocol::AskForApproval::OnRequest
@@ -171,7 +171,7 @@ impl ToolHandler for UnifiedExecHandler {
let request_permission_enabled =
session.features().enabled(Feature::RequestPermissions);
if sandbox_permissions.requires_additional_permissions()
if sandbox_permissions.requests_sandbox_override()
&& !matches!(
context.turn.approval_policy.value(),
codex_protocol::protocol::AskForApproval::OnRequest