PromptBase
Upgrade
Close icon
General
Home
Marketplace
Create
Hire
Login
Chat
Sell
Explore

Prompt Details

Model
(claude-4-6-sonnet)
Token size
540
Example input
[HOOK_TYPE]: PreToolUse [RULE_DESCRIPTION]: Block any Bash tool call whose command string contains "rm -rf" or "rm -r /" targeting paths outside /tmp β€” log the attempt to ~/.claude/blocked_commands.log with timestamp and working directory [PROJECT_CONTEXT]: Node.js monorepo with 3 packages under /packages; CI pipeline has no dry-run mode so accidental deletions in dev have caused incidents twice this quarter
Example output
# Claude Code Hook: Bash `rm -rf` Interceptor --- ## 1. HOOK SCRIPT Save to: `{PROJECT_ROOT}/.claude/hooks/block_dangerous_rm.sh` #!/usr/bin/env bash # Using bash (not sh) for process substitution and robust string ops. # Requires: jq >= 1.6 OR python3 (fallback). Tested on macOS 14, Ubuntu 22. set -euo pipefail # ── Constants ────────────────────────────────────────────────────────────── LOG_FILE="${HOME}/.claude/blocked_commands.log" SAFE_PREFIX="/tmp" # Only path root exempted from blocking # ── Helpers ─────────────────────────────────────────────────────────────── log_blocked() { # $1=command $2=cwd mkdir -p "$(dirname "$LOG_FILE")" # Idempotent: no-op if dir exists printf '[%s] BLOCKED | cwd=%s | cmd=%s\n' \ "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$2" "$1" \ >> "$LOG_FILE" # Append-only; safe for concurrent runs } parse_field() { # $1=json $2=field-path if command -v jq &>/dev/null; then printf '%s' "$1" | jq -r "$2" else # Fallback: python3 always present in Node envs printf '%s' "$1" | python3 -c \ "import sys,json; d=json.load(sys.stdin); \ keys='$2'.lstrip('.').split('.'); \ v=d; [v:=v[k] for k in keys]; print(v if v else '')" fi } # ── Read stdin once (hook contract: single JSON object on stdin) ─────────── INPUT="$(cat -)" # Buffer so we can query multiple fields TOOL_NAME="$(parse_field "$INPUT" '.tool_name')" CMD="$(parse_field "$INPUT" '.tool_input.command')" CWD="$(parse_field "$INPUT" '.tool_input.working_directory // ""')" # Normalise CWD: fall back to $PWD if the field is absent or literal "null" [[ "$CWD" == "null" || -z "$CWD" ]] && CWD="$PWD" # ── Guard: only inspect Bash tool calls ─────────────────────────────────── [[ "$TOOL_NAME" != "Bash" ]] && exit 0 # Allow all non-Bash tools immediately # ── Pattern detection ───────────────────────────────────────────────────── # Match "rm -rf", "rm -fr", "rm -r /" in any position within the command. # We intentionally do NOT try to parse shell AST; see Edge Cases section. DANGEROUS=0 if [[ "$CMD" =~ rm[[:space:]]+-[a-zA-Z]*r[a-zA-Z]*f|rm[[:space:]]+-[a-zA-Z]*f[a-zA-Z]*r ]]; then DANGEROUS=1 # Catches -rf, -fr, -Rf, -Rfv, etc. fi if [[ "$CMD" =~ rm[[:space:]]+-r[[:space:]] ]]; then DANGEROUS=1 # Catches bare "rm -r <path>" fi # ── Path extraction & safe-prefix check ─────────────────────────────────── if [[ "$DANGEROUS" -eq 1 ]]; then # Pull the first non-flag token after rm as a rough target path heuristic. # Example: "rm -rf /packages/core/dist" β†’ TARGET="/packages/core/dist" TARGET="$(printf '%s' "$CMD" | \ grep -oP '(?<=rm\s{0,20})(-\S+\s+)*\K/\S+' | head -1 || true)" # If no absolute path captured, treat as dangerous (deny by default). if [[ -z "$TARGET" ]]; then TARGET="<relative or unknown path>" SAFE=0 elif [[ "$TARGET" == /tmp/* || "$TARGET" == "/tmp" ]]; then SAFE=1 # /tmp targets are explicitly allowed else SAFE=0 fi if [[ "$SAFE" -eq 0 ]]; then log_blocked "$CMD" "$CWD" # Persist evidence before blocking # Human-readable explanation written to stderr (Claude surfaces this). cat >&2 <<EOF ╔══ BLOCKED by Claude Code hook: block_dangerous_rm ══════════════════════╗ β•‘ Command : ${CMD} β•‘ Target : ${TARGET} β•‘ Reason : "rm -rf / rm -r" outside /tmp is prohibited in this repo. β•‘ Two CI incidents this quarter were caused by accidental β•‘ deletions inside /packages. This hook prevents a third. β•‘ Logged to: ${LOG_FILE} β•‘ Safe alt : Use "trash" CLI, move files to /tmp first, or scope to β•‘ a specific sub-path you have confirmed is safe. β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β• EOF exit 1 # Exit 1 = block (Claude Code convention) fi fi exit 0 # Exit 0 = allow --- ## 2. SETTINGS JSON SNIPPET # Integration steps: # 1. Copy block_dangerous_rm.sh to {PROJECT_ROOT}/.claude/hooks/ # 2. chmod +x {PROJECT_ROOT}/.claude/hooks/block_dangerous_rm.sh # 3. Merge the JSON below into ~/.claude/settings.json under "hooks". # If "hooks" does not exist yet, add the entire "hooks" key. # 4. Replace {PROJECT_ROOT} with the absolute path to your monorepo root. # 5. Validate JSON: python3 -m json.tool ~/.claude/settings.json # 6. Open a new Claude Code session; hooks reload on session start. # # Timeout note: 10 s is generous for a pure string-check script (~5 ms # typical). Raise to 30 s only if you later add network policy lookups. { "hooks": { "PreToolUse": [ { "description": "Block dangerous rm -rf calls outside /tmp", "matcher": { "tool_name": "Bash" }, "command": "{PROJECT_ROOT}/.claude/hooks/block_dangerous_rm.sh", "timeout": 10, "enabled": true } ] } } --- ## 3. TEST CASES Input (tool_input.command) | Exit | Reason ───────────────────────────────────────────────────────────────────────────────── rm -rf /packages/core/dist | 1 | BLOCK β€” absolute path outside /tmp; | | matches -rf pattern, SAFE=0 ───────────────────────────────────────────────────────────────────────────────── rm -r / --no-preserve-root | 1 | BLOCK β€” targets filesystem root; | | matches "rm -r /" pattern, SAFE=0 ───────────────────────────────────────────────────────────────────────────────── rm -rf /tmp/build_cache_abc123 | 0 | ALLOW β€” path starts with /tmp, | | SAFE=1, no block triggered ───────────────────────────────────────────────────────────────────────────────── rm -f ./packages/core/dist/index.js | 0 | ALLOW β€” single file, no -r flag; | | DANGEROUS remains 0, exits 0 ───────────────────────────────────────────────────────────────────────────────── git clean -fdx | 0 | ALLOW β€” Bash tool but no rm pattern; | | exits 0 immediately after guard ───────────────────────────────────────────────────────────────────────────────── rm -Rfv /packages/shared/node_modules | 1 | BLOCK β€” -Rfv contains both r and f | | flags; TARGET outside /tmp --- ## 4. EDGE CASES & KNOWN LIMITATIONS **EC-1 β€” Shell variable expansion not evaluated** CMD="TARGET=/packages/core; rm -rf $TARGET" The hook sees the literal string before the shell expands $TARGET. A variable-expanded path could bypass the /tmp safe-prefix check. Mitigation: Pair with a shell AST linter (shellcheck --severity=error) as a PostToolUse hook, or run commands inside a chroot/container. **EC-2 β€” Subshell and pipe chaining** CMD="echo ok && rm -rf /packages/utils/dist || true" The regex matches this correctly only because "rm -rf /packages" is present literally. But a heavily obfuscated chain: CMD="eval 'rm -rf /packages'" would NOT be caught because "rm" does not appear adjacent to "-rf". Mitigation: Block all eval/exec calls containing single-quoted rm via a second hook matcher on the eval pattern. **EC-3 β€” Relative paths are blocked conservatively but imprecisely** CMD="rm -rf dist" (run from /packages/core) No absolute path is extracted; TARGET becomes "<relative or unknown path>" and SAFE=0 β†’ blocked. This is intentionally conservative but will frustrate developers cleaning local build artifacts. Mitigation: Add an allowlist of known-safe relative paths (e.g., "dist", "node_modules", ".cache") and resolve them against CWD before applying the safe-prefix check. **EC-4 β€” Hook bypass via Python/Node sub-process** CMD="node -e \"require('fs').rmSync('/packages', {recursive:true})\"" The hook only inspects Bash tool calls for the rm pattern. Language-level recursive-delete APIs are invisible to this hook. Mitigation: Add a PreToolUse hook for the Bash tool that also scans for fs.rmSync, shutil.rmtree, and similar patterns; or enforce filesystem-level write ACLs via OS-level controls (e.g., Linux immutable flags on /packages during dev sessions). **Suggested complementary safeguards** - chattr +i on /packages in CI pre-step (Linux only); remove after build. - A git pre-commit hook that rejects commits deleting tracked files in bulk. - Docker volume mount /packages as :ro in non-build containers. - PostToolUse hook that diffs the git working tree and alerts on unexpected mass deletions even if the PreToolUse hook was bypassed. --- ## 5. UPGRADE PATH **Evolve to a Policy-as-Code enforcement layer with Open Policy Agent (OPA)** Replace the ad-hoc regex logic with a declarative Rego policy evaluated by a locally running OPA sidecar: # policy/bash_safety.rego package claude.bash default allow = false allow { not dangerous_rm } dangerous_rm { re_match(`rm\s+-[a-zA-Z]*r[a-zA-Z]*f`, input.command) not safe_target } safe_target { startswith(input.target, "/tmp") } The hook script shrinks to a thin shim: #!/usr/bin/env bash set -euo pipefail INPUT="$(cat -)" RESULT="$(printf '%s' "$INPUT" | \ curl -sf http://localhost:8181/v1/data/claude/bash/allow \ -d @- -H 'Content-Type: application/json')" [[ "$(printf '%s' "$RESULT" | jq -r '.result')" == "true" ]] \ && exit 0 || exit 1 Benefits of this upgrade: 1. Policies are version-controlled, peer-reviewed Rego files β€” the same workflow used for infra and k8s policy. 2. A single OPA bundle can enforce rules across Claude Code, pre-commit, and CI (GitHub Actions OIDC β†’ OPA policy check) from one source of truth. 3. New rules (block shutil.rmtree, block aws s3 rm --recursive, etc.) are added as Rego clauses with zero changes to the hook script. 4. OPA's decision log provides tamper-evident audit records superior to the append-only log file used in the current implementation. 5. The sidecar can be started as a launchd/systemd user service so it survives session restarts with no manual intervention.
πŸŒ€ Claude

Code Hook Generator

Add to Cart
Instant accessInstant access
Usage rightsCommercial use
Money-back guaranteeMoney‑back
By purchasing this prompt, you agree to our terms of service
CLAUDE-4-6-SONNET
Tested icon
Guide icon
4 examples icon
Free credits icon
πŸ“ŒGenerates production-ready Claude Code hook scripts that enforce dev rules deterministically β€” no more hoping the model remembers. πŸ›‘οΈ Block dangerous commands automatically πŸ” Inject context on keyword detection βœ… Run linters on every file edit πŸ“ Auto-commit with formatted messages πŸ” Trigger security review on auth keywords
...more
Added 2 days ago
Report
Browse Marketplace