Skip to content

Fix #274: ToolContext no longer defaults to bypassPermissions#305

Open
ericleepi314 wants to merge 2 commits into
fix/issue-289-test-failuresfrom
fix/issue-274-permission-context-default
Open

Fix #274: ToolContext no longer defaults to bypassPermissions#305
ericleepi314 wants to merge 2 commits into
fix/issue-289-test-failuresfrom
fix/issue-274-permission-context-default

Conversation

@ericleepi314

Copy link
Copy Markdown
Collaborator

Closes #274
Closes #168 (duplicate)

Stacked on #304 (base: fix/issue-289-test-failures) so the suite is fully green at the tip. Merge #304 first; GitHub will retarget this PR to main.

Summary

ToolContext.permission_context defaulted to bypassPermissions, silently disabling the workspace-root allowlist for every caller that didn't pass an explicit context (SDK embedders, tests, the TUI fallback context). The two parity tests guarding the allowlist were red on main because of it.

Core changes

  • Default flipped to mode="default" (src/tool_system/context.py)
  • TS-parity read-only auto-allow in has_permissions_to_use_tool_inner: the port was missing the checkReadPermissionForTool → allow path, so the new default would have ask→denied every Read/Glob/Grep in handler-less contexts. Gated on the tool's own check_permissions returning passthrough — a read-only tool's own ask survives. Deny/ask rules still run first.
  • Workspace-allowlist violations are hard failures: Write/Edit check_permissions no longer swallow ToolPermissionError into passthrough; the permission flow re-raises it so out-of-workspace writes raise instead of soft-denying through the ask path. Production dispatch callers wrap in except Exception → error tool result (audited: query loop, REPL, headless, hooks paths).
  • WebFetch domain gating: preapproved hosts → allow, anything else → real ask. Without this, the auto-allow would have turned WebFetch's old no-op check_permissions into a silent allow for arbitrary URLs (found in critic review).
  • security_review.py shell preprocessing keeps its intentional bypass, now explicit.

Call-site audit (per the issue's fix sketch)

  • All production entrypoints already pass explicit contexts via setup_permissions — unaffected.
  • TUI fallback context deliberately inherits the new strict default (fail-closed).
  • ~15 test files that relied on the implicit bypass now pass explicit bypass contexts (non-permission tests) or real PermissionAskReply handlers; two stale tuple-shape handlers (dead code under the old default) updated to the live protocol.

Behavior matrix (verified)

Case Result
Read/Glob/Grep, default mode allow (read-only), path allowlist enforced in call
Write/Edit in-workspace, default + handler ask → handler decides
Write/Edit out-of-workspace raises ToolPermissionError
WebFetch non-preapproved, default ask (deny under dontAsk)
WebFetch preapproved allow
bypassPermissions allow (unchanged)

Test plan

  • test_write_outside_workspace_blocked / test_read_outside_workspace_blocked now pass
  • Full suite on the stacked branch: 7765 passed, 0 failed, 5 skipped
  • Critic review loop: APPROVE after 1 revision round (passthrough gate + WebFetch gating came out of it)

🤖 Generated with Claude Code

ericleepi314 and others added 2 commits June 11, 2026 16:17
…ns (#274)

The implicit bypassPermissions default silently disabled the
workspace-root allowlist for every caller that didn't pass an explicit
permission_context (SDK embedders, tests, the TUI fallback context).

- ToolContext.permission_context now defaults to mode="default"
- TS parity: read-only tools auto-allow in has_permissions_to_use_tool
  (the port was missing the checkReadPermissionForTool -> allow path,
  so flipping the default would have ask->denied every Read/Glob/Grep)
- Write/Edit check_permissions no longer swallow the allowlist
  ToolPermissionError into passthrough; the inner permission flow
  re-raises it so out-of-workspace writes fail hard instead of
  soft-denying through the ask path
- security_review's shell-preprocessing context passes an explicit
  bypass (it intentionally matched the old implicit default)
- tests that relied on the implicit bypass now pass an explicit
  bypass context or a real PermissionAskReply handler; two stale
  tuple-shape handlers updated to the live protocol

Closes #274, closes #168

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…n gating (#274)

Review follow-ups: the read-only auto-allow only applies when the tool's
own check_permissions expressed no opinion, so a read-only tool's ask
survives (TS shape: the read-only allow lives inside checkPermissions).
WebFetch now returns allow for preapproved hosts and a real ask
otherwise — without this, the auto-allow turned the old no-op
check_permissions into a silent allow for arbitrary URLs.
check_rule_based_permissions mirrors the allowlist re-raise, and the
CheckPermissionsTool protocol declares is_read_only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant