No repo here scanned for committed credentials. The hook is self-contained rather than delegating to a Makefile, because this repo has none and a hook reaching into a sibling repo breaks the moment this one is cloned elsewhere. Scans the outgoing range rather than full history: history carries settled findings — test fixtures, vendored third-party code — and a gate that fails on something unfixable gets bypassed within a week. Setting core.hooksPath means pql init must replant its replication shims into .githooks, which is why they are gitignored here alongside the tracked pre-push. Same layout pql itself uses. Co-Authored-By: Claude <noreply@anthropic.com>
53 lines
2.1 KiB
Bash
Executable File
53 lines
2.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Secret scan over the commits about to be pushed. Opt in per clone with:
|
|
#
|
|
# git config core.hooksPath .githooks
|
|
#
|
|
# Self-contained on purpose: this repo has no Makefile, and a hook that
|
|
# depends on one in a sibling repo breaks the moment the repo is cloned
|
|
# anywhere else.
|
|
set -euo pipefail
|
|
|
|
cd "$(git rev-parse --show-toplevel)"
|
|
|
|
# A non-login shell — which is what git gives a hook — skips /etc/profile.d
|
|
# and never sees ~/.local/bin, where the gitleaks release tarball lands.
|
|
# Without this the scan reports "not installed" on every push.
|
|
[ -d "$HOME/.local/bin" ] && PATH="$HOME/.local/bin:$PATH"
|
|
|
|
if ! command -v gitleaks >/dev/null 2>&1; then
|
|
echo "FAIL secrets — gitleaks not installed, so this check would be a no-op pretending to pass." >&2
|
|
echo " https://github.com/gitleaks/gitleaks/releases → ~/.local/bin/gitleaks" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Scan the outgoing range, not full history. History here carries findings
|
|
# that are settled — test fixtures and vendored third-party code — and a gate
|
|
# that fails on something unfixable gets bypassed within a week. What matters
|
|
# is what is about to leave this machine.
|
|
if upstream=$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null); then
|
|
range="$upstream..HEAD"
|
|
elif git rev-parse --verify --quiet origin/main >/dev/null; then
|
|
range="origin/main..HEAD"
|
|
else
|
|
range=""
|
|
fi
|
|
|
|
if [ -z "$range" ]; then
|
|
gitleaks dir . --redact --no-banner --exit-code 1 || {
|
|
echo "FAIL secrets — gitleaks found a credential in the working tree." >&2; exit 1; }
|
|
exit 0
|
|
fi
|
|
|
|
[ -n "$(git log --oneline "$range" 2>/dev/null)" ] || exit 0
|
|
|
|
gitleaks git . --log-opts="$range" --redact --no-banner --exit-code 1 >/dev/null 2>&1 || {
|
|
echo "FAIL secrets — gitleaks found a credential in the commits being pushed." >&2
|
|
echo " inspect (values redacted): gitleaks git . --log-opts=\"$range\" --redact" >&2
|
|
echo " then remove and rotate it, or suppress deliberately:" >&2
|
|
echo " inline '# gitleaks:allow <reason>'" >&2
|
|
echo " or add the fingerprint to .gitleaksignore WITH a reason" >&2
|
|
exit 1
|
|
}
|
|
echo " ok secrets"
|