Skip to content
Security
Hook

Hooks

What rugproof runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.

From plugin
rugproof
952 skills23 agents45 commands4 hooks
Install
> /plugin marketplace add omermaksutii/RugProof
> /plugin install rugproof@rugproof

Ships with rugproof. Installing the plugin gets these hooks.

Where it lives

  • hooks/post-test-coverage.shGitHub
    Read the script
    #!/usr/bin/env bash
    # Rugproof post-test hook — analyzes coverage after `forge test` and reports gaps.
    # Hooks into Bash PostToolUse via plugin.json, fires when forge/hardhat test commands finish.
    
    set -euo pipefail
    
    PAYLOAD="$(cat)"
    COMMAND="$(echo "${PAYLOAD}" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("tool_input",{}).get("command",""))' 2>/dev/null || echo "")"
    
    # Only act on test commands
    if ! echo "${COMMAND}" | grep -qE '(forge test|hardhat test|npm test|yarn test)'; then
      exit 0
    fi
    
    REPO_ROOT="$(pwd)"
    CONFIG="${REPO_ROOT}/.rugproof.yml"
    
    if [[ ! -f "${CONFIG}" ]]; then
      exit 0
    fi
    
    ENABLED="$(grep -A2 '^hooks:' "${CONFIG}" 2>/dev/null | grep -A1 'post_test:' | grep 'enabled:' | awk '{print $2}' || echo 'true')"
    if [[ "${ENABLED}" != "true" ]]; then
      exit 0
    fi
    
    # Run coverage analysis (best-effort; non-blocking)
    if command -v forge >/dev/null 2>&1; then
      COV_OUT="$(forge coverage --report summary 2>/dev/null | tail -n 30 || echo "")"
      if [[ -n "${COV_OUT}" ]]; then
        UNDER_80="$(echo "${COV_OUT}" | awk -F'|' '$3 ~ /[0-9]/ {gsub(/%/, "", $3); if ($3+0 < 80) print "  ⚠ " $2 " — " $3 "% line coverage"}' || true)"
        if [[ -n "${UNDER_80}" ]]; then
          cat >&2 <<EOF
    
    rugproof: coverage gaps detected (line < 80%):
    ${UNDER_80}
    
       tip: /coverage     (in Claude Code) auto-generates tests for the gaps
    EOF
        fi
      fi
    fi
    
    exit 0
    
  • hooks/pre-commit-quickscan.shGitHub
    Read the script
    #!/usr/bin/env bash
    # Rugproof pre-commit hook — runs /quick-scan on staged Solidity files.
    # Blocks the commit if findings at or above the configured threshold are found.
    #
    # Install: copy to .git/hooks/pre-commit, or symlink:
    #   ln -sf "$(pwd)/hooks/pre-commit-quickscan.sh" .git/hooks/pre-commit
    #
    # Disable per-commit with: git commit --no-verify  (not recommended)
    
    set -euo pipefail
    
    REPO_ROOT="$(git rev-parse --show-toplevel)"
    CONFIG="${REPO_ROOT}/.rugproof.yml"
    
    if [[ ! -f "${CONFIG}" ]]; then
      echo "rugproof: no .rugproof.yml found, skipping pre-commit scan."
      echo "         (run /rugproof-init in Claude Code to set up.)"
      exit 0
    fi
    
    # Read enabled flag from config (defaults to true)
    ENABLED="$(grep -A2 '^hooks:' "${CONFIG}" 2>/dev/null | grep -A1 'pre_commit:' | grep 'enabled:' | awk '{print $2}' || echo 'true')"
    if [[ "${ENABLED}" != "true" ]]; then
      exit 0
    fi
    
    # Get staged Solidity files
    STAGED="$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(sol|vy)$' || true)"
    if [[ -z "${STAGED}" ]]; then
      exit 0
    fi
    
    echo "rugproof: scanning $(echo "${STAGED}" | wc -l | tr -d ' ') staged file(s)…"
    
    # Invoke Claude Code with /quick-scan on the staged files.
    # This requires the `claude` CLI (Claude Code) to be on PATH.
    if ! command -v claude >/dev/null 2>&1; then
      echo "rugproof: 'claude' CLI not found on PATH; skipping scan."
      echo "         install: https://claude.ai/code"
      exit 0
    fi
    
    OUTPUT="$(echo "${STAGED}" | xargs -I {} claude code -p "/quick-scan {}" --output-format=json 2>/dev/null || echo '{"findings":[]}')"
    
    # Count findings at/above threshold
    THRESHOLD="$(grep '^severity_threshold:' "${CONFIG}" 2>/dev/null | awk '{print $2}' || echo 'high')"
    declare -A LEVEL=( [info]=0 [low]=1 [medium]=2 [high]=3 [critical]=4 )
    THRESHOLD_LVL="${LEVEL[${THRESHOLD}]:-3}"
    
    # Parse JSON output for severity counts (best-effort; falls back gracefully)
    N_BLOCKING="$(echo "${OUTPUT}" | python3 -c '
    import json, sys
    try:
        d = json.load(sys.stdin)
        sev_lvl = {"info":0,"low":1,"medium":2,"high":3,"critical":4}
        th = '"${THRESHOLD_LVL}"'
        print(sum(1 for f in d.get("findings",[]) if sev_lvl.get(f.get("severity","").lower(),0) >= th))
    except Exception:
        print(0)
    ' 2>/dev/null || echo 0)"
    
    if [[ "${N_BLOCKING}" -gt 0 ]]; then
      echo
      echo "❌ rugproof: ${N_BLOCKING} finding(s) at or above '${THRESHOLD}' severity"
      echo "   review:  /audit (in Claude Code) for details"
      echo "   bypass:  git commit --no-verify   (use only with reason)"
      exit 1
    fi
    
    echo "✓ rugproof: no blocking findings"
    exit 0
    
  • hooks/pre-deploy-check.shGitHub
    Read the script
    #!/usr/bin/env bash
    # Rugproof pre-deploy hook — runs /audit on build artifacts before deployment.
    # Blocks `forge create`, `hardhat run scripts/deploy.*`, etc. on Critical / High findings.
    #
    # Wired via plugin.json's PreToolUse hook on Bash. Receives JSON on stdin from Claude Code:
    #   { "tool_name": "Bash", "tool_input": { "command": "..." } }
    # Should exit 0 to allow, exit 2 with stderr message to deny (Claude Code convention).
    
    set -euo pipefail
    
    # Read the hook payload
    PAYLOAD="$(cat)"
    COMMAND="$(echo "${PAYLOAD}" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("tool_input",{}).get("command",""))' 2>/dev/null || echo "")"
    
    # Fast-path: only intercept commands that look like deploys
    if ! echo "${COMMAND}" | grep -qE '(forge create|forge script.*--broadcast|hardhat .*deploy|hardhat run.*deploy)'; then
      exit 0
    fi
    
    REPO_ROOT="$(pwd)"
    CONFIG="${REPO_ROOT}/.rugproof.yml"
    
    if [[ ! -f "${CONFIG}" ]]; then
      exit 0
    fi
    
    ENABLED="$(grep -A2 '^hooks:' "${CONFIG}" 2>/dev/null | grep -A1 'pre_deploy:' | grep 'enabled:' | awk '{print $2}' || echo 'true')"
    if [[ "${ENABLED}" != "true" ]]; then
      exit 0
    fi
    
    echo "rugproof: pre-deploy check — auditing build artifacts before broadcasting…" >&2
    
    if ! command -v claude >/dev/null 2>&1; then
      echo "rugproof: 'claude' CLI not found, skipping pre-deploy audit." >&2
      exit 0
    fi
    
    OUTPUT="$(claude code -p "/audit" --output-format=json 2>/dev/null || echo '{"findings":[]}')"
    
    THRESHOLD="$(grep -A2 '^hooks:' "${CONFIG}" 2>/dev/null | grep -A1 'pre_deploy:' | grep 'fail_on:' | awk '{print $2}' || echo 'medium')"
    declare -A LEVEL=( [info]=0 [low]=1 [medium]=2 [high]=3 [critical]=4 )
    TH="${LEVEL[${THRESHOLD}]:-2}"
    
    N="$(echo "${OUTPUT}" | python3 -c '
    import json, sys
    try:
        d = json.load(sys.stdin)
        sev_lvl = {"info":0,"low":1,"medium":2,"high":3,"critical":4}
        th = '"${TH}"'
        print(sum(1 for f in d.get("findings",[]) if sev_lvl.get(f.get("severity","").lower(),0) >= th))
    except Exception:
        print(0)
    ' 2>/dev/null || echo 0)"
    
    if [[ "${N}" -gt 0 ]]; then
      cat >&2 <<EOF
    ❌ rugproof: pre-deploy check failed — ${N} finding(s) at/above '${THRESHOLD}' severity
       review: /audit (full report)
       override: set RUGPROOF_BYPASS_DEPLOY=1 (logged to .rugproof-bypass-log)
    EOF
      if [[ "${RUGPROOF_BYPASS_DEPLOY:-0}" == "1" ]]; then
        echo "$(date -u +%FT%TZ) BYPASS user=$(whoami) cmd=${COMMAND}" >> "${REPO_ROOT}/.rugproof-bypass-log"
        echo "rugproof: BYPASS active — proceeding with deploy" >&2
        exit 0
      fi
      exit 2
    fi
    
    echo "✓ rugproof: pre-deploy audit clean" >&2
    exit 0
    
  • hooks/pre-push-audit.shGitHub
    Read the script
    #!/usr/bin/env bash
    # Rugproof pre-push hook — runs full /audit on changed files vs the upstream branch.
    # Blocks the push if Critical or High findings exist.
    #
    # Install: ln -sf "$(pwd)/hooks/pre-push-audit.sh" .git/hooks/pre-push
    
    set -euo pipefail
    
    REPO_ROOT="$(git rev-parse --show-toplevel)"
    CONFIG="${REPO_ROOT}/.rugproof.yml"
    
    if [[ ! -f "${CONFIG}" ]]; then
      exit 0
    fi
    
    ENABLED="$(grep -A2 '^hooks:' "${CONFIG}" 2>/dev/null | grep -A1 'pre_push:' | grep 'enabled:' | awk '{print $2}' || echo 'true')"
    if [[ "${ENABLED}" != "true" ]]; then
      exit 0
    fi
    
    # Determine base branch (default: main)
    BASE="${RUGPROOF_BASE:-main}"
    if ! git rev-parse --verify "${BASE}" >/dev/null 2>&1; then
      BASE="origin/main"
    fi
    
    CHANGED="$(git diff --name-only "${BASE}...HEAD" -- '*.sol' '*.vy' 2>/dev/null || true)"
    if [[ -z "${CHANGED}" ]]; then
      echo "rugproof: no Solidity/Vyper changes vs ${BASE}, skipping pre-push audit."
      exit 0
    fi
    
    echo "rugproof: auditing $(echo "${CHANGED}" | wc -l | tr -d ' ') changed file(s) vs ${BASE}…"
    
    if ! command -v claude >/dev/null 2>&1; then
      echo "rugproof: 'claude' CLI not found, skipping pre-push audit."
      exit 0
    fi
    
    OUTPUT="$(claude code -p "/audit-changes ${BASE}" --output-format=json 2>/dev/null || echo '{"findings":[]}')"
    
    THRESHOLD="$(grep -A2 '^hooks:' "${CONFIG}" 2>/dev/null | grep -A1 'pre_push:' | grep 'fail_on:' | awk '{print $2}' || echo 'high')"
    declare -A LEVEL=( [info]=0 [low]=1 [medium]=2 [high]=3 [critical]=4 )
    TH="${LEVEL[${THRESHOLD}]:-3}"
    
    N="$(echo "${OUTPUT}" | python3 -c '
    import json, sys
    try:
        d = json.load(sys.stdin)
        sev_lvl = {"info":0,"low":1,"medium":2,"high":3,"critical":4}
        th = '"${TH}"'
        findings = [f for f in d.get("findings",[]) if sev_lvl.get(f.get("severity","").lower(),0) >= th]
        print(len(findings))
        for f in findings[:5]:
            print(f"  ❌ [{f.get(\"severity\",\"?\").upper()}] {f.get(\"id\",\"?\")} {f.get(\"title\",\"\")} ({f.get(\"path\",\"?\")}:{f.get(\"line\",\"?\")})", file=sys.stderr)
    except Exception:
        print(0)
    ' 2>/dev/null || echo 0)"
    
    # Read first line as count, rest is already on stderr
    COUNT="$(echo "${N}" | head -n1)"
    
    if [[ "${COUNT}" -gt 0 ]]; then
      echo
      echo "❌ rugproof: ${COUNT} finding(s) at/above '${THRESHOLD}' severity in changed files"
      echo "   bypass: git push --no-verify   (use sparingly, document why)"
      exit 1
    fi
    
    echo "✓ rugproof: pre-push audit clean"
    exit 0
    

Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.

Ships withrugproof

Rugproof your code before someone else does. 🌐 Live site: omermaksutii.github.io/RugProof 📦 Latest: v1.0.0 — 45 commands · 23 agents · 45 skills · 13 MCP servers · tested, offline-first, with rule packs, a benchmark, non-EVM coverage, and post-deploy

Get the whole plugin