diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-11 14:47:10 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-11 15:01:57 +0200 |
| commit | 42b02c47be9b285099203e44a2570636d4ca6f03 (patch) | |
| tree | 82bcb9e19bd886f36e1ca7b2d94a204c988f1fd1 /scripts/leak-check | |
| download | krino-42b02c47be9b285099203e44a2570636d4ca6f03.tar.gz krino-42b02c47be9b285099203e44a2570636d4ca6f03.zip | |
krino: foundation — sexp reader, config language, init/new/check
Diffstat (limited to 'scripts/leak-check')
| -rwxr-xr-x | scripts/leak-check | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/scripts/leak-check b/scripts/leak-check new file mode 100755 index 0000000..fbbd5d5 --- /dev/null +++ b/scripts/leak-check @@ -0,0 +1,70 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-3.0-or-later +# +# leak-check: refuse personal data in the files git would commit. +# +# Scans every staged file (the index, not the working tree), and the names +# of staged files against the private list, for: +# - the current user's home directory, e.g. /home/alice; +# - email addresses (LICENSE is exempt); +# - a private pattern list: one extended regular expression per line, +# blank lines and lines starting with # ignored, matched ignoring case. +# Its path is set per clone with: git config krino.leakpatterns FILE +# Keep that file outside the repository so the list is never published. +# +# A line matching the regex in `git config krino.leakallow` is never +# reported (for example a public contact address in the README). +# +# Prints file:line for each hit, never the matched text, so a hit on a +# private pattern does not echo the secret. Exits 0 when clean, 1 when +# something matched, 2 when the private list is configured but missing. + +set -eu + +cd "$(git rev-parse --show-toplevel)" + +allow=$(git config --get krino.leakallow || true) +hits=0 + +# check LABEL GREP-ARGS...: report staged lines that match, as file:line. +check() { + label=$1 + shift + out=$(git grep --cached -n -I "$@" | { if [ -n "$allow" ]; then grep -v -E -i -e "$allow"; else cat; fi; } | cut -d: -f1,2 || true) + if [ -n "$out" ]; then + printf 'leak-check: %s:\n%s\n' "$label" "$out" | sed '2,$s/^/ /' >&2 + hits=1 + fi +} + +home=${HOME%/} # a HOME of / becomes empty and is skipped +if [ -n "$home" ]; then + check "your home directory ($home)" -F -e "$home" -- . +fi + +check "an email address" -E -e '[[:alnum:]._%+-]+@[[:alnum:].-]+\.[[:alpha:]]{2,}' -- . ':(exclude)LICENSE' + +list=$(git config --type=path --get krino.leakpatterns || true) +if [ -z "$list" ]; then + echo "leak-check: no private pattern list (git config krino.leakpatterns FILE); built-in checks only" >&2 +elif [ ! -r "$list" ]; then + echo "leak-check: private pattern list $list is configured but cannot be read" >&2 + exit 2 +else + patterns=$(mktemp) + trap 'rm -f "$patterns"' EXIT + grep -v -e '^[[:space:]]*#' -e '^[[:space:]]*$' "$list" > "$patterns" || true + if [ -s "$patterns" ]; then + check "a private pattern from $list" -i -E -f "$patterns" -- . + named=$(git ls-files --cached | grep -i -E -f "$patterns" || true) + if [ -n "$named" ]; then + printf 'leak-check: a file name matching a private pattern:\n%s\n' "$named" | sed '2,$s/^/ /' >&2 + hits=1 + fi + fi +fi + +if [ "$hits" -ne 0 ]; then + echo "leak-check: remove the data above before committing; for a false positive, narrow the pattern or set krino.leakallow" >&2 + exit 1 +fi |
