
Scanner pour l'attaque de la chaîne d'approvisionnement keyv/cacheable : détecte les paquets npm compromis, vérifie les hashs des payloads et recherche les implants de persistance en modes repo et hôte.
English | Português
Scanner autonome pour l'incident de chaîne d'approvisionnement keyv/cacheable (« Shai-Hulud: Here We Go Again », 4 août 2026) — plus de 440 paquets npm compromis par un ver auto-propagateur qui vole des identifiants cloud/CI et installe une persistance avec un interrupteur homme mort.
Détecte, en quelques minutes et sans rien installer :
package-lock.json, npm-shrinkwrap.json, yarn.lock (v1 et Berry), pnpm-lock.yaml et bun.lock — y compris les dépendances transitives, avec la chaîne complète (p. ex. eslint → file-entry-cache → flat-cache → [email protected]) ;node_modules (nom + empreinte SHA-256 des artefacts connus) ;SUSPECT, jamais confirmées sans empreinte ;.claude/settings.json et .vscode/tasks.json, artefacts temporaires (bun-dl-*) ;keyv/cacheable a été compromis ; l'attaquant a publié de nouvelles versions avec un hook "preinstall": "node setup.mjs" — du code qui s'exécute avant l'installation du paquet, avec les privilèges de celui qui a lancé npm install.setup.mjs télécharge le runtime Bun depuis GitHub et y exécute la charge utile — une évasion face aux outils qui ne surveillent que les processus node.Math_Symbol.js (~728 Ko, obfusqué) vole des identifiants : métadonnées d'instance AWS, clés AWS/GCP/Azure, jetons Vault, comptes de service Kubernetes, secrets GitHub Actions, jetons npm, plus un balayage regex générique à la recherche de clés privées et de jetons bearer sur le disque.npm install/npm ci avec les scripts de cycle de vie activés depuis 2026-08-04 09:35 UTC. Avec --ignore-scripts, le hook ne s'exécutait pas..claude/settings.json (SessionStart) et .vscode/tasks.json (folderOpen) qui exécutent le chargeur à l'ouverture du dossier cloné — pas de npm install, rien d'installé. Cela inclut les personnes ayant cloné le dépôt pour enquêter sur l'incident et les agents de codage IA ayant ouvert le répertoire — l'un des premiers cas publics de hooks d'agents IA (.claude/) utilisés comme vecteur de chaîne d'approvisionnement.L'implant installe un « watcher » (gh-token-monitor) maintenu en vie par un LaunchAgent (macOS) ou un service utilisateur systemd + loginctl enable-linger (Linux). Toutes les 60 secondes, il valide le jeton GitHub volé auprès de l'API. Tant que le jeton fonctionne, rien ne se passe. Lorsque la réponse passe en 4xx — c'est-à-dire au moment où vous révoquez le jeton — il exécute, via eval, le contenu de ~/.config/gh-token-monitor/handler : une commande arbitraire définie à distance par l'attaquant. Les analyses publiques ne savent pas ce qu'elle contient — cela pourrait être une destruction de données, une ré-implantation, un rançongiciel, ou rien. Le risque n'est pas évaluable ; c'est pourquoi l'ordre de réponse est absolu.
Trois propriétés qui changent la réponse :
host.[email protected] a été publié avec une attestation SLSA valide. La provenance atteste l'intégrité du build, pas celle de la source : le workflow légitime a compilé un code déjà trojanisé.package.json (le hook preinstall) et dans deux nouveaux fichiers ajoutés au paquet (setup.mjs, Math_Symbol.js).eslint → file-entry-cache → flat-cache → keyv. C'est pourquoi le scanner affiche la chaîne dans chaque résultat.scan.mjs possède les propriétés suivantes — importantes pour quiconque répond à un incident de chaîne d'approvisionnement :
npm install. Auditez l'intégralité de scan.mjs en 15 minutes avant de l'exécuter.--update (téléchargement d'un nouveau manifeste d'IOC), explicite et facultative.docker run --network=none ou une machine isolée : il suffit de copier scan.mjs + iocs.json.Prérequis : Node.js ≥ 18 (toute machine avec npm l'a déjà). Téléchargez les deux fichiers — scan.mjs + iocs.json — et c'est tout : aucune installation.
Attention : si vous avez cloné ce dépôt complet, le dossier
fixtures/contient des IOC inertes utilisés dans les tests (vrais noms et versions, contenu factice — aucun malware). Le scanner l'ignore automatiquement et vous en avertit dans la sortie ; ses résultats n'apparaissent que si vous le scannez exprès.
Il existe deux modes d'exécution qui répondent à des questions différentes, et c'est ce qui détermine où l'exécuter :
repo lit les lockfiles et node_modules — et les lockfiles vivent dans git, il peut donc être centralisé : une seule personne scanne tous les dépôts de l'entreprise.host recherche l'implant (watcher, LaunchAgent/systemd, hooks IDE), qui vit sur la machine où le code s'est exécuté — cela ne se trouve pas dans git et ne peut pas être centralisé.node scan.mjs repo /folder/with/all/the/repos --json=result.json --html=report.html
Répond en quelques minutes à la question « quels projets sont exposés », sans impliquer personne. Accepte plusieurs chemins ; parcourt les sous-répertoires (monorepos et workspaces inclus).