
Vérificateur d'IOC pour l'attaque sur la chaîne d'approvisionnement npm TanStack/Mini Shai-Hulud (CVE-2026-45321)
curl -fsSL https://raw.githubusercontent.com/nkopylov/tanscript-exploit-check/main/check-tanstack-exploit.sh | bash
Ou clonez et exécutez localement :
git clone https://github.com/nkopylov/tanscript-exploit-check.git
cd tanscript-exploit-check
./check-tanstack-exploit.sh [project_dir ...]
Ce vérificateur couvre deux vagues de la campagne d'attaque de la chaîne d'approvisionnement Mini Shai-Hulud menée par TeamPCP :
Le 11 mai 2026 (19:20-19:26 UTC), un attaquant a publié 84 versions malveillantes sur 42 paquets npm @tanstack/* en une fenêtre de 6 minutes. L'attaque a également touché des paquets de Mistral AI, UiPath, OpenSearch et d'autres — plus de 170 paquets au total sur npm et PyPI.
Aucun identifiant de mainteneur n'a été volé. L'attaquant a exploité la chaîne de confiance CI/CD elle-même via une attaque en 3 étapes :
pull_request_target — Un fork GitHub jetable a ouvert une PR qui exécutait du code attaquant dans le contexte de sécurité du dépôt de baseLe 19 mai 2026 (01:39-02:06 UTC), le compte npm compromis atool ([email protected]) a publié 637 versions malveillantes sur 314 paquets en deux vagues automatisées. Les cibles à fort impact incluent :
size-sensor (4,2 M de téléchargements/mois)echarts-for-react (3,8 M de téléchargements/mois)@antv/scale (2,2 M de téléchargements/mois)timeago.js (1,15 M de téléchargements/mois)@antv/* et autresCette vague utilisait une charge utile basée sur Bun (498 Ko index.js) déclenchée via "preinstall": "bun run index.js", avec une charge utile de deuxième niveau cachée dans des commits imposteurs poussés vers le dépôt GitHub antvis/G2 via l'exploit de partage d'objet fork.
Les deux vagues utilisent la même famille d'outils « Mini Shai-Hulud » :
gh-token-monitor (Vague 1) et kitty-monitor (Vague 2) via LaunchAgent/systemd ; hooks dans .claude/settings.json et .vscode/tasks.jsontoJSON(secrets), échange de jetons OIDC npmrm -rf ~/ si le jeton GitHub est révoqué alors que le démon est actiffiredalazer, commandes signées RSA-PSS| # | Vérification | Description |
|---|---|---|
| 1 | Interrupteur de sécurité | Démons de persistance : gh-token-monitor (Vague 1), kitty-monitor (Vague 2) |
| 2 | Processus malveillants | Noms de processus attaquants connus (les deux vagues) |
| 3 | Fichiers de charge utile | Fichiers malveillants connus par nom et hachage SHA-256 (4 hachages) |
| 4 | Hooks Claude Code | Hooks injectés dans .claude/settings.json + heuristique générique SessionStart |
| 5 | Tâches VS Code | Tâches injectées dans .vscode/tasks.json + heuristique générique runOn: folderOpen |
| 6 | GitHub Actions | toJSON(secrets) dans tout workflow + avertissement pull_request_target |
| 7 | Lockfiles npm | Versions de paquets compromises dans les lockfiles |
| 8 | optionalDependencies | @tanstack/setup et @antv/setup malveillants + 4 SHA de commits imposteurs |
| 9 | Connexions réseau | Connexions actives vers l'infrastructure C2 (5 domaines/IP) |
| 10 | Cache DNS | Résolution antérieure de domaines attaquants (4 domaines) |
| 11 | Branches Git | Modèle de nommage de branches attaquantes inspiré de Dune |
| 12 | Scripts de cycle de vie | Heuristique : bun run dans preinstall/postinstall (paquets installés) |
| 13 | C2 par dépôt mort | Mot‑clé firedalazer et marqueurs « Shai-Hulud » dans l'historique git |
| 14 | Dépendances GitHub | Heuristique : dépendances github: épinglées à un SHA de commit dans optionalDeps |
Seuls @tanstack/router* et @tanstack/start* ont été concernés. NON concernés : @tanstack/query*, @tanstack/table*, @tanstack/form*, @tanstack/virtual*, @tanstack/store.
| Paquet | Versions malveillantes | Première version sûre |
|---|---|---|
@tanstack/react-router | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/router-core | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/vue-router | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/solid-router | 1.169.5, 1.169.8 | 1.169.9 |
@tanstack/react-start | 1.167.68, 1.167.71 | 1.167.72 |
@tanstack/router-plugin | 1.167.38, 1.167.41 | 1.167.42 |
Voir l'avis complet pour les 42 paquets.
Tous les paquets publiés par l'utilisateur npm atool ([email protected]) ont reçu des versions malveillantes le 19 mai 2026. Les paquets à fort impact incluent :
| Paquet | Téléchargements mensuels |
|---|---|
size-sensor | 4,2 M |
echarts-for-react | 3,8 M |
@antv/scale | 2,2 M |
timeago.js | 1,15 M |
Plus 310+ paquets principalement dans le scope @antv/* (@antv/g2, @antv/g6, @antv/l7, @antv/s2, @antv/x6, @antv/f2, etc.), ai-figure, timeago-react, jest-canvas-mock, jest-date-mock, et d'autres.
Voir l'article SafeDep pour la liste complète.
CRITIQUE : Désactivez l'interrupteur de sécurité AVANT de révoquer un quelconque jeton. Le logiciel malveillant efface
$HOMEsi les jetons sont révoqués alors que le démon est actif.
gh-token-monitor ET kitty-monitor) et supprimez les services LaunchAgent/systemd.claude/router_runtime.js, .vscode/setup.mjs, ~/.local/share/kitty/cat.py, /var/tmp/.gh_update_state, etc.)node_modules et les lockfiles, réinstallez avec --ignore-scriptsapi.masscan.cloud, filev2.getsession.org, git-tanstack.com, t.m-kosche.com)Copiez-collez ces invites dans votre agent de codage IA (Claude Code, Cursor, Cline/OpenClaw, Windsurf, Hermes, etc.) pour durcir votre projet contre les attaques de la chaîne d'approvisionnement comme celle-ci.
Durcissez la configuration npm de ce projet contre les attaques de la chaîne d'approvisionnement. Effectuez les actions suivantes :
Épinglez les versions exactes : Supprimez tous les préfixes
^et~de chaque dépendance danspackage.jsonafin quenpm installne récupère jamais silencieusement une version nouvellement publiée.Désactivez les scripts postinstall par défaut : Ajoutez
ignore-scripts=truedans.npmrc. Ajoutez ensuite un script"preinstall"explicite danspackage.jsonqui n'exécute que les scripts de cycle de vie connus et sûrs dont ce projet a réellement besoin (le cas échéant).Imposez des installations basées uniquement sur le lockfile en CI : Assurez-vous que la CI utilise
npm ci(pasnpm install). S'il existe un fichier de configuration CI, vérifiez-le. Sinon, notez-le comme une étape manuelle.Ajoutez la vérification de provenance : Ajoutez
npm audit signaturescomme étape dans le pipeline CI et comme hook git pre-push.Créez un script de politique de mise à jour : Créez un
scripts/safe-update.shqui :
- Prend un nom de paquet en argument
- Vérifie quand la dernière version a été publiée (
npm view <pkg> time --json)- Refuse de mettre à jour si la version a moins de 3 jours
- Si elle a plus de 3 jours, exécute
npm install <pkg>@latest --save-exact- Affiche un résumé de ce qui a changé
Ne modifiez aucun code applicatif. Touchez uniquement les fichiers de configuration,
.npmrc, les scriptspackage.jsonet la configuration CI.
Auditez et durcissez la configuration de l'agent de codage IA de ce projet contre les attaques par injection de la chaîne d'approvisionnement (comme la campagne TanStack/Mini Shai-Hulud qui a injecté des hooks malveillants dans
.claude/settings.jsonet.vscode/tasks.json). Effectuez les actions suivantes :Claude Code (
.claude/) :
- Examinez
.claude/settings.jsonet.claude/settings.local.jsonpour toute entréehooksqui exécute des commandes shell. Signalez tout ce qui exécute des fichiers.js,.mjsou.sh— surtout s'ils proviennent de.claude/,.vscode/ounode_modules/.- Supprimez tous les hooks que vous ne pouvez pas rattacher à un usage légitime et créé par l'utilisateur.
- Ajoutez une règle
.gitignorepour empêcher que.claude/settings.local.jsonsoit commité (il doit rester local).VS Code (
.vscode/) :
- Examinez
.vscode/tasks.jsonet.vscode/launch.jsonpour les tâches qui exécutent des scripts ou binaires inattendus.- Supprimez toute entrée de tâche qui référence des fichiers comme
setup.mjs,router_runtime.jsou d'autres noms qui n'appartiennent pas à ce projet.- Vérifiez
.vscode/extensions.jsonpour des extensions que vous ne reconnaissez pas.Cursor (
.cursor/) :
- Examinez
.cursor/settings.jsonet tous les fichiers de règles pour des commandes ou hooks injectés.- Mêmes vérifications que VS Code ci-dessus — Cursor hérite de la configuration
.vscode/.Général :
- Vérifiez s'il y a des scripts
preinstall,postinstall,prepareouprestartdanspackage.jsonque vous n'avez pas écrits. Signalez ceux qui sont suspects.- Vérifiez
.github/workflows/pour tout workflow utilisantpull_request_target— signalez-le comme un risque de sécurité avec un commentaire expliquant pourquoi.- Assurez-vous que
.gitignoreexclut les fichiers de session de l'agent qui pourraient fuiter des identifiants (.claude/projects/,.cursor/logs/, etc.).Rapportez ce que vous avez trouvé et ce que vous avez modifié. Ne modifiez pas le code applicatif.
Auditez et durcissez la configuration GitHub Actions de ce dépôt contre les attaques CI/CD de la chaîne d'approvisionnement. Effectuez les actions suivantes :
Supprimez ou refactorez tout déclencheur
pull_request_target— ceux-ci exécutent le code du workflow dans le contexte du dépôt de base avec accès aux secrets, même lorsqu'ils sont déclenchés par un fork. Remplacez parpull_request+ un workflow séparé avec validation d'approbation si nécessaire.Épinglez toutes les actions tierces à des SHA de commit complets (pas des tags ou branches). Par exemple, remplacez
actions/checkout@v4paractions/checkout@<sha-complet>. Ajoutez un commentaire avec le tag pour la lisibilité.Appliquez le principe du moindre privilège à chaque workflow. Ajoutez des blocs
permissions:explicites. La plupart des workflows n'ont besoin que decontents: read. Les workflows de publication ont besoin deid-token: write— et rien d'autre.Restreignez la portée du cache : Si vous utilisez
actions/cache, assurez-vous que les clés de cache sont limitées à la branche pour éviter l'empoisonnement inter-branches. Ajoutezrestore-keysavec précaution — ne restaurez pas les caches de branches non fiables.Ajoutez StepSecurity Harden-Runner comme première étape de chaque job :
step-security/harden-runner@v2avecegress-policy: audit(oublocksi vous connaissez vos points de terminaison autorisés).Vérifiez la présence de secrets dans les journaux du workflow : Assurez-vous qu'aucune étape du workflow n'affiche
${{ secrets.* }}ou${{ toJSON(secrets) }}dans la sortie standard.Rapportez toutes les modifications. Ne modifiez pas le code applicatif.
MIT