Add machine-level suppressions to the repo-sourced hooks

The live global pre-push hook was hand-edited to resolve suppressions from a
machine-level file (${SH_SECURITY_SUPPRESSIONS_DIR:-~/.config/sea-haven/security-review}/
<repo-basename>/suppressions.json) kept out of repo history, falling back to a
repo-local .security-review/suppressions.json. The repo-sourced hooks lacked it, so
install-hooks.sh --global would overwrite the live hook and lose the feature.

Port the prefer-machine/fallback-repo-local block into hooks/pre-push, align
hooks/pre-commit to the same (else a machine-suppressed finding passes at push but
blocks at commit), and document the path + SH_SECURITY_SUPPRESSIONS_DIR override +
basename-collision caveat in the README.
This commit is contained in:
Adam Moussa 2026-06-16 15:33:43 -04:00
parent f4dd72ced9
commit c217c5656d
3 changed files with 35 additions and 6 deletions

View file

@ -32,10 +32,21 @@ security-review/install-hooks.sh --global
security-review/install-hooks.sh /path/to/repo
```
The global mode sets `git config --global core.hooksPath ~/.config/git/hooks`. Skip a repo with a
`.security-review-skip` file at its root; suppress a specific false positive in the repo's
`.security-review/suppressions.json` (a written justification is required and is surfaced); bypass once
with `git push --no-verify`. Caveat: a repo with its own local `core.hooksPath` overrides the global hook
— install per-repo there. See memory `reference_global_security_review_hook`.
`.security-review-skip` file at its root; bypass once with `git push --no-verify`. Caveat: a repo with
its own local `core.hooksPath` overrides the global hook — install per-repo there. See memory
`reference_global_security_review_hook`.
**Suppressing a false positive.** A written justification is required and is surfaced in the report. The
hooks resolve a suppressions file in this order:
1. **Machine-level (preferred), kept out of repo history:**
`${SH_SECURITY_SUPPRESSIONS_DIR:-~/.config/sea-haven/security-review}/<repo-basename>/suppressions.json`
(override the base dir with `SH_SECURITY_SUPPRESSIONS_DIR`). Keeps a suppression from becoming a
permanent in-history "ignore."
2. **Repo-local fallback:** `<repo>/.security-review/suppressions.json` (used only if no machine-level file exists).
Same JSON either place: `{"suppressions":[{"id":"<review.sh finding id>","justification":"…"}]}`. Caveat:
machine-level files are keyed by **repo basename**, so two repos sharing a name collide — fine for the
current single-namespace layout under `~/Documents/repositories`.
## No CI — by design
There is **no CI** wiring for this gate. For a solo dev the git hooks + nightly VM sweep are the backstop,

View file

@ -14,6 +14,15 @@ fi
# Nothing staged -> nothing to do.
git diff --cached --name-only --diff-filter=ACM | grep -q . || exit 0
SUP=()
[ -f "$REPO_ROOT/.security-review/suppressions.json" ] && SUP=(--suppressions "$REPO_ROOT/.security-review/suppressions.json")
# Suppressions: prefer a MACHINE-LEVEL file kept out of repo history
# (<dir>/<repo-basename>/suppressions.json), else fall back to a repo-local
# .security-review/suppressions.json. Keyed by repo basename — adequate for the
# current single-namespace layout under ~/Documents/repositories.
MACHINE_SUP="${SH_SECURITY_SUPPRESSIONS_DIR:-$HOME/.config/sea-haven/security-review}/$(basename "$REPO_ROOT")/suppressions.json"
if [ -f "$MACHINE_SUP" ]; then
SUP=(--suppressions "$MACHINE_SUP")
elif [ -f "$REPO_ROOT/.security-review/suppressions.json" ]; then
SUP=(--suppressions "$REPO_ROOT/.security-review/suppressions.json")
fi
# ${SUP[@]+"${SUP[@]}"} = bash-3.2-safe expansion of a possibly-empty array under set -u.
exec bash "$REVIEW_SH" --scanners-only ${SUP[@]+"${SUP[@]}"} "$REPO_ROOT"

View file

@ -10,7 +10,16 @@ REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
REVIEW_SH="${SH_REVIEW_SH:-$HOME/Documents/repositories/orchestrator/security-review/review.sh}"
if [ -f "$REVIEW_SH" ]; then
SUP=()
[ -f "$REPO_ROOT/.security-review/suppressions.json" ] && SUP=(--suppressions "$REPO_ROOT/.security-review/suppressions.json")
# Suppressions: prefer a MACHINE-LEVEL file kept out of repo history
# (<dir>/<repo-basename>/suppressions.json), else fall back to a repo-local
# .security-review/suppressions.json. Keyed by repo basename — adequate for the
# current single-namespace layout under ~/Documents/repositories.
MACHINE_SUP="${SH_SECURITY_SUPPRESSIONS_DIR:-$HOME/.config/sea-haven/security-review}/$(basename "$REPO_ROOT")/suppressions.json"
if [ -f "$MACHINE_SUP" ]; then
SUP=(--suppressions "$MACHINE_SUP")
elif [ -f "$REPO_ROOT/.security-review/suppressions.json" ]; then
SUP=(--suppressions "$REPO_ROOT/.security-review/suppressions.json")
fi
echo "security-review: scanning $REPO_ROOT (scanners-only) before push..." >&2
# ${SUP[@]+"${SUP[@]}"} = bash-3.2-safe expansion of a possibly-empty array under set -u.
if ! bash "$REVIEW_SH" --scanners-only ${SUP[@]+"${SUP[@]}"} "$REPO_ROOT"; then