【BUG】Sandbox same-mode escalation dead-ends with invalid justification / not strictly wider when approval is disabled #5847
Replies: 2 comments
|
Targeted note for the maintainers who own this path. The fix is a straight short-circuit, so the core hunk is inlined below for direct lifting (CONTRIBUTING says external PRs are not accepted, so I am not opening one). Why this matters: the failing configuration (standing Root cause: Core fix — full change: diff --git a/packages/sandbox/sandbox/src/escalation.ts b/packages/sandbox/sandbox/src/escalation.ts
index 5cc180fe2b..2bde6c9087 100644
--- a/packages/sandbox/sandbox/src/escalation.ts
+++ b/packages/sandbox/sandbox/src/escalation.ts
@@ -44,11 +44,21 @@ export const ESCALATION_TARGETS: readonly SandboxMode[] = ['workspace-write', 'd
* Validate the escalation argument pairing a tool schema cannot express:
* `sandbox_permissions` and `justification` travel together — an approval
* prompt without a reason, or a reason driving nothing, is a malformed ask —
- * and the justification must be a non-empty sentence.
+ * and the justification must be a non-empty sentence. A request for the mode
+ * already in force is NOT an escalation: it widens nothing, so it passes
+ * without a justification and never reaches the approval channel.
* @param sandboxPermissions - the raw `sandbox_permissions` argument, if given.
* @param justification - the raw `justification` argument, if given.
+ * @param effectiveMode - the call's standing sandbox mode, when a confining
+ * executor is mounted; lets a same-mode request short-circuit the pairing
+ * checks.
*/
-export function validateEscalationArgs(sandboxPermissions: string | undefined, justification: string | undefined): void {
+export function validateEscalationArgs(
+ sandboxPermissions: string | undefined,
+ justification: string | undefined,
+ effectiveMode?: SandboxMode,
+): void {
+ if (effectiveMode !== undefined && sandboxPermissions === effectiveMode) return
if (sandboxPermissions !== undefined && justification === undefined) {
throw new Error('invalid escalation: sandbox_permissions requires a justification')
}Reference implementation: fork branch (中文摘要: |
|
Verified against master Source confirmation (master):
Your three error paths map exactly: no justification → On the fix shape: resolving the standing policy before validating and short-circuiting Family context: #5611 is the closest sibling — full-access state serialized with escalation fields still attached dies the same double failure (empty justification → validation; non-empty → not-strictly-wider). Earlier #3519/#4359/#4383/#4412/#5570/#5588 (plus #4976/#4990) cover the equivalent-declaration idempotency axis. What your report adds is the tool-layer ordering fact — validation fires before the mode is known — which is precisely why the fix belongs in the tool layers (or in On the PR offer: upstream collaboration here is Discussions + plugins only (Issues/PRs are disabled), so the in-thread diff plus the fork reference ( |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Summary
Tool calls that request the sandbox mode already in force hit a fail-closed dead end instead of running, when the session's standing mode is
danger-full-accessand the approval policy isnever(approval prompts disabled). The model gpt-5.6-sol reproduced this repeatedly on the DeepSeek Harness repo while editing files.Exact errors observed
Error: invalid justification: expected a non-empty sentenceError: sandbox escalation to "danger-full-access" is not strictly wider than this call's current "danger-full-access" modeEnvironment
danger-full-access(the standing mode already in force)never(approval prompts disabled / auto-rejected)bash,pwsh,write,edit(any tool that advertisessandbox_permissions)What happens
When the standing sandbox mode is already
danger-full-access, the model's tool calls sometimes still carrysandbox_permissions: "danger-full-access"(e.g. after an escalation hint or by generation habit). Because the request targets the mode already in force, it widens nothing — but the tool layer could not tell that:justificationthe call is rejected asinvalid escalation: sandbox_permissions requires a justification.justificationthe call is rejected asinvalid justification: expected a non-empty sentence.justificationthe call reaches the approval channel and is rejected byapproveEscalationassandbox escalation to "danger-full-access" is not strictly wider than this call's current "danger-full-access" mode.None of these paths ever executes the command or file operation. In a
never-approval session there is no human to approve anything, and retrying without the field does not reliably help because generation keeps re-supplyingsandbox_permissions. Calls of the same tools that happen to omit the field succeed, so the failure looks like a tool-schema defect when the schema and parameter transmission are actually correct.How to reproduce (complete steps)
Code under test. Any commit that predates the fix — the fix is not merged into the official repo, so upstream
masterreproduces it. Confirmed onmaster@d347e70390(dsh 0.1.3-alpha.1); the pre-fix code is also visible at commitcd5ef81481(the fix branch's base).Prerequisites. A DSH session whose standing sandbox mode is
danger-full-accessand whose approval policy isnever(approval prompts disabled), running model gpt-5.6-sol.Repro 1 — deterministic, no model needed (test harness)
Run the pre-fix regression suites; the old assertions pass precisely because they pin the dead-end texts:
tool-bash/tool-pwsh— "rejects injected escalation … non-widening escalation without prompting": abashcall carryingsandbox_permissions: 'workspace-write'under a standingworkspace-writesession override is asserted to fail withnot strictly widerand never prompt.tool-fs— "rejects the escalation argument pairing (one field without the other)": a same-modewritecarrying onlysandbox_permissionsis asserted to fail withsandbox_permissions requires a justification.escalation.spec/ the bash generic-producer suite — a blankjustification: ' 'is asserted to fail withinvalid justification: expected a non-empty sentence.Repro 2 — live session, direct tool calls (no model needed)
In a session already running under
danger-full-accesswith approvalnever, issue these three calls in sequence (they mirror exactly what the model does when it retries):write(file_path: "a.txt", content: "x", sandbox_permissions: "danger-full-access")— nojustificationError: invalid escalation: sandbox_permissions requires a justificationjustification: ""(empty string, as generated when there is nothing to justify)Error: invalid justification: expected a non-empty sentencejustification: "the file is already inside the permitted project"Error: sandbox escalation to "danger-full-access" is not strictly wider than this call's current "danger-full-access" modebash/pwshbehave identically (they fail atvalidateBashArgs/validatePwshArgsandapproveEscalation);write/editfail atFsSandboxController.resolvePolicy. Every attempt returnsisErrorand no command or file mutation ever executes.Repro 3 — model-driven (the original report, gpt-5.6-sol)
danger-full-access(session overridesandbox/mode) and approval policy tonever.write/editcall carriessandbox_permissions: "danger-full-access"(the model re-supplies the field even though the session already runs in that mode — e.g. after seeing the escalation hint). It is rejected per Repro 2.sandbox_permissionsand is rejected again; empty-justification retries produceinvalid justification: expected a non-empty sentence, justified retries producenot strictly wider…, and with approvalneverthere is no approval prompt to rescue the non-widening path.Root cause
The escalation validator had no concept of the call's standing (already-effective) sandbox mode.
validateEscalationArgsinpackages/sandbox/sandbox/src/escalation.tsvalidated thesandbox_permissions⇔justificationpairing unconditionally, andapproveEscalationrejected any target that was not strictly wider than the effective mode. A same-mode request is not an escalation at all, but it was validated and approved as one.Fix we applied locally
fix/sandbox-escalation-same-mode(commitadd0182fc5, plus test follow-up9a564d48f9) resolves this: a request for the mode already in force is treated as a no-op escalation that runs directly under the standing policy — no justification required, no approval asked, nothing widened.validateEscalationArgsgains an optionaleffectiveModeargument and returns immediately whensandboxPermissions === effectiveMode.tool-bash,tool-pwsh, andtool-fsnow resolve the standing policy before validating and short-circuit same-mode requests past the approval channel.sandbox_permissionsis still rejected by the JSON-Schema enum layer.Suggested upstream fix mirrors that branch. Happy to open a PR against the official repo if maintainers prefer (the fork branch is
fix/sandbox-escalation-same-mode).All reactions