Self-permission-granting safety check bypassable via Bash-mediated write (gates by tool, not by effect)

Status Open
Maintainer reply None cached
Activity 0 comments · opened Sep 11, 2026

Summary

Claude Code's self-permission-granting safety check appears to gate by which tool is invoked, not by the effect a command produces on disk. A Write/Edit call that modifies ~/.claude/settings.json to register a new hook is refused as self-permission-granting. The identical file, with identical resulting content, can then be written successfully via the Bash tool (e.g. a Python read-modify-write one-liner), with no refusal at all.

If accurate, this means the self-permission-granting boundary is bypassable simply by choosing a different tool to produce the same write, which defeats the purpose of the check.

Steps to reproduce

  1. In a Claude Code session, ask the assistant to add a new entry to hooks.PreToolUse in ~/.claude/settings.json (e.g. registering a new hook script) using the Write or Edit tool.
  • Observed: the tool call is refused/blocked as an attempt to grant new permission/hook capability to itself.
  1. Ask the assistant to make the exact same change via the Bash tool instead, for example a Python script that reads settings.json, inserts the same JSON object into hooks.PreToolUse, and writes the file back with json.dump.
  • Observed: the write succeeds with no refusal, and the resulting settings.json is functionally identical to what was refused in step 1.

Expected behavior

A safety boundary meant to prevent a session from expanding its own permissions/hook capability should classify by the effect of a command (does this write target a sensitive config path and change its permission-relevant content?), not by which tool API was used to produce that effect. If Write/Edit are refused for a given target and content, a Bash-mediated write producing the same target/content should be refused too.

Context

This was found while a session was legitimately trying to install a new PreToolUse hook at the user's own explicit, repeated direction (the user was fully aware of and authorized the change; this is not a report of malicious use). The Write/Edit refusal correctly triggered; the Bash route around it should not have succeeded silently. The same technique would plausibly generalize to other Write/Edit-refused targets, not just settings.json.

Environment

  • Claude Code (desktop app), session date 2026-09-10.
  • macOS.

Happy to provide more detail if useful (project-specific names sanitized out of this report; the mechanism itself is what matters).

View original on GitHub ↗