fix(verify-no-secrets): resolve repo root via git, allow scaffolding under secrets/
Two defects found by testing the guard against itself: 1. When invoked through the .git/hooks/pre-commit symlink, deriving the repo root from dirname(BASH_SOURCE)/.. resolved to .git/ instead of the work tree, so the hook scanned nothing and never blocked. Use 'git rev-parse --show-toplevel' instead. 2. The blanket secrets/ rule rejected secrets/.gitkeep. Replaced with an explicit allowlist: .gitkeep, README.md, *.example, *.template. Verified: a staged file containing a Telegram bot token now aborts the commit and leaves HEAD unchanged.
This commit is contained in:
@@ -16,7 +16,13 @@
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
# Resolve the repository root via git, not via BASH_SOURCE: when this script is
|
||||||
|
# invoked through the .git/hooks/pre-commit symlink, dirname(BASH_SOURCE)/..
|
||||||
|
# resolves to .git/ rather than the work tree.
|
||||||
|
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || {
|
||||||
|
echo "verify-no-secrets: not inside a git work tree" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
cd "$REPO_ROOT" || exit 1
|
cd "$REPO_ROOT" || exit 1
|
||||||
|
|
||||||
MODE="${1:-staged}"
|
MODE="${1:-staged}"
|
||||||
@@ -40,14 +46,21 @@ fail() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# --- Rule 1: filenames that must never be committed ------------------------
|
# --- Rule 1: filenames that must never be committed ------------------------
|
||||||
FORBIDDEN_NAMES='(^|/)(models\.json|auth\.json|trust\.json|models-store\.json)$|(^|/)\.env$|\.env\.[^/]*$|(^|/)id_(ed25519|rsa)|\.(pem|p12)$|\.rendered(\..*)?$|(^|/)secrets/(?!.*\.(example|template)$)'
|
FORBIDDEN_NAMES='(^|/)(models\.json|auth\.json|trust\.json|models-store\.json)$|(^|/)\.env$|\.env\.[^/]*$|(^|/)id_(ed25519|rsa)|\.(pem|p12)$|\.rendered(\..*)?$'
|
||||||
|
# Inside secrets/ only documentation and placeholder scaffolding may be tracked.
|
||||||
|
SECRETS_ALLOWED='(^|/)secrets/(\.gitkeep|README\.md|.*\.(example|template))$'
|
||||||
for f in "${FILES[@]}"; do
|
for f in "${FILES[@]}"; do
|
||||||
# .env.example / .env.template are allowed
|
# Templates and examples are the intended way to track credential-shaped files.
|
||||||
case "$f" in
|
case "$f" in
|
||||||
*.env.example|*.env.template|*.example|*.template) continue ;;
|
*.env.example|*.env.template|*.example|*.template) continue ;;
|
||||||
esac
|
esac
|
||||||
if printf '%s' "$f" | grep -qP "$FORBIDDEN_NAMES"; then
|
if printf '%s' "$f" | grep -qP "$FORBIDDEN_NAMES"; then
|
||||||
fail "$f" "filename is on the never-commit list"
|
fail "$f" "filename is on the never-commit list"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
if printf '%s' "$f" | grep -qP '(^|/)secrets/' \
|
||||||
|
&& ! printf '%s' "$f" | grep -qP "$SECRETS_ALLOWED"; then
|
||||||
|
fail "$f" "files under secrets/ may only be .gitkeep, README.md, *.example or *.template"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user