Plugin PreToolUse hooks silently override `bypassPermissions` / `acceptEdits` with no indication to the user

Resolved 💬 4 comments Opened Apr 30, 2026 by DrWaKu Closed May 31, 2026

Summary

A plugin can register a PreToolUse hook that forces a confirmation prompt on every matching tool call, and this overrides the user's permission mode (acceptEdits, bypassPermissions) and the user's allowlist entries. The user has no in-session indication that the prompt is coming from a plugin hook rather than from the normal permission system, and no way to tell from the prompt UI which plugin is responsible.

Concrete case

The official plugin security-guidance@claude-plugins-official (file: ~/.claude/plugins/cache/claude-plugins-official/security-guidance/unknown/hooks/hooks.json) registers:

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          { "type": "command", "command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/security_reminder_hook.py" }
        ],
        "matcher": "Edit|Write|MultiEdit"
      }
    ]
  }
}

With this plugin enabled, every single Edit, Write, and MultiEdit call triggers a confirmation prompt — even when the user has all of:

  • permissions.defaultMode: "acceptEdits"
  • permissions.allow containing Bash(*), Read, Write, Edit, MultiEdit, NotebookEdit
  • Toggled auto-accept edits (shift+tab) on at the session level

The prompt UI shows only the standard "Do you want to make this edit? [Yes / Yes, allow all edits during this session / No]" — there is nothing identifying that a plugin hook is the source, and clicking "Yes, allow all edits during this session" does not stick because the hook re-fires on the next tool call.

User-visible impact

I spent ~6–7 sessions trying to figure out why acceptEdits had stopped working. I rewrote my settings, added wildcard allowlist entries, switched to bypassPermissions, restarted Claude Code repeatedly — none of it helped, because the prompt was coming from the plugin hook layer, not the permission layer. The cause was only found by greping the plugin cache for hooks.json files.

Suggested fixes

Any of these would be enough on their own:

  1. Surface plugin-originated prompts visually, e.g. label them "from security-guidance plugin" so users know which knob to turn.
  2. Make plugin hooks honor bypassPermissions (or document loudly that they don't, and provide a way to disable plugin hooks for a session — a flag, an /unhook command, etc.).
  3. Make plugin enable/disable state more visible — the user usually doesn't remember which plugins are active or what hooks they registered.
  4. At minimum, document this behavior clearly in the plugin docs and the permissions docs.

Environment

  • Claude Code 2.1.123
  • macOS Darwin 25.5.0
  • Plugin: security-guidance@claude-plugins-official, version unknown per the cache directory name

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗