From 55834529a7d1336185bbfdcd3a3387c9db6b3e51 Mon Sep 17 00:00:00 2001 From: Matteo Cherubini Date: Sun, 10 May 2026 15:11:04 +0200 Subject: [PATCH] refactor: Pre-commit hook uses dynamic git-crypt checks --- templates/pre-commit.sh | 54 ++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/templates/pre-commit.sh b/templates/pre-commit.sh index 649b850..de2fde4 100644 --- a/templates/pre-commit.sh +++ b/templates/pre-commit.sh @@ -1,50 +1,48 @@ #!/usr/bin/env bash # ============================================================================= # .git/hooks/pre-commit -# Fail-safe security hook: Prevents plaintext leaks of sensitive data. +# Fail-safe security hook: prevents plaintext commits of encrypted files. +# Reads encryption requirements dynamically from .gitattributes via +# git check-attr — no hardcoded paths, inherits all future rules automatically. # ============================================================================= set -euo pipefail -# Directories that MUST be encrypted -PRIVATE_PATTERNS=("raw/private/" "wiki/private/") FAILED=0 -# Check on git-crypt +# Verify git-crypt is initialized if [[ ! -d ".git-crypt" ]]; then - echo -e "\n\033[0;31m[CRITICAL] git-crypt is not initialized in this repository.\033[0m" - echo "Run 'git-crypt init' and 'make setup' before committing." - exit 1 + printf "\n\033[0;31m[CRITICAL] git-crypt not initialized.\033[0m\n" + printf "Run 'git-crypt init' and 'make setup' before committing.\n" + exit 1 fi -# Get staged files (excluding deletions) +# Get staged files (additions, copies, modifications — no deletions) STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null || true) -if [[ -z "$STAGED_FILES" ]]; then - exit 0 -fi +[[ -z "$STAGED_FILES" ]] && exit 0 -for pattern in "${PRIVATE_PATTERNS[@]}"; do - while IFS= read -r file; do - if [[ "$file" == ${pattern}* ]]; then - # Check encryption status via git-crypt - STATUS=$(git-crypt status "$file" 2>/dev/null || echo "error") - if echo "$STATUS" | grep -q "not encrypted"; then - echo -e "\n\033[0;31m[SECURITY ALERT] PLAINTEXT LEAK DETECTED\033[0m" - echo "-----------------------------------------------------------" - echo "File: $file" - echo "Status: This file is in a private/ folder but is NOT encrypted." - echo "Action: Fix your .gitattributes or run 'git-crypt init'." - echo "-----------------------------------------------------------" +while IFS= read -r file; do + # Dynamically check if this file requires git-crypt encryption + filter=$(git check-attr filter -- "$file" 2>/dev/null | sed 's/.*: //') + [[ "$filter" != "git-crypt" ]] && continue + + # File is required to be encrypted — verify it actually is + status=$(git-crypt status "$file" 2>/dev/null || printf "error") + if printf '%s\n' "$status" | grep -q "not encrypted"; then + printf "\n\033[0;31m[SECURITY ALERT] PLAINTEXT LEAK DETECTED\033[0m\n" + printf -- "-----------------------------------------------------------\n" + printf "File: %s\n" "$file" + printf "Status: Marked for git-crypt in .gitattributes but NOT encrypted.\n" + printf "Action: Verify .gitattributes rules and re-run 'git-crypt init'.\n" + printf -- "-----------------------------------------------------------\n" FAILED=1 - fi fi - done <<< "$STAGED_FILES" -done +done <<< "$STAGED_FILES" if [[ "$FAILED" -ne 0 ]]; then - echo -e "\033[0;31mCommit blocked for security reasons.\033[0m\n" - exit 1 + printf "\n\033[0;31mCommit blocked: security policy violation.\033[0m\n\n" + exit 1 fi exit 0