Claude Hookbook

Hooks / safety

Block writes outside the repo

Stops Edit/Write calls whose target path resolves outside the project root.

PreToolUseEdit|Writeblocking riskneeds manual check

What it does

Resolves the real path of the write target (following symlinks, handling files that do not exist yet via their parent directory) and compares it with the real project root. Anything outside is denied. Catches ../ escapes and absolute paths to your home directory or /etc.

When it runs

EventPreToolUse
MatcherEdit|Write
Scopeproject
Platformsmacos, linux
Timeout10s
Requiresjq
Failure modeblock
Verified againstClaude Code hooks reference, event/matcher schema checked 2026-09-10
Test statusneeds manual check - run the test steps below before relying on it

Config

Merge this into .claude/settings.json - or use the generator to combine hooks.

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-writes-outside-repo.sh",
            "timeout": 10
          }
        ],
        "matcher": "Edit|Write"
      }
    ]
  }
}

Script

Save as .claude/hooks/block-writes-outside-repo.sh and chmod +x it.

#!/bin/bash
# block-writes-outside-repo: deny writes that land outside the project root.
set -u
file=$(jq -r '.tool_input.file_path // empty')
[ -z "$file" ] && exit 0
root=$(cd "${CLAUDE_PROJECT_DIR:-$PWD}" && pwd -P)
# Resolve the target even if it does not exist yet: resolve the deepest
# existing ancestor, then append the rest.
target="$file"
case "$target" in /*) ;; *) target="$root/$target" ;; esac
dir=$(dirname "$target")
while [ ! -d "$dir" ] && [ "$dir" != "/" ]; do dir=$(dirname "$dir"); done
resolved_dir=$(cd "$dir" 2>/dev/null && pwd -P || echo "$dir")
resolved="$resolved_dir/$(basename "$target")"
case "$resolved" in
  "$root"|"$root"/*) exit 0 ;;
esac
jq -n --arg f "$resolved" --arg r "$root" '{
  hookSpecificOutput: {
    hookEventName: "PreToolUse",
    permissionDecision: "deny",
    permissionDecisionReason: ("Write outside project root blocked (" + $f + " is not under " + $r + ").")
  }
}'
exit 0

Install

  1. Save the script to .claude/hooks/block-writes-outside-repo.sh and chmod +x it.
  2. Add the config fragment to .claude/settings.json.

Test it

If it blocks something you wanted

If Claude legitimately needs a file outside the repo (a shared config, a sibling package), either move the work into the repo or allowlist that path in the script.

Sources

Related hooks

Block destructive shell commands

Blocks rm -rf, disk tools, force pushes, and destructive git commands before they run.

PreToolUseBashblocking riskneeds manual check

Protect .env and secret files

Blocks Claude from writing to .env files, keys, and credential files - unless you say otherwise.

PreToolUseEdit|Writeblocking riskneeds manual check

Pro Pack (later)

Framework bundles, cross-platform scripts, tested blocking policies, and a test harness. Free recipes and the generator stay free.

Waitlist opens soon.