Aller au contenu
medium

npm ne lance plus les scripts d'installation : pnpm l'avait fait avant

Read this article in English

11 min de lecture
Un paquet compromis marqué d'une tête de mort parmi des paquets sains

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, install et postinstall des dépendances ;
  • les compilations node-gyp implicites, déclenchées par la simple présence d'un fichier binding.gyp sans script déclaré ;
  • les scripts prepare des 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 :

Fenêtre de terminal
npm approve-scripts

Pourquoi 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 :

VersionDate de publicationCe qu'elle apporte
pnpm 10.0.07 janvier 2025Scripts des dépendances désactivés par défaut
pnpm 11.0.028 avril 2026Délai d'attente de 24 h actif par défaut
npm 12.0.08 juillet 2026Scripts 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èmeExécution à l'installationNature de la situation
npmBloquée depuis juillet 2026Correction récente
pnpm / BunBloquée depuis janvier 2025Correction précoce
Composer (PHP)Jamais exécutée pour les dépendancesChoix de conception
GoN'a jamais existéChoix de conception
NuGetSupprimée en 2017 avec PackageReferenceCorrection ancienne
Maven CentralAucune, les JAR sont passifsChoix de conception
RubyGemsToujours ouverte (extensions natives)Retard réel
PyPIPossible via une archive sourceNeutralisé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

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn