Skip to content

Instantly share code, notes, and snippets.

@stracker-phil
Created November 25, 2025 15:27
Show Gist options
  • Select an option

  • Save stracker-phil/d7be22d9ee16127cf3cbf3c258acb82e to your computer and use it in GitHub Desktop.

Select an option

Save stracker-phil/d7be22d9ee16127cf3cbf3c258acb82e to your computer and use it in GitHub Desktop.
Git Hooks to protect against Sha1-Hulud
#!/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...
#!/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...
#!/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