Skip to content

Instantly share code, notes, and snippets.

@olaurendeau
Last active November 27, 2025 14:15
Show Gist options
  • Select an option

  • Save olaurendeau/f8ce0dac158334ecb7fa5579a2c365de to your computer and use it in GitHub Desktop.

Select an option

Save olaurendeau/f8ce0dac158334ecb7fa5579a2c365de to your computer and use it in GitHub Desktop.
Incubateur des territoires - Shai-Hulud Vulnerability Checker

🛡️ Shai-Hulud Vulnerability Checker

Script bash rapide pour détecter les paquets npm compromis par l'attaque supply-chain Shai-Hulud 2.0 (novembre 2024).

🚀 Utilisation

# Rendre le script exécutable
chmod +x check-shai-hulud-fast.sh

# Analyser un fichier lock spécifique
./check-shai-hulud-fast.sh yarn.lock
./check-shai-hulud-fast.sh package-lock.json
./check-shai-hulud-fast.sh pnpm-lock.yaml
./check-shai-hulud-fast.sh bun.lock

# Analyser tous les lock files d'un répertoire (récursif)
./check-shai-hulud-fast.sh /path/to/project
./check-shai-hulud-fast.sh ~/monorepo

# Mode debug (affiche plus d'informations)
./check-shai-hulud-fast.sh yarn.lock --debug
./check-shai-hulud-fast.sh /path/to/project --debug

Note pour Bun : Si vous avez bun.lockb (format binaire), convertissez-le d'abord :

bun install  # Génère bun.lock (texte)

✨ Fonctionnalités

  • Rapide : analyse optimisée (O(n+m) au lieu de O(n×m))
  • 🎯 Détection précise : compare versions exactes avec la liste officielle
  • 📦 Multi-formats : yarn.lock, package-lock.json, pnpm-lock.yaml (v6 et v9), bun.lock
  • 📂 Scan récursif : analyse tous les lock files d'un répertoire en une seule commande
  • 🔄 Deux sources : agrège Tenable + Datadog pour une couverture maximale
  • ⚠️ Warnings : alerte sur les paquets affectés dans d'autres versions
  • 📊 Timers détaillés : affiche le temps de chaque étape
  • 📋 Récapitulatif global : synthèse des vulnérabilités avec localisation des fichiers
  • 🔧 Sans dépendances : uniquement bash, grep, awk, curl
  • 🚦 Code de sortie : 0 si safe, 1 si vulnérabilités (parfait pour CI/CD)

📋 Prérequis

  • Bash 4+
  • curl (pour télécharger la liste officielle)
  • Unix/Linux/macOS (non testé sur Windows/WSL)

🔍 Que fait le script ?

  1. Télécharge les listes officielles des paquets compromis (Tenable + Datadog)
  2. Analyse et fusionne les deux sources (dédoublonnage)
  3. Extrait tous les paquets installés de vos fichier(s) lock
  4. Compare les versions pour détecter les correspondances exactes
  5. Affiche les vulnérabilités par fichier + récapitulatif global
  6. Retourne un code de sortie approprié (0 = safe, 1 = vulnérabilités)

📊 Exemple de sortie

Scan d'un répertoire

$ ./check-shai-hulud-fast.sh ~/monorepo/

📥 Téléchargement des listes de paquets vulnérables...
   ✓ Liste Tenable téléchargée
   ✓ Liste Datadog téléchargée
   ⏱️  Téléchargement: 1s

🔍 Analyse des listes de vulnérabilités...
   ✓ Liste Tenable analysée (1371 entrées)
   ✓ Liste Datadog analysée (5030 entrées)
   ✓ 5645 entrées vulnérables uniques
   ⏱️  Analyse: 0s

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📁 Analyse: /home/project/packages/frontend/yarn.lock
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📦 Extraction des paquets installés...
   ✓ 847 paquets détectés
   ⏱️  Extraction: 0s

🔍 Recherche de correspondances...
   ⚠️  VULNÉRABILITÉ: @accordproject/concerto-analysis@3.24.1
   ⏱️  Comparaison: 2s

❌ 1 vulnérabilité(s) détectée(s)
   • @accordproject/concerto-analysis@3.24.1

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📁 Analyse: /home/project/packages/backend/package-lock.json
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

📦 Extraction des paquets installés...
   ✓ 1074 paquets détectés
   ⏱️  Extraction: 0s

🔍 Recherche de correspondances...
   ⏱️  Comparaison: 3s

✅ Aucune vulnérabilité détectée dans ce fichier

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 RÉSUMÉ GLOBAL
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

🚨 DES VULNÉRABILITÉS ONT ÉTÉ DÉTECTÉES

📋 Récapitulatif des paquets compromis:

  • @accordproject/concerto-analysis@3.24.1
    → /home/project/packages/frontend/yarn.lock

Actions urgentes:
   1. NE PAS mettre à jour vos dépendances
   2. Isolez l'environnement concerné
   3. Vérifiez vos logs pour toute activité suspecte
   4. Tournez vos secrets (npm, GitHub, cloud, etc.)
   5. Contactez votre équipe de sécurité

🔄 Utilisation en CI/CD

Le script retourne un code de sortie approprié pour l'intégration dans vos pipelines :

# GitHub Actions
- name: Check for Shai-Hulud vulnerabilities
  run: |
    curl -O https://raw.githubusercontent.com/.../check-shai-hulud-fast.sh
    chmod +x check-shai-hulud-fast.sh
    ./check-shai-hulud-fast.sh .
# GitLab CI
security_scan:
  script:
    - curl -O https://raw.githubusercontent.com/.../check-shai-hulud-fast.sh
    - chmod +x check-shai-hulud-fast.sh
    - ./check-shai-hulud-fast.sh .
  allow_failure: false

⚠️ Important

  • NE PAS mettre à jour vos dépendances actuellement
  • La liste des paquets compromis évolue encore
  • En cas de détection, contactez un expert en sécurité
  • Consultez les ressources officielles pour plus d'informations

📚 Ressources

📝 Notes

  • Le script ne modifie jamais vos fichiers
  • Les résultats sont basés sur la liste officielle au moment de l'exécution
  • Code de sortie : 0 si aucune vulnérabilité, 1 si vulnérabilités détectées

Exemple de sortie

📥 Téléchargement des listes de paquets vulnérables...
   ✓ Liste Tenable téléchargée
   ✓ Liste Datadog téléchargée
   ⏱️  Téléchargement: 1s
🔍 Analyse de /Users/olaurendeau/Downloads/yarn.lock...


📦 Extraction des paquets installés...
   ✓ 3 paquets détectés
   ⏱️  Extraction: 0s

🔍 Analyse des listes de vulnérabilités...
   ✓ Liste Tenable analysée (1371 entrées)
   ✓ Liste Datadog analysée (5030 entrées)
   ✓ 5645 entrées vulnérables uniques
   ⏱️  Analyse: 1s

🔍 Recherche de correspondances...
   ⚠️  VULNÉRABILITÉ: @accordproject/concerto-analysis@3.24.1
   ⚠️  VULNÉRABILITÉ: @ahmedhfarag/ngx-perfect-scrollbar@20.0.20
   ⏱️  Comparaison: 17s

⚠️  Vérification des paquets affectés (autres versions)...
   ⏱️  Vérification warnings: 0s

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

❌ 2 version(s) vulnérable(s) détectée(s)

🔴 Paquets compromis identifiés:
   • @accordproject/concerto-analysis@3.24.1
   • @ahmedhfarag/ngx-perfect-scrollbar@20.0.20

🤝 Contribution

Ce script est fourni tel quel. Pour signaler un bug ou proposer une amélioration, ouvrez une issue.


⚡ Rappel : Tous les paquets compromis n'ont probablement pas encore été identifiés. Restez vigilant.

#!/bin/bash
# Script de vérification des paquets vulnérables Shai-Hulud (Version optimisée)
# Usage: ./check-shai-hulud-fast.sh <lock-file-or-directory> [--debug]
# Version sans dépendance jq
# Démarrer le timer global
START_TIME=$(date +%s)
# Variable globale pour tracker les vulnérabilités
GLOBAL_VULNERABILITIES_FOUND=0
# Sources de données
TENABLE_URL="https://raw.githubusercontent.com/tenable/shai-hulud-second-coming-affected-packages/main/list.json"
DATADOG_URL="https://raw.githubusercontent.com/DataDog/indicators-of-compromise/main/shai-hulud-2.0/consolidated_iocs.csv"
LOCK_FILE_OR_DIR="$1"
DEBUG=false
# Vérifier si --debug est passé
if [ "$2" = "--debug" ] || [ "$1" = "--debug" ]; then
DEBUG=true
if [ "$1" = "--debug" ]; then
LOCK_FILE_OR_DIR="$2"
fi
fi
TEMP_DIR=$(mktemp -d)
TENABLE_LIST="$TEMP_DIR/tenable.json"
DATADOG_LIST="$TEMP_DIR/datadog.csv"
# Couleurs pour l'affichage
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
ORANGE='\033[0;33m'
NC='\033[0m' # No Color
# Fonction de nettoyage
cleanup() {
rm -rf "$TEMP_DIR"
}
trap cleanup EXIT
# Fonction d'aide
usage() {
echo "Usage: $0 <lock-file-or-directory> [--debug]"
echo ""
echo "Vérifie si des paquets vulnérables (Shai-Hulud) sont présents dans votre fichier lock"
echo ""
echo "Fichiers supportés:"
echo " - yarn.lock"
echo " - package-lock.json"
echo " - pnpm-lock.yaml"
echo " - bun.lock"
echo ""
echo "Exemple:"
echo " $0 yarn.lock"
echo " $0 /path/to/project"
echo " $0 package-lock.json --debug"
exit 1
}
# Vérification des arguments
if [ -z "$LOCK_FILE_OR_DIR" ]; then
echo -e "${RED}Erreur: Fichier lock ou répertoire manquant${NC}"
usage
fi
if [ ! -f "$LOCK_FILE_OR_DIR" ] && [ ! -d "$LOCK_FILE_OR_DIR" ]; then
echo -e "${RED}Erreur: Le fichier/répertoire '$LOCK_FILE_OR_DIR' n'existe pas${NC}"
exit 1
fi
# Vérification de la présence de curl
if ! command -v curl &> /dev/null; then
echo -e "${RED}Erreur: curl n'est pas installé${NC}"
exit 1
fi
echo -e "${YELLOW}📥 Téléchargement des listes de paquets vulnérables...${NC}"
DOWNLOAD_START=$(date +%s)
# Télécharger la liste Tenable
if ! curl -s "$TENABLE_URL" -o "$TENABLE_LIST" 2>/dev/null; then
echo -e "${RED}⚠️ Avertissement: Impossible de télécharger la liste Tenable${NC}"
TENABLE_LIST=""
fi
# Télécharger la liste Datadog
if ! curl -s "$DATADOG_URL" -o "$DATADOG_LIST" 2>/dev/null; then
echo -e "${RED}⚠️ Avertissement: Impossible de télécharger la liste Datadog${NC}"
DATADOG_LIST=""
fi
# Vérifier qu'au moins une source est disponible
if [ -z "$TENABLE_LIST" ] && [ -z "$DATADOG_LIST" ]; then
echo -e "${RED}Erreur: Aucune liste de paquets vulnérables disponible${NC}"
exit 1
fi
DOWNLOAD_END=$(date +%s)
DOWNLOAD_TIME=$((DOWNLOAD_END - DOWNLOAD_START))
# Afficher les sources disponibles
if [ -n "$TENABLE_LIST" ]; then
echo -e "${GREEN} ✓ Liste Tenable téléchargée${NC}"
fi
if [ -n "$DATADOG_LIST" ]; then
echo -e "${GREEN} ✓ Liste Datadog téléchargée${NC}"
fi
echo -e "${YELLOW} ⏱️ Téléchargement: ${DOWNLOAD_TIME}s${NC}"
# Parser les listes des paquets vulnérables (une seule fois)
echo ""
echo -e "${YELLOW}🔍 Analyse des listes de vulnérabilités...${NC}"
PARSE_START=$(date +%s)
PARSED_VULNERABLE="$TEMP_DIR/vulnerable_parsed.txt"
> "$PARSED_VULNERABLE" # Créer le fichier vide
# Parser Tenable (JSON)
if [ -n "$TENABLE_LIST" ] && [ -f "$TENABLE_LIST" ]; then
tenable_temp="$TEMP_DIR/tenable_parsed.txt"
# Extraire toutes les chaînes entre guillemets puis parser avec awk
grep -o '"[^"]*"' "$TENABLE_LIST" | awk '
# Si ça ressemble à un nom de paquet npm (commence par @ ou contient /)
/^"@/ || /\// {
gsub(/"/, "")
# Exclure les clés JSON comme "vuln_vers"
if ($0 !~ /vuln_vers/ && $0 !~ /^[0-9]/) {
package = $0
}
}
# Si ça ressemble à une version (commence par un chiffre)
/^"[0-9]/ {
gsub(/"/, "")
if (package != "") {
print package ":" $0
}
}
' > "$tenable_temp"
# Ajouter au fichier principal
cat "$tenable_temp" >> "$PARSED_VULNERABLE"
echo -e "${GREEN} ✓ Liste Tenable analysée ($(wc -l < "$tenable_temp" | xargs) entrées)${NC}"
fi
# Parser Datadog (CSV)
if [ -n "$DATADOG_LIST" ] && [ -f "$DATADOG_LIST" ]; then
datadog_temp="$TEMP_DIR/datadog_parsed.txt"
# Utiliser awk pour un parsing beaucoup plus rapide
# Format CSV: package_name,package_versions,sources
awk -F',' '
NR > 1 {
# Ignorer la ligne de header
package = $1
gsub(/^[[:space:]]+|[[:space:]]+$/, "", package) # trim
# Extraire les versions (colonne 2, peut contenir des virgules internes)
# On reconstruit tout sauf la dernière colonne (sources)
versions = $2
for (i = 3; i < NF; i++) {
versions = versions "," $i
}
# Nettoyer les guillemets
gsub(/"/, "", versions)
# Split par virgule et afficher chaque version
n = split(versions, vers, ",")
for (i = 1; i <= n; i++) {
version = vers[i]
gsub(/^[[:space:]]+|[[:space:]]+$/, "", version) # trim
if (version != "" && package != "") {
print package ":" version
}
}
}
' "$DATADOG_LIST" > "$datadog_temp"
# Ajouter au fichier principal
cat "$datadog_temp" >> "$PARSED_VULNERABLE"
echo -e "${GREEN} ✓ Liste Datadog analysée ($(wc -l < "$datadog_temp" | xargs) entrées)${NC}"
fi
# Supprimer les doublons
sort -u "$PARSED_VULNERABLE" -o "$PARSED_VULNERABLE"
# Créer une liste des paquets affectés (sans version) pour détecter les warnings
AFFECTED_PACKAGES="$TEMP_DIR/affected_packages.txt"
cut -d: -f1 "$PARSED_VULNERABLE" | sort -u > "$AFFECTED_PACKAGES"
PARSE_END=$(date +%s)
PARSE_TIME=$((PARSE_END - PARSE_START))
VULNERABLE_TOTAL=$(wc -l < "$PARSED_VULNERABLE" | xargs)
echo -e "${GREEN} ✓ $VULNERABLE_TOTAL entrées vulnérables uniques${NC}"
echo -e "${YELLOW} ⏱️ Analyse: ${PARSE_TIME}s${NC}"
analyse_file() {
local LOCK_FILE="$1"
local FILE_HAS_VULNERABILITIES=0
echo ""
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${YELLOW}📁 Analyse: $LOCK_FILE${NC}"
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
# Détection du type de fichier lock
LOCK_TYPE=""
if [[ "$LOCK_FILE" == *"yarn.lock"* ]]; then
LOCK_TYPE="yarn"
elif [[ "$LOCK_FILE" == *"package-lock.json"* ]]; then
LOCK_TYPE="npm"
elif [[ "$LOCK_FILE" == *"pnpm-lock.yaml"* ]]; then
LOCK_TYPE="pnpm"
elif [[ "$LOCK_FILE" == *"bun.lock"* ]] || [[ "$LOCK_FILE" == *"bun.lockb"* ]]; then
LOCK_TYPE="bun"
else
echo -e "${RED}⚠️ Type de fichier lock non reconnu (ignoré)${NC}"
return 0
fi
# OPTIMISATION: Pré-extraire toutes les versions installées dans un fichier temporaire
echo ""
echo -e "${YELLOW}📦 Extraction des paquets installés...${NC}"
EXTRACT_START=$(date +%s)
# Encoder le path pour éviter les problèmes de noms de fichiers (remplacer / par _)
LOCK_FILE_ENCODED=$(echo "$LOCK_FILE" | tr '/' '_')
INSTALLED_VERSIONS="$TEMP_DIR/installed_${LOCK_FILE_ENCODED}.txt"
case "$LOCK_TYPE" in
"yarn")
awk '
/^[" ]*[^# ].*@/ {
gsub(/^[" ]*/, "")
gsub(/[":].*$/, "")
package = $0
getline
if ($1 == "version") {
gsub(/[" ]/, "", $2)
split(package, parts, "@")
pkg_name = parts[1]
for (i = 2; i < length(parts); i++) {
pkg_name = pkg_name "@" parts[i]
}
print pkg_name ":" $2
}
}
' "$LOCK_FILE" > "$INSTALLED_VERSIONS"
;;
"npm")
grep -E '"(node_modules/|dependencies)' -A 3 "$LOCK_FILE" | \
awk '
/"node_modules\// || /"[^"]+": \{$/ {
if (/"node_modules\//) {
gsub(/.*node_modules\//, "")
gsub(/".*/, "")
package = $0
} else {
gsub(/[" :].*/, "")
package = $1
}
}
/"version":/ {
gsub(/[", ]/, "", $2)
if (package != "") print package ":" $2
}
' > "$INSTALLED_VERSIONS"
;;
"pnpm")
# Extraire de pnpm-lock.yaml (supporte v6 et v9)
awk '
# Format v9: package@version: (avec possibles quotes)
/^ .*@[0-9]/ {
line = $0
gsub(/^ /, "", line)
gsub(/['"'"']/, "", line) # Enlever les quotes
gsub(/:.*/, "", line)
# Trouver le dernier @ qui sépare la version
last_at = 0
for (i = length(line); i > 0; i--) {
if (substr(line, i, 1) == "@") {
last_at = i
break
}
}
if (last_at > 0) {
package = substr(line, 1, last_at - 1)
version = substr(line, last_at + 1)
# Si version commence par un chiffre, c'"'"'est bon
if (version ~ /^[0-9]/ && package != "") {
print package ":" version
}
}
}
# Format ancien v6: package: suivi de version:
/^ [^ ].*:$/ && !/^ .*@[0-9]/ {
gsub(/:.*/, "")
gsub(/^ /, "")
gsub(/['"'"']/, "", $0)
package = $0
}
/^ version:/ {
gsub(/.*version: /, "")
gsub(/['"'"']/, "", $0)
if (package != "" && $0 != "") {
print package ":" $0
package = ""
}
}
' "$LOCK_FILE" > "$INSTALLED_VERSIONS"
;;
"bun")
if [[ "$LOCK_FILE" == *".lockb"* ]]; then
echo -e "${RED}❌ bun.lockb est un format binaire non supporté directement${NC}"
echo " Solutions: bun install (génère bun.lock) ou bun install --yarn"
return 0
fi
# bun.lock v1 est au format JSON
# Format: "key": ["package@version", "path", {...}, "sha"]
sed -n '/^ "packages": {/,/^ }/p' "$LOCK_FILE" | \
grep -E '\["[^"]+@[0-9]' | \
sed 's/.*\["//' | \
sed 's/".*//' | \
awk -F'@' '
{
# Trouver le dernier @ qui sépare la version
version = $NF
if (version ~ /^[0-9]/) {
# Reconstruire le nom du paquet (tout sauf le dernier champ)
package = $1
for (i = 2; i < NF; i++) {
package = package "@" $i
}
if (package != "") {
print package ":" version
}
}
}
' | sort -u > "$INSTALLED_VERSIONS"
;;
esac
EXTRACT_END=$(date +%s)
EXTRACT_TIME=$((EXTRACT_END - EXTRACT_START))
INSTALLED_COUNT=$(wc -l < "$INSTALLED_VERSIONS" | xargs)
echo -e "${GREEN} ✓ $INSTALLED_COUNT paquets détectés${NC}"
echo -e "${YELLOW} ⏱️ Extraction: ${EXTRACT_TIME}s${NC}"
# Comparer avec les vulnérabilités
echo ""
echo -e "${YELLOW}🔍 Recherche de correspondances...${NC}"
COMPARE_START=$(date +%s)
VULNERABLE_COUNT=0
WARNING_COUNT=0
declare -a FOUND_VULNERABILITIES
declare -a FOUND_WARNINGS
# Vérifier les vulnérabilités exactes
while IFS=':' read -r package version; do
if grep -Fx "$package:$version" "$INSTALLED_VERSIONS" > /dev/null; then
echo -e " ${RED}⚠️ VULNÉRABILITÉ: $package@$version${NC}"
FOUND_VULNERABILITIES+=("$package@$version")
((VULNERABLE_COUNT++))
FILE_HAS_VULNERABILITIES=1
GLOBAL_VULNERABILITIES_FOUND=1
fi
done < "$PARSED_VULNERABLE"
COMPARE_END=$(date +%s)
COMPARE_TIME=$((COMPARE_END - COMPARE_START))
echo -e "${YELLOW} ⏱️ Comparaison: ${COMPARE_TIME}s${NC}"
# Vérifier les warnings (paquets affectés, versions différentes)
echo ""
echo -e "${YELLOW}⚠️ Vérification des paquets affectés (autres versions)...${NC}"
WARNING_START=$(date +%s)
while IFS=':' read -r package version; do
if grep -Fx "$package" "$AFFECTED_PACKAGES" > /dev/null; then
if ! grep -Fx "$package:$version" "$PARSED_VULNERABLE" > /dev/null; then
echo -e " ${ORANGE}ℹ️ ATTENTION: $package@$version (version différente)${NC}"
FOUND_WARNINGS+=("$package@$version")
((WARNING_COUNT++))
fi
fi
done < "$INSTALLED_VERSIONS"
WARNING_END=$(date +%s)
WARNING_TIME=$((WARNING_END - WARNING_START))
echo -e "${YELLOW} ⏱️ Warnings: ${WARNING_TIME}s${NC}"
echo ""
# Afficher le résumé pour ce fichier
if [ $VULNERABLE_COUNT -eq 0 ]; then
echo -e "${GREEN}✅ Aucune vulnérabilité détectée dans ce fichier${NC}"
if [ $WARNING_COUNT -gt 0 ]; then
echo -e "${ORANGE}⚠️ $WARNING_COUNT paquet(s) avec versions affectées connues${NC}"
fi
else
echo -e "${RED}❌ $VULNERABLE_COUNT vulnérabilité(s) détectée(s)${NC}"
for vuln in "${FOUND_VULNERABILITIES[@]}"; do
echo -e " ${RED}• $vuln${NC}"
done
if [ $WARNING_COUNT -gt 0 ]; then
echo ""
echo -e "${ORANGE}⚠️ $WARNING_COUNT autre(s) paquet(s) affecté(s)${NC}"
fi
fi
return $FILE_HAS_VULNERABILITIES
}
# Si un répertoire est fourni, analyser tous les fichiers lock à l'intérieur
if [ -d "$LOCK_FILE_OR_DIR" ]; then
echo ""
echo -e "${YELLOW}📂 Scan du répertoire: $LOCK_FILE_OR_DIR${NC}"
FILES_FOUND=0
for file in $(find "$LOCK_FILE_OR_DIR" -type f \( -name "yarn.lock" -o -name "package-lock.json" -o -name "pnpm-lock.yaml" -o -name "bun.lock" -o -name "bun.lockb" \) -not -path "*/node_modules/*"); do
((FILES_FOUND++))
analyse_file "$file"
done
if [ $FILES_FOUND -eq 0 ]; then
echo -e "${YELLOW}⚠️ Aucun fichier lock trouvé dans le répertoire${NC}"
exit 0
fi
else
analyse_file "$LOCK_FILE_OR_DIR"
fi
# Résumé final
END_TIME=$(date +%s)
TOTAL_TIME=$((END_TIME - START_TIME))
echo ""
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo -e "${YELLOW}📊 RÉSUMÉ GLOBAL${NC}"
echo -e "${YELLOW}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
echo ""
if [ $GLOBAL_VULNERABILITIES_FOUND -eq 1 ]; then
echo -e "${RED}🚨 DES VULNÉRABILITÉS ONT ÉTÉ DÉTECTÉES${NC}"
echo ""
# Collecter toutes les vulnérabilités trouvées (optimisé)
ALL_VULNS="$TEMP_DIR/all_vulnerabilities.txt"
> "$ALL_VULNS"
# Pour chaque fichier d'installation, croiser avec les vulnérabilités
for installed_file in "$TEMP_DIR"/installed_*.txt; do
if [ -f "$installed_file" ]; then
# Extraire et décoder le path original
# Format: installed_<encoded_path>.txt
original_path=$(basename "$installed_file" .txt | sed 's/^installed_//' | tr '_' '/')
# Utiliser grep -Fxf pour comparer les deux fichiers en une seule fois (BEAUCOUP plus rapide)
grep -Fxf "$PARSED_VULNERABLE" "$installed_file" 2>/dev/null | while IFS=':' read -r package version; do
echo "$package@$version|$original_path"
done >> "$ALL_VULNS"
fi
done
if [ -f "$ALL_VULNS" ] && [ -s "$ALL_VULNS" ]; then
# Grouper par paquet et afficher
echo -e "${RED}📋 Récapitulatif des paquets compromis:${NC}"
echo ""
# Trier et grouper par paquet
sort "$ALL_VULNS" | awk -F'|' '
{
vuln = $1
file = $2
if (vuln != last_vuln) {
if (last_vuln != "") {
# Afficher le paquet précédent
printf " \033[0;31m• %s\033[0m\n", last_vuln
printf " \033[1;33m→ %s\033[0m\n\n", files
}
last_vuln = vuln
files = file
} else {
# Même paquet, ajouter le fichier
files = files ", " file
}
}
END {
# Afficher le dernier
if (last_vuln != "") {
printf " \033[0;31m• %s\033[0m\n", last_vuln
printf " \033[1;33m→ %s\033[0m\n", files
}
}
'
fi
echo ""
echo -e "${RED}Actions urgentes:${NC}"
echo " 1. NE PAS mettre à jour vos dépendances"
echo " 2. Isolez l'environnement concerné"
echo " 3. Vérifiez vos logs pour toute activité suspecte"
echo " 4. Tournez vos secrets (npm, GitHub, cloud, etc.)"
echo " 5. Contactez votre équipe de sécurité"
else
echo -e "${GREEN}✅ Aucune vulnérabilité détectée${NC}"
echo ""
echo -e "${YELLOW}Recommandations:${NC}"
echo " • NE PAS mettre à jour vos dépendances pour le moment"
echo " • Surveillez les mises à jour des listes officielles"
fi
echo ""
echo -e "${YELLOW}📚 Ressources:${NC}"
echo " • Tenable: https://github.com/tenable/shai-hulud-second-coming-affected-packages"
echo " • Datadog: https://github.com/DataDog/indicators-of-compromise"
echo " • Blog: https://www.tenable.com/blog/faq-about-sha1-hulud-2-0"
echo ""
echo -e "⏱️ Temps total: ${TOTAL_TIME}s"
echo ""
# Code de sortie: 1 si des vulnérabilités ont été trouvées, 0 sinon
exit $GLOBAL_VULNERABILITIES_FOUND
@olaurendeau
Copy link
Author

Updated to fix warnings : some unaffected paquets were listed as affected

@olaurendeau
Copy link
Author

Updated to use Datadog vuln list in addition to the Tenable one https://github.com/DataDog/indicators-of-compromise/blob/main/shai-hulud-2.0/consolidated_iocs.csv

@olaurendeau
Copy link
Author

Compatible with bun.lock

@olaurendeau
Copy link
Author

Find lock files in a folder, excluding node_modules

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment