#!/bin/bash # Pre-commit hook to prevent accidental commits of sensitive files # Enable: git config core.hooksPath .githooks set -e RED='\033[0;31m' YELLOW='\033[1;33m' GREEN='\033[0;32m' NC='\033[0m' # Allowed file extensions (whitelist) ALLOWED_EXTENSIONS=( '\.rs$' '\.toml$' '\.lock$' '\.yml$' '\.yaml$' '\.json$' '\.sh$' '\.html$' '\.css$' '\.js$' '\.gitignore$' '\.dockerignore$' 'Dockerfile$' 'LICENSE$' 'Makefile$' ) # Extensions that trigger a warning (not blocked) WARN_EXTENSIONS=( '\.md$' ) # Always blocked patterns (regardless of extension) BLOCKED_PATTERNS=( '\.env$' '\.env\.' '\.key$' '\.pem$' '\.p12$' '\.pfx$' '\.htpasswd$' 'secret' 'credential' 'password' '\.bak$' '\.swp$' '\.swo$' 'node_modules/' 'target/debug/' '\.DS_Store' ) # Get staged files (only NEW files, not already tracked) STAGED_FILES=$(git diff --cached --name-only --diff-filter=A) if [ -z "$STAGED_FILES" ]; then # No new files, only modifications to existing - allow exit 0 fi # Build patterns ALLOWED_PATTERN=$(IFS='|'; echo "${ALLOWED_EXTENSIONS[*]}") WARN_PATTERN=$(IFS='|'; echo "${WARN_EXTENSIONS[*]}") BLOCKED_PATTERN=$(IFS='|'; echo "${BLOCKED_PATTERNS[*]}") # Check for blocked patterns first BLOCKED_FILES=$(echo "$STAGED_FILES" | grep -iE "$BLOCKED_PATTERN" || true) if [ -n "$BLOCKED_FILES" ]; then echo -e "${RED}BLOCKED: Suspicious files detected in commit${NC}" echo "" echo -e "${YELLOW}Files:${NC}" echo "$BLOCKED_FILES" | sed 's/^/ - /' echo "" echo "If intentional, use: git commit --no-verify" exit 1 fi # Check for files with unknown extensions UNKNOWN_FILES="" WARN_FILES="" while IFS= read -r file; do [ -z "$file" ] && continue if echo "$file" | grep -qE "$BLOCKED_PATTERN"; then continue # Already handled above elif echo "$file" | grep -qE "$WARN_PATTERN"; then WARN_FILES="$WARN_FILES$file"$'\n' elif ! echo "$file" | grep -qE "$ALLOWED_PATTERN"; then UNKNOWN_FILES="$UNKNOWN_FILES$file"$'\n' fi done <<< "$STAGED_FILES" # Warn about .md files if [ -n "$WARN_FILES" ]; then echo -e "${YELLOW}WARNING: Markdown files in commit:${NC}" echo "$WARN_FILES" | sed '/^$/d' | sed 's/^/ - /' echo "" fi # Block unknown extensions if [ -n "$UNKNOWN_FILES" ]; then echo -e "${RED}BLOCKED: Files with unknown extensions:${NC}" echo "$UNKNOWN_FILES" | sed '/^$/d' | sed 's/^/ - /' echo "" echo "Allowed extensions: rs, toml, lock, yml, yaml, json, sh, html, css, js, md" echo "If intentional, use: git commit --no-verify" exit 1 fi # Check for large files (>5MB) LARGE_FILES=$(echo "$STAGED_FILES" | while read f; do if [ -f "$f" ]; then size=$(stat -f%z "$f" 2>/dev/null || stat -c%s "$f" 2>/dev/null || echo 0) if [ "$size" -gt 5242880 ]; then echo "$f ($(numfmt --to=iec $size 2>/dev/null || echo "${size}B"))" fi fi done) if [ -n "$LARGE_FILES" ]; then echo -e "${YELLOW}WARNING: Large files (>5MB) in commit:${NC}" echo "$LARGE_FILES" | sed 's/^/ - /' echo "" fi # Run cargo fmt check if Rust files changed if git diff --cached --name-only | grep -q '\.rs$'; then if command -v cargo &> /dev/null; then if ! cargo fmt --check &> /dev/null; then echo -e "${RED}BLOCKED: cargo fmt check failed${NC}" echo "Run: cargo fmt" exit 1 fi fi fi exit 0