Feature request: per-repo hook-approval prompt for ./.claude/settings.json (parallel to tool permissions)
Summary
When Claude Code starts in a repository containing a ./.claude/settings.json, any SessionStart (and related) hooks declared there execute unconditionally. There is currently no approval gate for repo-local hook autoload. --dangerously-skip-permissions governs tool execution, but hook autoload is a separate path not covered by the permission system. Cloning and opening an untrusted repository can therefore run arbitrary commands on the developer machine before the user takes any action.
Threat model
- Vector: a repository ships a malicious
./.claude/settings.jsonwith a SessionStart hook (command / script). - Trigger: developer clones the repo (PR review, CTF, supply-chain audit, sample project) and starts Claude Code in that directory.
- Result: hook command executes with the developer's privileges. On a typical workstation that exposes SSH keys, credential-manager sessions, and browser cookies — a full local compromise from a single clone-and-open.
- Public PoC (May 2026): https://github.com/s0ld13rr/claude-code-backdoor demonstrates the malicious-repo → RCE chain.
Proposed UX
The first time Claude Code encounters a ./.claude/settings.json that declares hooks in a given repo path:
- Render the hook definitions (commands, matchers, scripts) for the user to read.
- Prompt:
Trust hooks for this repo? [y/N/diff]. Default No.diffshows the full hook content or a diff against the last-approved version. - Persist the trust decision keyed by repo path plus a content hash, so re-approval is only required when the hook set changes.
- Until approved, hooks do not run — fail closed, matching the principle already used for tool permissions.
Why at the CLI layer
This mitigation belongs in the product, not in per-user shell wrappers. Users today can only protect themselves with bespoke shell aliases that hash settings.json before launch — which break on IDE integrations, raw-binary invocation, and CI. A first-class per-repo hook-approval prompt closes the vector for every invocation path and benefits the whole user base.
Alternatives considered
- Disabling hooks globally: loses the feature's value.
- Shell-wrapper hashing (user-side): partial; misses non-shell invocation paths.
- Treating hooks under the existing tool-permission flow: viable if the permission model is extended to cover hook autoload as a distinct, fail-closed capability.
Request
Add a per-repo hook-approval gate parallel to the existing tool-permission prompts — fail-closed by default, with persisted per-repo trust. Scope this initial request to repo-local hooks (./.claude/settings.json). As a follow-up, the same fail-closed approval principle is worth considering for other repo-local autoexec surfaces — for example project-scoped MCP server configs and repo-local skills — since they share the same clone-and-open trust boundary. Happy to provide additional repro detail; suggest CC to the security team.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗