Pendant seize ans, npm install a exécuté du code arbitraire fourni par
n'importe laquelle de vos dépendances. Pas la dépendance que vous avez
choisie : n'importe laquelle des centaines qu'elle entraîne derrière elle. La
seule parade tenait dans un drapeau, --ignore-scripts, que je n'ai presque
jamais vu activé en production.
Depuis le 8 juillet 2026 et la sortie de npm v12, c'est terminé. Les scripts
preinstall, install et postinstall des dépendances ne s'exécutent plus,
sauf autorisation explicite. C'est la fin du vecteur le plus rentable de
l'écosystème JavaScript, celui qu'ont emprunté Shai-Hulud, axios et les 57
paquets Mastra.
J'ai voulu vérifier si les autres écosystèmes suivaient le même chemin. La réponse m'a surpris : npm n'a pas ouvert la voie, il a rattrapé son concurrent. pnpm avait pris cette décision dix-huit mois plus tôt.
Ce que npm v12 bloque exactement
npm v12 refuse d'exécuter les scripts d'installation qui proviennent de vos
dépendances, mais continue d'exécuter les vôtres. La nuance compte : votre
propre postinstall de build fonctionne toujours, c'est le code des paquets
tiers qui est neutralisé.
Le blocage est plus large que ce que le titre laisse croire. Il couvre trois choses :
- les scripts
preinstall,installetpostinstalldes dépendances ; - les compilations
node-gypimplicites, déclenchées par la simple présence d'un fichierbinding.gypsans script déclaré ; - les scripts
preparedes dépendances installées depuis Git ou un lien local.
Au passage, npm v12 bloque aussi par défaut les dépendances Git et les
dépendances par URL distante. Le motif documenté pour les dépendances Git
vaut le détour : un paquet malveillant pouvait livrer son propre .npmrc
pointant vers un faux exécutable git.
Pour rétablir un script légitime, npm fournit une commande dédiée qui construit une liste d'autorisation à committer dans le dépôt :
npm approve-scriptsPourquoi les scripts d'installation étaient le vecteur préféré des attaquants
Un script postinstall s'exécute avec les droits du développeur ou du runner
de CI, sans validation, sans revue, et souvent sans que personne ne sache qu'il
existe. C'est ce qui en fait une arme aussi efficace : la victime n'a rien
fait d'autre qu'installer ses dépendances.
Le catalogue d'incidents du site est éloquent sur ce point. Le ver Shai-Hulud a compromis des dizaines de paquets AntV en volant les secrets des runners GitHub Actions. axios a livré un cheval de Troie d'accès distant. Le 17 juin 2026, ce sont 57 paquets Mastra et plus de 286 versions malveillantes qui sont passés par là.
Le guide
Attaques via les gestionnaires de paquets en CI/CD
détaille les cinq techniques d'exécution de code via les fichiers de
configuration. La recommandation qu'il porte depuis toujours,
--ignore-scripts, vient de changer de statut : ce n'est plus un conseil,
c'est le comportement par défaut.
pnpm avait dix-huit mois d'avance
Voici le fait qui remet l'annonce à sa place. J'ai vérifié les dates de publication directement dans le registre npm, qui les horodate :
| Version | Date de publication | Ce qu'elle apporte |
|---|---|---|
| pnpm 10.0.0 | 7 janvier 2025 | Scripts des dépendances désactivés par défaut |
| pnpm 11.0.0 | 28 avril 2026 | Délai d'attente de 24 h actif par défaut |
| npm 12.0.0 | 8 juillet 2026 | Scripts des dépendances désactivés par défaut |
pnpm a désactivé les scripts d'installation dix-huit mois avant npm, et il
est allé plus loin : depuis pnpm 11, aucune version publiée depuis moins de
24 heures n'est installée, sans rien configurer. Bun, de son côté, n'exécute
que les scripts des paquets déclarés dans trustedDependencies.
npm propose bien un délai d'attente équivalent, min-release-age, livré
en février 2026. Mais il est désactivé par défaut, ce qui, sur ce genre
de protection, revient à ne pas exister pour l'immense majorité des projets.
PyPI donne le tempo depuis 2023
Le vrai métronome du durcissement des registres, c'est PyPI, et c'est rarement dit. L'ordre des dates est sans ambiguïté :
- 2023 : PyPI déploie le trusted publishing par OIDC, le premier grand registre à le faire. crates.io suivra en juillet 2025, npm fin juillet 2025, NuGet en septembre 2025.
- 1er janvier 2024 : l'authentification à deux facteurs devient obligatoire pour tous les comptes qui publient.
- Novembre 2024 : les attestations numériques signées avec Sigstore arrivent.
- 22 juillet 2026 : une release refuse tout nouveau fichier passé quatorze jours, pour empêcher l'empoisonnement rétroactif d'anciennes versions.
npm a fait en un an ce que PyPI a construit en trois, mais sous la contrainte : c'est le seul registre qui a révoqué en masse des identifiants existants, en supprimant tous les jetons classiques le 9 décembre 2025.
Les autres écosystèmes n'ont pas le même problème à résoudre
C'est le piège de ce genre de comparaison, et je m'y suis fait prendre en préparant cet article : classer les écosystèmes par ordre de vertu n'a aucun sens quand ils n'ont pas la même architecture. Le modèle d'exécution compte davantage que la date de la dernière annonce.
| Écosystème | Exécution à l'installation | Nature de la situation |
|---|---|---|
| npm | Bloquée depuis juillet 2026 | Correction récente |
| pnpm / Bun | Bloquée depuis janvier 2025 | Correction précoce |
| Composer (PHP) | Jamais exécutée pour les dépendances | Choix de conception |
| Go | N'a jamais existé | Choix de conception |
| NuGet | Supprimée en 2017 avec PackageReference | Correction ancienne |
| Maven Central | Aucune, les JAR sont passifs | Choix de conception |
| RubyGems | Toujours ouverte (extensions natives) | Retard réel |
| PyPI | Possible via une archive source | Neutralisée en pratique par les wheels |
Composer est instructif : sa documentation précise que seuls les scripts définis dans le paquet racine sont exécutés, et les greffons exigent une liste d'autorisation. Le problème que npm vient de corriger n'a jamais existé en PHP.
Go non plus : les modules sont du code source, go get n'exécute rien,
il n'y a même pas de comptes de publication à hameçonner puisque tout vient
des dépôts. Présenter cela comme une avance obtenue en 2026 serait malhonnête,
c'est une décision de conception d'origine.
Ce que ces protections coûtent
Aucune de ces mesures n'est gratuite, et les passer sous silence donnerait une image fausse.
Les délais d'attente retardent aussi les correctifs. Attendre 24 heures ou sept jours avant d'installer une nouvelle version protège du paquet compromis publié il y a une heure, mais retarde d'autant le patch d'une faille critique. D'où les échappatoires prévues par chaque outil, qui deviennent à leur tour des points de faiblesse : il suffit qu'un paquet soit dans la liste d'exclusion pour que la protection tombe précisément là où elle comptait.
La fenêtre de quatorze jours de PyPI a un effet de bord : il n'est plus possible d'ajouter rétroactivement un paquet compilé pour une nouvelle version de Python à une ancienne release. Il faut republier. L'analyse d'impact publiée par PyPI chiffre la gêne à 56 projets sur les 15 000 les plus téléchargés, ce qui reste faible, mais pas nul.
Le trusted publishing concentre le risque. Il ne fonctionne aujourd'hui qu'avec les grandes plateformes d'intégration continue. Les mainteneurs qui publient depuis ailleurs restent sur des jetons, et la sécurité de l'écosystème se couple à celle de deux fournisseurs.
Ce que je fais concrètement
Sur mes projets, la bascule tient en trois gestes. Committer la liste d'autorisation des scripts en la construisant à partir des paquets qui en ont réellement besoin, pas en autorisant tout pour faire taire l'erreur. Activer le délai d'attente même sur npm où il est désactivé par défaut, avec une valeur basse plutôt que rien. Et garder un scanner en amont : OSV-Scanner détecte les dépendances malveillantes sans exécuter leur code, ce qui reste la seule vérification qui ne vous expose pas.
Le reste des bonnes pratiques n'a pas changé et vit dans le guide Sécuriser les dépendances npm, PyPI et Go.
À retenir
- npm v12, publié le 8 juillet 2026, n'exécute plus les scripts d'installation des dépendances. Première rupture de ce comportement en seize ans.
- Seuls les scripts des dépendances sont bloqués, les vôtres continuent de s'exécuter.
- Le blocage couvre aussi les compilations node-gyp implicites, ce qui cassera les projets à modules natifs en intégration continue.
- pnpm avait pris cette décision dix-huit mois plus tôt, en janvier 2025, et impose un délai d'attente de 24 heures par défaut depuis avril 2026.
- npm propose ce délai depuis février 2026, mais désactivé par défaut.
- PyPI mène la marche depuis 2023 : trusted publishing en premier, 2FA obligatoire dès janvier 2024, attestations Sigstore, releases figées à quatorze jours.
- Composer, Go, Maven et NuGet n'ont pas ce problème, par conception ou depuis longtemps. Ce n'est pas une avance récente, c'est une architecture différente.
- Les délais d'attente retardent aussi les correctifs de sécurité : la protection a un coût qu'il faut assumer consciemment.
Liens utiles
- Attaques via les gestionnaires de paquets en CI/CD : les cinq techniques d'exécution de code, avec labs reproductibles.
- Sécuriser les dépendances npm, PyPI et Go : typosquatting, dependency confusion, provenance.
- OSV-Scanner : détecter les dépendances vérolées : la détection sans exécution, du poste au pipeline.
- Shai-Hulud revient : un ver npm pille l'écosystème AntV : l'attaque qui a précipité le durcissement de npm.
- Incidents de la chaîne logicielle : le catalogue des cas réels, rattachés aux exigences qui les auraient évités.