aboutsummaryrefslogtreecommitdiff
path: root/working/hook-fail-open/validate-el.sh.superseded
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-24 12:28:18 -0500
committerCraig Jennings <c@cjennings.net>2026-07-24 12:28:18 -0500
commitf0c1bc40708615d5b423922c08c1f27e6cf96259 (patch)
tree5fc8ebe7fde6381f4cd91b5de15fe4a2521889c8 /working/hook-fail-open/validate-el.sh.superseded
parent781fa0786e2096797e630dcb81ffbc62ed98460b (diff)
downloadrulesets-f0c1bc40708615d5b423922c08c1f27e6cf96259.tar.gz
rulesets-f0c1bc40708615d5b423922c08c1f27e6cf96259.zip
fix(hooks): the secret scan no longer passes when git fails
Every bundle built its scan input as `git diff --cached ... | grep ... || true`. The `|| true` has to stay, since grep exits 1 when it matches nothing and that is the ordinary case. But with no pipefail it also swallowed a failure of git itself, so an empty result made the scan search nothing, find nothing, and report clean with a real credential staged. A gate that passes without having looked. .emacs.d found it in elisp. It was in all five: bash, elisp, go, python and typescript, eleven sites once each bundle's staged-file list is counted. Two of those bundles are ones I wrote yesterday by copying bash, so I propagated it while closing an unrelated gap in the same file. Each site now reads the diff on its own and aborts if git fails, leaving the greps their `|| true`. Verified per bundle on three axes: refuses when the diff cannot be read, still blocks a real staged secret, still passes a clean commit. The cross-bundle test suite had its own version of the same disease. Its VARIANTS list read "elisp bash go" while python and typescript also shipped hooks, so every "in every variant" assertion had quietly skipped two bundles since the day they were added. VARIANTS is now discovered from the tree. Two fail-closed assertions join it, and .emacs.d's elisp suite is adopted here beside the canonical hook, because a test living in the consuming project cannot fail when the canonical regresses. Also removes the validate-el auto-test cap, Craig's call. Above 20 matching test files the runner skipped everything and exited 0 with no output. The premise was speed and it did not hold: a whole family runs in about a second. The cap was also hiding a real cross-test pollution bug that only surfaces when a family runs in one process.
Diffstat (limited to 'working/hook-fail-open/validate-el.sh.superseded')
-rw-r--r--working/hook-fail-open/validate-el.sh.superseded142
1 files changed, 0 insertions, 142 deletions
diff --git a/working/hook-fail-open/validate-el.sh.superseded b/working/hook-fail-open/validate-el.sh.superseded
deleted file mode 100644
index 045c93f..0000000
--- a/working/hook-fail-open/validate-el.sh.superseded
+++ /dev/null
@@ -1,142 +0,0 @@
-#!/usr/bin/env bash
-# Validate and test .el files after Edit/Write/MultiEdit.
-# PostToolUse hook: receives tool-call JSON on stdin.
-#
-# On success: exit 0 silent.
-# On failure: emit JSON with hookSpecificOutput.additionalContext so Claude
-# sees a structured error in its context, THEN exit 2 to block the tool
-# pipeline. stderr still echoes the error for terminal visibility.
-#
-# Phase 1: check-parens + byte-compile
-# Phase 2: for non-test .el files, run matching tests/test-<stem>*.el
-
-set -u
-
-# Emit a JSON failure payload and exit 2. Arguments:
-# $1 — short failure type (e.g. "PAREN CHECK FAILED")
-# $2 — file path
-# $3 — emacs output (error body), always sent to Claude in additionalContext
-# $4 — optional compact terminal echo; when set, the terminal shows this
-# instead of the full $3 (Claude still gets the full $3). Used by the
-# test runner so a failing suite prints a short summary to the pane
-# rather than dumping every ERT backtrace.
-fail_json() {
- local ctx
- ctx="$(printf '%s: %s\n\n%s\n\nFix before proceeding.' "$1" "$2" "$3" \
- | jq -Rs .)"
- cat <<EOF
-{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": $ctx}}
-EOF
- printf '%s: %s\n%s\n' "$1" "$2" "${4:-$3}" >&2
- exit 2
-}
-
-# Report something without failing the edit. Used when the hook declines to do
-# work it would normally do: a silent decline is indistinguishable from a clean
-# run, which is how the over-cap skip below hid missing test runs for months.
-# $1 — message for both the terminal and Claude's context
-notice_json() {
- local ctx
- ctx="$(printf '%s' "$1" | jq -Rs .)"
- cat <<EOF
-{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": $ctx}}
-EOF
- printf '%s\n' "$1" >&2
-}
-
-# Portable project root: prefer Claude Code's env var, fall back to deriving
-# from this script's location ($project/.claude/hooks/validate-el.sh).
-PROJECT_ROOT="${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "$0")/../.." && pwd)}"
-
-f="$(jq -r '.tool_input.file_path // .tool_response.filePath // empty')"
-[ -z "$f" ] && exit 0
-[ "${f##*.}" = "el" ] || exit 0
-
-MAX_AUTO_TEST_FILES=20 # skip if more matches than this (large test suites)
-
-# --- Phase 1: syntax + byte-compile ---
-case "$f" in
- */init.el|*/early-init.el)
- # Byte-compile here would load the full package graph. Parens only.
- if ! output="$(emacs --batch --no-site-file --no-site-lisp "$f" \
- --eval '(check-parens)' 2>&1)"; then
- fail_json "PAREN CHECK FAILED" "$f" "$output"
- fi
- ;;
- *.el)
- # -L the file's own directory (and a sibling project root for files
- # under a tests/ subdir) so cross-project edits compile against their
- # own modules, not just this project's.
- if ! output="$(emacs --batch --no-site-file --no-site-lisp \
- --eval '(setq load-prefer-newer t)' \
- -L "$(dirname "$f")" \
- -L "$(dirname "$f")/.." \
- -L "$PROJECT_ROOT" \
- -L "$PROJECT_ROOT/modules" \
- -L "$PROJECT_ROOT/tests" \
- -L "$PROJECT_ROOT/themes" \
- --eval '(package-initialize)' \
- "$f" \
- --eval '(check-parens)' \
- --eval "(or (byte-compile-file \"$f\") (kill-emacs 1))" 2>&1)"; then
- fail_json "VALIDATION FAILED" "$f" "$output"
- fi
- ;;
-esac
-
-# --- Phase 2: test runner ---
-# Determine which tests (if any) apply to this edit. Works for projects with
-# source at root, in modules/, or elsewhere — stem-based test lookup is the
-# common pattern.
-tests=()
-case "$f" in
- */init.el|*/early-init.el)
- : # Phase 1 handled it; skip test runner
- ;;
- "$PROJECT_ROOT/tests/testutil-"*.el)
- stem="$(basename "${f%.el}")"
- stem="${stem#testutil-}"
- mapfile -t tests < <(find "$PROJECT_ROOT/tests" -maxdepth 1 -name "test-${stem}*.el" 2>/dev/null | sort)
- ;;
- "$PROJECT_ROOT/tests/test-"*.el)
- tests=("$f")
- ;;
- *.el)
- # Any other .el under the project — find matching tests by stem
- stem="$(basename "${f%.el}")"
- mapfile -t tests < <(find "$PROJECT_ROOT/tests" -maxdepth 1 -name "test-${stem}*.el" 2>/dev/null | sort)
- ;;
-esac
-
-count="${#tests[@]}"
-if [ "$count" -ge 1 ] && [ "$count" -le "$MAX_AUTO_TEST_FILES" ]; then
- load_args=()
- for t in "${tests[@]}"; do load_args+=("-l" "$t"); done
- if ! output="$(emacs --batch --no-site-file --no-site-lisp \
- --eval '(setq load-prefer-newer t)' \
- -L "$PROJECT_ROOT" \
- -L "$PROJECT_ROOT/modules" \
- -L "$PROJECT_ROOT/tests" \
- -L "$PROJECT_ROOT/themes" \
- --eval '(package-initialize)' \
- --eval "(cd \"$PROJECT_ROOT/tests\")" \
- -l ert "${load_args[@]}" \
- --eval "(ert-run-tests-batch-and-exit '(not (tag :slow)))" 2>&1)"; then
- # Terminal gets a compact summary (the run tally + the failing test names);
- # Claude still gets the full backtrace via additionalContext. Keeps the
- # pane from drowning in ERT stack frames on every red test.
- summary="$(printf '%s\n' "$output" \
- | grep -E '^Ran [0-9]+ tests|unexpected results:|^[[:space:]]+FAILED' || true)"
- [ -n "$summary" ] && summary="${summary}"$'\n'"(full backtrace in Claude's context)"
- fail_json "TESTS FAILED ($count test file(s))" "$f" "$output" "$summary"
- fi
-elif [ "$count" -gt "$MAX_AUTO_TEST_FILES" ]; then
- # Say so. Running this many files on every keystroke is not wanted, but a
- # silent skip reads exactly like a passing run, so an edit to one of the big
- # families looked verified when nothing had been tested.
- notice_json "TESTS SKIPPED: $count test files match $(basename "$f") — over the \
-auto-run cap of $MAX_AUTO_TEST_FILES, so none ran. This is NOT a pass. Run them \
-explicitly, e.g. make test-file FILE=$(basename "${tests[0]}")"
-fi
-
-exit 0