Le 11 mai, entre 19h20 et 19h26 UTC, 84 versions malveillantes de 42 paquets @tanstack/* ont été publiées sur npm, parmi lesquels @tanstack/react-router, @tanstack/router-core et @tanstack/react-start. Aucun jeton npm n’a été volé : l’attaquant a fait publier ses paquets par le pipeline officiel du projet. Le code malveillant s’exécute à l’installation et vole les secrets de la machine. L’avis de sécurité GHSA-g7cv-rxg3-hmpx (CVE-2026-45321) est noté 9,6 sur 10.
Comment l’attaque a fonctionné
Le post-mortem publié par TanStack décrit une chaîne de trois faiblesses, dont aucune n’aurait suffi seule :
- Un workflow
pull_request_target: le workflow de mesure de taille des bundles se déclenchait sur les pull requests de forks, récupérait le code du fork et l’exécutait avec les privilèges du dépôt principal (attaque dite « Pwn Request »). - Un empoisonnement du cache : le code du fork a écrit un store pnpm piégé sous la clé de cache que le workflow de release allait ensuite restaurer. TanStack note que la sauvegarde du cache en fin de job par
actions/cachen’est pas contrôlée par le blocpermissions:. - Le vol du jeton OIDC : lors de la release suivante, les binaires de l’attaquant, restaurés depuis le cache, ont lu la mémoire du processus du runner pour en extraire le jeton OIDC de publication, puis ont publié directement sur le registre npm.
Résultat : des paquets malveillants publiés via la liaison « trusted publisher » légitime, avec une provenance en apparence valide.
Chronologie
| Date (UTC) | Événement |
|---|---|
| 10 mai, 17h16 | Création d’un fork de TanStack/router, renommé pour échapper aux recherches |
| 11 mai, ~10h49 | Ouverture de la PR #7378 ; les workflows pull_request_target s’exécutent |
| 11 mai, 11h29 | Le cache pnpm empoisonné est sauvegardé ; la PR est ensuite fermée |
| 11 mai, 19h20-19h26 | Deux vagues de publications malveillantes |
| 11 mai, 19h46 | Un chercheur de StepSecurity ouvre un ticket de suivi |
| 11 mai, 20h19-21h03 | Dépréciation des 84 versions |
| 11 mai, 22h13-23h55 | npm retire les archives du registre |
| 12 mai, 05h02 | Rapport d’indicateurs de compromission transmis à npm et GitHub |
Ce que fait le malware
- Il s’exécute via le script
prepared’une dépendance optionnelle@tanstack/setuppointant vers un commit GitHub piégé, qui lance un fichier obfusquérouter_init.jsd’environ 2,3 Mo. - Il récolte les identifiants AWS (IMDS, Secrets Manager), GCP, les jetons de compte de service Kubernetes, les jetons Vault, le fichier
~/.npmrc, les jetons GitHub et les clés SSH. - Il exfiltre via le réseau de messagerie Session/Oxen (
filev2.getsession.org,seed1àseed3.getsession.org), sans serveur de commande dédié. - Il se propage en republiant, avec la même injection, les autres paquets maintenus par la victime.
Vérifier si vous êtes touché
Les deux indicateurs les plus simples sont la dépendance optionnelle @tanstack/setup dans les manifestes et la présence de router_init.js à la racine d’un paquet @tanstack. Depuis la racine de votre projet :
grep -n "@tanstack/setup" package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null
find node_modules -name router_init.js -path "*@tanstack*"
Pensez aussi à vos runners CI : tout pipeline ayant installé un paquet @tanstack/* entre 19h20 et 19h30 UTC le 11 mai doit être considéré comme compromis.
Que faire si vous avez installé une version piégée
- Considérez la machine ou le runner comme compromis.
- Faites tourner tous les secrets accessibles depuis cet hôte : AWS, GCP, Kubernetes, Vault, GitHub, npm et SSH.
- Consultez les journaux d’audit cloud pour toute activité suspecte depuis l’installation.
- Mettez à jour vers les versions corrigées (par exemple
@tanstack/react-router1.169.9,@tanstack/react-start1.167.72) et réinstallez depuis un lockfile propre. - Bloquez les domaines
getsession.orglistés ci-dessus au niveau réseau.
En défense en profondeur, l’avis recommande de désactiver temporairement les scripts d’installation avec npm config set ignore-scripts true. Côté mainteneurs, la leçon est claire : ne jamais exécuter le code d’un fork dans un workflow pull_request_target, épingler les actions tierces par SHA et ne pas partager de cache entre workflows de confiance différente.
TanStack indique avoir restructuré le workflow fautif, épinglé ses actions par SHA, purgé tous les caches de ses dépôts et retiré par précaution certains droits d’écriture.