Bug: Certain characters in permissions.deny rules silently break all hooks
Bug: Certain characters in permissions.deny rules silently break all hooks
Summary
Specific characters in permissions.deny rules cause Claude Code to silently ignore the entire hooks configuration. The /hooks UI shows "No matchers configured yet" even though hooks are properly defined in settings.json.
Environment
- Claude Code version: v2.0.64
- OS: Ubuntu Linux (also reproduced concept on other systems)
- Shell: bash
Steps to Reproduce
- Create a
~/.claude/settings.jsonwith a problematic deny rule:
{
"permissions": {
"allow": ["Bash"],
"deny": [
"Bash(curl *| bash)"
]
},
"hooks": {
"PreToolUse": [
{
"matcher": "*",
"hooks": [
{"type": "command", "command": "echo 'hook fired'"}
]
}
]
}
}
- Start Claude Code:
claude - Type
/hooksand selectPreToolUse
Expected: Shows [User] * with 1 hook
Actual: Shows No matchers configured yet
Root Cause Analysis
Through extensive binary search testing (30+ isolated tests), we identified that the following patterns in deny rules break hook parsing:
| Pattern | Example | Breaking Character(s) |
|---------|---------|----------------------|
| Pipe to command | Bash(curl *\| bash) | \| |
| Fork bomb | Bash(:(){ :\|:& };:) | { } ; : & \| |
| Trailing wildcard | Bash(shred -vfz *) | * at end (but /* is OK) |
| Embedded quotes | Bash(perl -e 'fork') | '...' |
Working Patterns (for reference)
These patterns work correctly:
Bash(rm -rf /)- simple commandsBash(rm -rf /*)- path wildcards (trailing/*)Bash(git push --force origin main)- flags and argumentsBash(rm -rf $HOME)- environment variables
Impact
- Severity: High - All hooks silently stop working
- No error messages are displayed
/hooksUI misleadingly shows "No matchers configured yet"- Users have no indication their hooks configuration is being ignored
Workaround
Remove deny rules containing problematic characters. We created a validation script to detect these patterns:
const BREAKING_PATTERNS = [
{ pattern: /\|\s*\w+\)$/, desc: "pipe to command" },
{ pattern: /[{}]/, desc: "curly braces" },
{ pattern: /;\s*:/, desc: "semicolon-colon combo" },
{ pattern: /[^/]\*\)$/, desc: "trailing wildcard (not /*)" },
{ pattern: /'\s*[^']+\s*'\)$/, desc: "embedded quotes" },
];
Suggested Fix
The deny rule parser should properly escape special regex/shell characters, or provide clear error messages when it encounters patterns it cannot parse.
Additional Context
- The hooks themselves work correctly when tested manually via stdin
- The JSON is valid (verified with multiple JSON validators)
- No BOM or encoding issues (verified with
xxd) - The failure is completely silent - no logs, no errors
Test Methodology
We performed systematic binary search testing:
- Started with minimal working config (hooks only) ✓
- Added top-level keys one by one ✓
- Added full permissions with simple deny rules ✓
- Added full deny rules → BROKEN
- Binary searched deny rules to isolate culprits
- Confirmed each culprit with single-rule tests
Total tests performed: 30+
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗