Enforce a Command Allowlist

Claude Code's permissions.allow rules are fundamentally broken in many configurations. Wildcard matching fails, compound commands bypass rules, and bypassPermissions ignores allowlists entirely.

A PreToolUse hook enforces allowlists at the process level. The model cannot bypass hooks.

The Hook

allowlist.sh — only approved commands can execute:

#!/bin/bash
# TRIGGER: PreToolUse | MATCHER: "Bash"
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
[[ -z "$COMMAND" ]] && exit 0

ALLOWED=(
    "^\\s*git (add|commit|diff|log|status|show|stash|branch)"
    "^\\s*npm (test|run|install|ci|ls)"
    "^\\s*python3? -m (pytest|py_compile)"
    "^\\s*(cat|head|tail|wc|sort|grep|find|ls|pwd)"
)

for pattern in "${ALLOWED[@]}"; do
    echo "$COMMAND" | grep -qE "$pattern" && exit 0
done

echo "BLOCKED: Not in allowlist" >&2
exit 2

Why Hooks Beat Permissions

Featurepermissions.allowHook allowlist
Compound commandsBrokenWorks
Wildcard matchingInconsistentFull regex
bypassPermissionsIgnoredStill enforced
Background agentsOften brokenAlways works

Related issues: #6850 (settings not respected), #30519 (permissions fundamentally broken), #40343 (bypassPermissions ignores allowlist).

Install the Allowlist Hook

npx cc-safe-setup --install-example allowlist

591 hooks. 8,872 tests.

cc-safe-setup · GitHub