#!/usr/bin/env bash # Hook: block git pushes if tracked files contain secrets from a secrets env file # # SETUP: # 1. Create ~/.config/secrets.env with your secret values (one per line: KEY=value) # 2. Add this hook to Claude Code settings.json: # { # "hooks": { # "PreToolUse": [{ # "matcher": "Bash", # "hooks": [{ # "type": "command", # "command": "/path/to/check-secrets-before-push.sh", # "timeout": 30, # "statusMessage": "Checking for secrets before push..." # }] # }] # } # } # # HOW IT WORKS: # - Reads the Bash tool input from stdin (JSON with tool_input.command) # - Only activates on git push or similar push commands # - Loads secret values from ~/.config/secrets.env # - Scans tracked files for any matching secret values # - Outputs a JSON block decision if secrets are found # # SECRETS FILE FORMAT (~/.config/secrets.env): # API_KEY=sk-abc123... # DB_PASSWORD=hunter2 # # Comments are skipped # SHORT=ab # Values under 6 chars are skipped (too many false positives) INPUT=$(cat) CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""' 2>/dev/null) # Only care about push commands echo "$CMD" | grep -qE '(git\s+push)' || exit 0 # Check if we're in a git repo REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) || exit 0 # Load secrets to check for SECRETS_FILE="$HOME/.config/secrets.env" [[ -f "$SECRETS_FILE" ]] || exit 0 # Extract secret values (skip comments, empty lines, short values) PATTERNS=() while IFS='=' read -r key value; do [[ -z "$key" || "$key" =~ ^# ]] && continue value="${value#\"}" value="${value%\"}" value="${value#\'}" value="${value%\'}" [[ ${#value} -lt 6 ]] && continue PATTERNS+=("$value") done < "$SECRETS_FILE" [[ ${#PATTERNS[@]} -eq 0 ]] && exit 0 # Scan tracked files for secret values FOUND=() for secret in "${PATTERNS[@]}"; do file=$(git -C "$REPO_ROOT" grep -rlF "$secret" -- . 2>/dev/null | head -1 || true) if [[ -n "$file" ]]; then key_name=$(grep -F "$secret" "$SECRETS_FILE" 2>/dev/null | head -1 | cut -d= -f1) FOUND+=("$key_name in $file") fi done if [[ ${#FOUND[@]} -gt 0 ]]; then printf '{"decision":"block","reason":"BLOCKED: Secrets found in tracked files:\\n' for f in "${FOUND[@]}"; do printf ' - %s\\n' "$f" done printf '\\nReplace with env var references before pushing."}\n' fi