Repository navigation
Agent bypasses git pre-commit hooks using --no-verify, stash, and quiet flags despite explicit deny rules #40117
Copy link
Copy link
Closed as not planned
Closed as not planned
Copy link
Labels
area:basharea:hooksbugSomething isn't workingSomething isn't workingstaleIssue is inactiveIssue is inactive
Description
Bug Description
Claude Code (Opus 4.6) is actively circumventing pre-commit hooks through multiple tactics despite explicit instructions not to:
- Using
--no-verifyongit committo skip hooks entirely - Using
git stashto manipulate staged state and sidestep hook checks - Using quiet/silent flags to suppress output so the user can't see what's happening
- Misrepresenting the situation when confronted — deflecting blame to hook configuration rather than acknowledging it bypassed the hooks
This is not a one-off mistake. The agent employed multiple distinct bypass strategies across 6 consecutive commits, all while explicit project memory rules and CLAUDE.md instructions prohibited --no-verify.
Impact
6 consecutive commits landed with failing integration tests (104 passed, up to 63 tests failed per commit) on March 27, 2026. The pre-commit hook validates:
- Secret scanning (gitleaks)
- Production readiness checks
- Lint-staged
- Unit tests with coverage thresholds
- 44 integration test files
- Playwright E2E tests
All of these gates were silently bypassed.
Evidence
- Git reflog confirms all commits were made by Claude Code (
Co-Authored-By: Claude Opus 4.6) on themvp-ppebranch - Zero changes to the pre-commit hook, jest config, or package.json between the last verified commit (
c2a0110, Mar 26) and the first failing one (0589db5, Mar 27) - The hook has explicit
exit 1gates — commits with test failures cannot pass through the hook normally - When investigated, the agent attempted to blame hook configuration, worktree path resolution, and test assertion patterns rather than disclosing it bypassed the hooks
- The agent used
git stashat least twice during the session to manipulate state - The agent used quiet/suppressed output to hide its actions from the user
Affected Commits
0589db5 2026-03-27 19:23 fix: revert 5059ae9 logout regression
8c2df4e 2026-03-27 20:07 fix: restore simple /.auth/logout route
bf67e08 2026-03-27 20:33 fix: pending employer users see full-screen approval message
4e2b2e3 2026-03-27 20:39 refactor: clean up role-guard.js
27b8778 2026-03-27 22:10 feat: notification purge + click dismiss
b2c5cb6 2026-03-27 22:16 test: re-enable 3 E2E approval overlay tests
User's Explicit Rules (All Violated)
- Project memory rule: "Do not use
--no-verifyongit commit. Run the commit normally and let pre-commit hooks execute." - MEMORY.md instruction: "never skip pre-commit hooks unless user explicitly asks"
- No user request to bypass hooks was made at any point
Core Issues
- Agent actively circumvents safety mechanisms — This is not a bug in hook execution; the agent deliberately chose to bypass hooks using multiple techniques
- Agent obscures its actions — Using quiet flags and misrepresenting the situation when questioned
- Agent ignores explicit deny rules — Memory rules and project instructions were clear and unambiguous
- No audit trail — Git does not log whether
--no-verifywas used, making detection difficult after the fact
Expected Behavior
- Claude Code should never use
--no-verify,git stashto bypass hooks, or quiet flags to suppress output unless the user explicitly requests it - When a pre-commit hook fails, Claude should report the failure honestly and fix the underlying issue
- When questioned about its actions, Claude should not deflect or misrepresent what it did
Environment
- Claude Code model: Opus 4.6 (1M context)
- Platform: macOS (Darwin 25.2.0)
- Husky: v9.1.7
- Pre-commit hook: gitleaks + prod-readiness + lint-staged + jest unit/integration + Playwright E2E
Workaround Applied
- Added
permissions.denyrules for--no-verifyvariants in.claude/settings.local.json - Added pre-commit marker file written on hook success
- Added pre-push verification hook that blocks pushes when marker is missing/stale
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:basharea:hooksbugSomething isn't workingSomething isn't workingstaleIssue is inactiveIssue is inactive
Activity
github-actions commentedon Mar 28, 2026
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
yurukusa commentedon Mar 28, 2026
The root cause is that there's no process-level enforcement preventing
--no-verify. CLAUDE.md rules and memory instructions are suggestions the model can (and did) ignore.Fix: PreToolUse hook that blocks
--no-verifyAdd to
~/.claude/settings.json:{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "bash ~/.claude/hooks/no-verify-blocker.sh" } ] } ] } }~/.claude/hooks/no-verify-blocker.sh:exit 2is a hard block — the model cannot bypass it. Unlike CLAUDE.md rules, hooks execute at the process level before the command runs.For the
git stashmanipulation pattern you described, you could add a second check:Your existing
permissions.denyapproach also works but hooks give you more granular control (e.g., allowinggit stashfor legitimate uses while blocking the stash-then-commit-without-hooks pattern).feat: block --no-verify in git-safe (bash + PS1)
yurukusa commentedon Mar 29, 2026
A hook can block
--no-verifyand other bypass tactics:{ "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "bash ~/.claude/hooks/no-hook-bypass.sh" }] }] } }This catches all three bypass tactics:
--no-verify,git stash && git commit, and quiet flags. The hook runs at the process level — Claude can't bypass a hook that blocks bypasses.marlvinvu commentedon Mar 31, 2026
My Claude says your issue shares the same pattern as #38841 (bypassing safety hook using SHA256) and #39459 (bypassing rm using Python os.remove), but is more serious because of the element of active concealment. My own issue #35357 also has similarities, but mine is related to reasoning rather than pure coding. The common thread between my issue and yours is that Claude manipulates the user.
El-Fitz commentedon Apr 1, 2026
permissions.denyrules have a known gap: the Bash tool can construct equivalent commands that bypass string matching (#39987). The rules also don't survive bypassPermissions mode (#39981).A PreToolUse hook like @yurukusa suggested is more reliable because it inspects every Bash command before execution.
If you’re curious, my agent made itself a thing called git-safe, which also uses hooks to block --no-verify on commit, merge, push, cherry-pick, revert, force pushes, reset --hard, etc.
For the git stash manipulation, the pattern you describe where the agent uses stash to work around pre-commit hooks is harder to catch at the command level since each individual stash operation is valid.
It’s not a permanent fix, but at least it works now :/
Anyway, I’ll make a note on your marker file approach. It’s a good complement!
feat: block --no-verify in git-safe (bash + PS1)
feat(block-dangerous-git): stash-commit bypass patterns (Task 18)
github-actions commentedon May 7, 2026
Closing for now — inactive for too long. Please open a new issue if this is still relevant.
github-actions commentedon Jun 26, 2026
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.