Created
November 25, 2025 15:27
-
-
Save stracker-phil/d7be22d9ee16127cf3cbf3c258acb82e to your computer and use it in GitHub Desktop.
Git Hooks to protect against Sha1-Hulud
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # FILE: .git/hooks/post-checkout | |
| # Updates the DDEV environment after switching a branch. | |
| PREV_HEAD="$1" # Hash of previous HEAD | |
| NEW_HEAD="$2" # Hash of new HEAD | |
| CHECKOUT_TYPE="$3" # 1 if checking out a branch, 0 if checking out something else, such as a file (rollbacks) | |
| # No action when we are not checkout out a branch. | |
| [ "1" != "$CHECKOUT_TYPE" ] && exit 0 | |
| # First: Scan the changeset for security issues. | |
| .git/hooks/security-check.sh post-checkout "$PREV_HEAD" "$NEW_HEAD" || exit 1 | |
| # Your other post-checkout logic follows here... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # FILE: .git/hooks/pre-commit | |
| # The hook should exit with non-zero status after issuing an appropriate message | |
| # if it wants to stop the commit. | |
| # | |
| # Action: Security checks, phpcs/psalm check on staged PHP files. | |
| # Note: This hook will validate the _entire_ staged file, even if the staged | |
| # file contains unstaged code blocks. | |
| # First: Scan the commit for security issues. | |
| .git/hooks/security-check.sh pre-commit || exit 1 | |
| # Your other pre-commit logic follows here... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # FILE: .git/hooks/security-check.sh | |
| # security-check.sh - Standalone malware detection | |
| # Supports pre-commit and post-checkout hooks. | |
| # Returns: 0 if clean, 1 if malware found. | |
| # | |
| # Issues detected: | |
| # - Sha1-Hulud (the second coming) | |
| # | |
| # Usage in pre-commit: | |
| # .git/hooks/security-check.sh pre-commit || exit 1 | |
| # | |
| # Usage in post-checkout: | |
| # PREV_HEAD=$1 | |
| # NEW_HEAD=$2 | |
| # CHECKOUT_TYPE=$3 | |
| # | |
| # # No action when we are not checkout out a branch. | |
| # [ "1" != "$CHECKOUT_TYPE" ] && exit 0 | |
| # | |
| # .git/hooks/security-check.sh post-checkout "$PREV_HEAD" "$NEW_HEAD" | |
| set -euo pipefail | |
| # Detect hook context | |
| HOOK_TYPE="${1:-pre-commit}" # pre-commit or post-checkout | |
| # Parse remaining flags | |
| shift 2>/dev/null || true | |
| QUIET=0 | |
| while [[ $# -gt 0 ]]; do | |
| case $1 in | |
| -q|--quiet) QUIET=1; shift ;; | |
| *) shift ;; | |
| esac | |
| done | |
| # Colors | |
| if [ $QUIET -eq 0 ]; then | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| NC='\033[0m' | |
| else | |
| RED=''; GREEN=''; YELLOW=''; NC='' | |
| fi | |
| log() { [ $QUIET -eq 0 ] && echo -e "$@"; } | |
| # Sha1-Hulud (the second coming) | |
| MALWARE_CSV_URL="https://docs.google.com/spreadsheets/d/16aw6s7mWoGU7vxBciTEZSaR5HaohlBTfVirvI-PypJc/export?format=csv&gid=1289659284" | |
| MALWARE_CSV="/tmp/sha1hulud_$$.csv" | |
| cleanup() { rm -f "$MALWARE_CSV"; } | |
| trap cleanup EXIT INT TERM | |
| # Get files to check based on hook type | |
| if [ "$HOOK_TYPE" = "post-checkout" ]; then | |
| # Check files that changed in the checkout | |
| # post-checkout receives: prev_head new_head branch_flag | |
| PREV_HEAD="${2:-HEAD@{1}}" | |
| NEW_HEAD="${3:-HEAD}" | |
| CHANGED_LOCK_FILES=$(git diff --name-only "$PREV_HEAD" "$NEW_HEAD" 2>/dev/null | grep -E '(package-lock\.json|package\.json|yarn\.lock|pnpm-lock\.yaml)$' || true) | |
| else | |
| # pre-commit: check staged files | |
| CHANGED_LOCK_FILES=$(git diff --cached --name-only --diff-filter=ACM 2>/dev/null | grep -E '(package-lock\.json|package\.json|yarn\.lock|pnpm-lock\.yaml)$' || true) | |
| fi | |
| if [ -z "$CHANGED_LOCK_FILES" ]; then | |
| log "${GREEN}✓ No lock files changed${NC}" | |
| exit 0 | |
| fi | |
| log "${YELLOW}Scanning lock files for malware...${NC}" | |
| # Download signatures | |
| if command -v curl &> /dev/null; then | |
| curl -sfL "$MALWARE_CSV_URL" -o "$MALWARE_CSV" 2>/dev/null || { | |
| log "${RED}✗ Failed to download malware list${NC}" | |
| exit 1 | |
| } | |
| else | |
| wget -q "$MALWARE_CSV_URL" -O "$MALWARE_CSV" 2>/dev/null || { | |
| log "${RED}✗ Failed to download malware list${NC}" | |
| exit 1 | |
| } | |
| fi | |
| [ ! -s "$MALWARE_CSV" ] && { log "${RED}✗ Empty malware list${NC}"; exit 1; } | |
| MALWARE_COUNT=$(tail -n +2 "$MALWARE_CSV" 2>/dev/null | grep -c ^ || echo "0") | |
| log "${YELLOW}Checking against $MALWARE_COUNT signatures${NC}" | |
| MALWARE_FOUND=0 | |
| while IFS= read -r file; do | |
| [ -z "$file" ] && continue | |
| log " Checking: $file" | |
| # Read file content differently based on hook | |
| if [ "$HOOK_TYPE" = "post-checkout" ]; then | |
| # Read from working directory | |
| FILE_CONTENT=$(cat "$file" 2>/dev/null || true) | |
| else | |
| # Read from staging area | |
| FILE_CONTENT=$(git show ":$file" 2>/dev/null || true) | |
| fi | |
| [ -z "$FILE_CONTENT" ] && continue | |
| while IFS=',' read -r package version; do | |
| package=$(echo "$package" | tr -d '"' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') | |
| version=$(echo "$version" | tr -d '"' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') | |
| [ -z "$package" ] || [ "$package" = "Package Name" ] && continue | |
| if echo "$FILE_CONTENT" | grep -qF "\"$package\"" 2>/dev/null; then | |
| if [ -n "$version" ]; then | |
| if echo "$FILE_CONTENT" | grep -F "\"$package\"" | grep -qF "\"$version\"" 2>/dev/null; then | |
| log " ${RED}✗ MALWARE: $package@$version${NC}" | |
| MALWARE_FOUND=1 | |
| else | |
| log " ${RED}✗ SUSPICIOUS: $package${NC}" | |
| MALWARE_FOUND=1 | |
| fi | |
| else | |
| log " ${RED}✗ SUSPICIOUS: $package${NC}" | |
| MALWARE_FOUND=1 | |
| fi | |
| fi | |
| done < <(tail -n +2 "$MALWARE_CSV" 2>/dev/null) | |
| done <<< "$CHANGED_LOCK_FILES" | |
| if [ $MALWARE_FOUND -eq 1 ]; then | |
| log "" | |
| log "${RED}╔════════════════════════════════╗${NC}" | |
| log "${RED}║ MALWARE DETECTED! ║${NC}" | |
| log "${RED}╚════════════════════════════════╝${NC}" | |
| log "" | |
| if [ "$HOOK_TYPE" = "post-checkout" ]; then | |
| log "${YELLOW}You just checked out malicious code!${NC}" | |
| log "" | |
| log "Immediate actions:" | |
| log " 1. DO NOT run npm install/yarn install/composer install!" | |
| log " 2. Return to previous branch:" | |
| log " git checkout -" | |
| log " 3. Report to your team" | |
| log "" | |
| log "Info: https://www.wiz.io/blog/shai-hulud-2-0-ongoing-supply-chain-attack" | |
| fi | |
| log "DO NOT proceed. Remove malicious packages first." | |
| exit 1 | |
| fi | |
| log "${GREEN}✓ Clean: No security vulnerability found${NC}" | |
| exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment