Quand npm install devient la brèche: ChainDrop sans panique
Un guide calme sur la vague Shai-Hulud dans npm: ce qui s’est passé, les limites de la provenance et la vérification des secrets CI sans confondre téléchargements et victimes.
Une installation de dépendances devrait être un geste banal. Dans la nouvelle vague ChainDrop autour de Shai-Hulud, c’est précisément ce geste qui est devenu dangereux : un runner ou un poste de développeur résolvait un paquet npm compromis, ce paquet exécutait un script de cycle de vie pendant l’installation, et l’environnement où se trouvent souvent les jetons de publication et les accès cloud devenait une surface d’attaque.

Ce qui s’est passé du 4 au 9 août
Aikido, Socket, SafeDep, JFrog, Unit 42, Wiz, Endor Labs et d’autres équipes ont décrit une campagne de supply chain npm liée aux familles Keyv et Cacheable. Selon Aikido, le premier foyer visible est parti d’un chemin de publication compromis pour [email protected] et des paquets associés comme flat-cache, file-entry-cache, cacheable-request, cacheable, @cacheable/memory et cache-manager. The Hacker News a ensuite résumé la semaine comme une campagne à comportement de ver touchant des centaines d’artefacts.
Les chiffres varient parce que la situation bougeait pendant la rédaction des analyses. JFrog a parlé de plus de 400 paquets; SafeDep et Unit 42 ont publié d’autres comptes de paquets ou de versions; Endor Labs a insisté sur le volume de téléchargements de certaines dépendances. Ces chiffres ne doivent pas être additionnés ni pris pour un nombre de victimes. Ils mesurent des paquets, des versions ou des téléchargements, pas l’exécution effective du malware.
Pourquoi ce cas est plus inquiétant
Beaucoup d’incidents npm commencent par un paquet quasi inconnu ou une imitation de nom. ChainDrop est plus gênant parce que l’entrée inclut des dépendances reçues de manière transitive. Une équipe peut ne jamais choisir keyv directement et le recevoir via un framework, un cache, un linter, un outil de build ou une bibliothèque serveur. Le lockfile devient donc la preuve principale: la version dangereuse peut être plusieurs niveaux sous le paquet que l’on croit avoir installé.
La campagne a aussi touché des signaux de confiance légitimes. Les chercheurs indiquent que certaines versions malveillantes ont été publiées par des workflows qui semblaient normaux et pouvaient présenter une provenance valide. La provenance reste utile, mais elle répond à une question précise: quel workflow a produit le paquet, sous quelle identité, depuis quel contexte de dépôt. Elle ne prouve pas que le compte du mainteneur, le commit de release, les entrées du workflow ou les identifiants étaient sains.
Le mécanisme pendant l’installation
Les rapports décrivent une chaîne simple. Une version compromise ajoutait une commande de cycle de vie comme preinstall, lançant node setup.mjs. Le script chargeait ensuite du JavaScript obfusqué, avec des noms cités comme Math_Symbol.js ou math_init.js, et utilisait Bun comme runtime pratique dans certaines étapes. Bun n’était pas compromis; il servait d’outil au payload.
Une fois dans CI ou sur le poste de développement, le malware cherchait des secrets: jetons npm et GitHub, identifiants cloud, éléments Vault et Kubernetes, clés SSH, fichiers .env, état Terraform, configurations Docker et Helm, ainsi que des secrets temporaires de runners GitHub Actions. Le danger d’un runner est que des identifiants courts ou dérivés d’OIDC peuvent apparaître en mémoire ou dans l’environnement au moment même où l’installation s’exécute.
La propagation venait de l’accès à la publication. Avec des jetons npm, le payload pouvait publier des versions infectées d’autres paquets maintenus par la victime. Avec un accès GitHub, les analyses décrivent des modifications de dépôts et des surfaces de persistance, notamment des fichiers liés à VS Code tasks ou Claude Code settings. Tout ne s’exécute pas automatiquement, mais ces changements montrent la volonté d’utiliser l’automatisation de développement comme relais.
Vérifier l’exposition sans panique
Commencez par les preuves. Cherchez les noms et versions exacts listés par Aikido, SafeDep, Socket, JFrog, Unit 42 et d’autres dans package-lock.json, npm-shrinkwrap.json, pnpm-lock.yaml, yarn.lock, les lockfiles Bun, les miroirs internes et les outils de scan. Ne vous limitez pas aux dépendances directes: le transitif est le cœur du problème.
Ensuite, vérifiez si une installation a réellement tourné pendant la fenêtre concernée. Les logs CI, dates de build, caches de package manager, couches de conteneur et historiques de shell distinguent une dépendance théorique d’un code exécuté. Si une version affectée n’a jamais été installée, la réponse peut rester limitée. Si elle a tourné dans un environnement avec secrets, traitez cet environnement comme exposé.
Ne terminez pas par une simple mise à jour. Mettre à jour empêche la prochaine installation de la mauvaise version; cela ne récupère pas un jeton copié. Isolez runner ou poste, conservez les logs utiles, reconstruisez les runners éphémères, vérifiez caches et outils de développement, puis révoquez npm, GitHub, cloud, Vault, Kubernetes, SSH et secrets de déploiement présents dans l’environnement.
La mesure durable consiste à considérer les scripts d’installation comme du code privilégié. Utilisez --ignore-scripts quand c’est possible, approuvez explicitement les scripts nécessaires, testez les contrôles de npm 12 et leurs équivalents dans pnpm, Yarn et Bun, imposez un âge minimal de release pour l’automatisation, gardez des runners éphémères et séparez installation, tests et publication. ChainDrop ne signe pas l’échec de l’open source; il rappelle que npm install exécute du code à proximité des clés.
Comments
Sign in to comment.
No comments yet.