diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 00000000..912a677d --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,35 @@ +#!/usr/bin/env bash +# pre-commit: block commits containing secrets +# Scans staged files for known secret patterns +set -e + +PATTERNS_FILE="$(git rev-parse --show-toplevel)/.secret-patterns.txt" +STAGED=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null) + +if [ ! -f "$PATTERNS_FILE" ]; then + exit 0 # no patterns file = no scanning +fi + +FOUND=0 +for file in $STAGED; do + [ -f "$file" ] || continue + while IFS= read -r pattern; do + [ -z "$pattern" ] && continue + [[ "$pattern" == \#* ]] && continue + MATCH=$(git diff --cached -- "$file" | grep -nP "$pattern" 2>/dev/null | head -3) + if [ -n "$MATCH" ]; then + echo "SECRET DETECTED in $file (pattern: $pattern):" + echo "$MATCH" + FOUND=1 + fi + done < "$PATTERNS_FILE" +done + +if [ $FOUND -eq 1 ]; then + echo "" + echo "COMMIT BLOCKED — secret pattern detected." + echo "Override (NOT RECOMMENDED): git commit --no-verify" + exit 1 +fi + +exit 0 diff --git a/.githooks/pre-push b/.githooks/pre-push new file mode 100755 index 00000000..a4643c23 --- /dev/null +++ b/.githooks/pre-push @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# pre-push: block pushes containing secrets + run quality gates +# Scans all commits being pushed for secret patterns +set -e + +PATTERNS_FILE="$(git rev-parse --show-toplevel)/.secret-patterns.txt" + +if [ ! -f "$PATTERNS_FILE" ]; then + exit 0 +fi + +# Get commits being pushed +RANGE="@{u}..HEAD" +COMMITS=$(git rev-list "$RANGE" 2>/dev/null || echo "") + +if [ -z "$COMMITS" ]; then + # No upstream — scan last 5 commits + COMMITS=$(git rev-list HEAD~5..HEAD 2>/dev/null || echo "") +fi + +FOUND=0 +while IFS= read -r pattern; do + [ -z "$pattern" ] && continue + [[ "$pattern" == \#* ]] && continue + MATCH=$(git log -p $RANGE 2>/dev/null | grep -nP "$pattern" 2>/dev/null | head -5) + if [ -n "$MATCH" ]; then + echo "SECRET in push history (pattern: $pattern):" + echo "$MATCH" + FOUND=1 + fi +done < "$PATTERNS_FILE" + +if [ $FOUND -eq 1 ]; then + echo "" + echo "PUSH BLOCKED — secrets detected in commit history." + echo "Scrub with: git filter-repo --replace-text .secret-patterns.txt" + exit 1 +fi + +echo "No secrets detected." +exit 0 diff --git a/.secret-patterns.txt b/.secret-patterns.txt new file mode 100644 index 00000000..24fe5cfb --- /dev/null +++ b/.secret-patterns.txt @@ -0,0 +1,28 @@ +# Secret patterns — one regex per line, # for comments +# Block commits/pushes containing these patterns + +# Nostr private keys +nsec1[a-z0-9]{20,} + +# API keys (PPQ, OpenAI, etc.) +sk-[A-Za-z0-9]{10,} + +# z.ai key format (32-char hex hash + dot + alphanumeric) +[0-9a-f]{32}\.[A-Za-z0-9]{8,} + +# Private keys (PEM format) +-----BEGIN [A-Z ]*PRIVATE KEY + +# Bearer tokens +Bearer [A-Za-z0-9\-._~+/]{20,} + +# Hardcoded password/secret assignments (not env vars) +password.*=.*['\"][^'\"]{4,}['\"] +api_key.*=.*['\"][^'\"]{8,}['\"] +secret_key.*=.*['\"][^'\"]{8,}['\"] + +# Signal phone numbers +\+1\d{10} + +# Matrix access tokens +syt\.[A-Za-z0-9_-]{10,}