Pourquoi l’incident is-promise s’est-il produit et que pouvons-nous en apprendre ?
28 avril 2020
0 minutes de lectureLe 25 avril 2020, le développeur JavaScript et mainteneur Forbes Lindesay a publié la version 2.2.0 de la bibliothèque is-promise sur npm. Selon certaines informations, cette publication a provoqué des échecs dans des outils de build populaires utilisés par les développeurs pour créer de nouveaux projets, comme create-react-app de Facebook, firebase-tools de Google, angular-cli, et d’autres. Forbes a rapidement corrigé les problèmes liés à la version 2.2.0 et a finalement publié la version 2.2.2 environ trois heures plus tard.
Un nouvel incident left-pad ? Pas tout à fait !
Contrairement à ce que semble penser une bonne partie des personnes qui relaient cette histoire, il ne s’agit pas d’un problème de chaîne d’approvisionnement logicielle et ce cas n’est pas comparable à l’incident left-pad de 2016.
De plus, le problème vient d’une erreur involontaire qui aurait pu arriver à n’importe qui. Forbes a fait preuve de responsabilité en tant que mainteneur en publiant rapidement un correctif, quelques heures à peine après l’incident. Il a également rédigé un compte rendu de l’incident, que je vous recommande vivement de lire.
Dans cet article, je voudrais expliquer ce qui a conduit à l’incident is-promise et ce que nous pouvons en retenir, en tant que mainteneurs et utilisateurs.
Qui a été touché par l’incident is-promise ?
Le package npm is-promise est utilisé par environ 500 dépendances directes et totalise 12 000 000 de téléchargements par semaine. On peut donc dire qu’il est populaire. Toutefois, il n’a pas provoqué l’échec de milliers de builds pour des équipes : il a surtout touché les utilisateurs qui créaient de nouveaux projets à l’aide d’outils de développement.
500 dépendances directes, cela ne semble peut-être pas si impressionnant. Mais si vous connaissez la complexité de l’écosystème npm et de ses dépendances imbriquées, la statistique de GitHub — 3 500 000 millions de projets qui dépendent de is-promise — vous donnera une meilleure idée de sa popularité.
De plus, le problème n’aurait touché que les utilisateurs de Node.js 12.16 et versions ultérieures — une version LTS. Pourtant, la plupart des problèmes signalés sur GitHub que j’ai consultés (par exemple : [1], [2]) mentionnent des utilisateurs de Node.js 13 ou 14, des versions qui ne sont pas stables et qui ne sont pas recommandées, sauf si vous souhaitez utiliser les toutes dernières nouveautés.
Que s’est-il donc réellement passé ?
Le mainteneur souhaitait ajouter la prise en charge des modules ES (ESM) à la bibliothèque, tout en lui permettant d’utiliser à la fois le système de modules CommonJS (CJS) de Node.js, utilisé depuis longtemps, et le système de modules JavaScript standardisé, relativement récent.
Cela s’est produit avec la première version en cinq ans — la version 2.1.0 — dont vous trouverez le diff ci-dessous. (Consulter la comparaison originale d’is-promise sur GitHub) En résumé, les modifications comprenaient :
un export par défaut explicite
l’utilisation de la méthode relativement récente permettant de spécifier un système à double module dans
package.jsonà l’aide de la cléexports, ainsi que la définition de la clé ESMtypela mise à jour des définitions TypeScript

« Mais qu’est-ce qui a réellement mal tourné ici ? Le champ exports n’a pas été défini correctement, ce qui a entraîné l’exception suivante et incité les utilisateurs à signaler des problèmes sur GitHub :
Que retenir de cet incident ?
Je pense que les mainteneurs comme les utilisateurs peuvent tirer des leçons de cet incident, afin de mieux se préparer à l’avenir. D’ailleurs, Forbes a déjà mis en place des contrôles en tant que mainteneur pour éviter que ce type de problème ne se reproduise.
S’agit-il d’un problème de chaîne d’approvisionnement logicielle ?
Cette erreur de bonne foi aurait pu arriver à n’importe qui. Elle n’est pas due au fait qu’is-promise soit une bibliothèque qui tient sur une seule ligne, et elle ne relève pas en soi d’un problème de chaîne d’approvisionnement logicielle.
L’incident left-pad a révélé des failles dans la gestion par le registre npm de la propriété des packages, de leur cycle de vie, etc. Il en a résulté des politiques et des mesures destinées à éviter qu’un tel cas ne se reproduise. Qu’a fait le registre npm pour corriger le problème is-promise ? Rien, car il n’y avait rien à faire. Comme je l’ai déjà souligné, il ne s’agit pas d’un problème de chaîne d’approvisionnement logicielle.
Le versionnage sémantique compte
L’ajout ou le remplacement de la prise en charge de CommonJS par ESM, en utilisant les clés type et exports dans package.json, est documenté comme nécessitant un changement incompatible. Pourtant, is-promise@2.2.0 a été publiée comme une version mineure et a donc été considérée comme la dernière version par de nombreux packages qui en dépendent.
Notre deuxième leçon à retenir est donc de bien réfléchir au versionnage sémantique : son impact sur notre écosystème est considérable.
Tests de bout en bout des packages
Pour détecter ce problème avant la publication, on aurait notamment pu ajouter des tests de bout en bout du package, afin de le tester comme le ferait un utilisateur et de vérifier avec des tests de validation qu’il fonctionne comme prévu.
Forbes a ajouté ces tests de bout en bout dans la dernière version. J’ai adopté une approche un peu différente, mais similaire dans son principe, avec des tests de bout en bout pour un package appelé Pie My Vulns. Dans mes tests CircleCI, je lance Verdaccio en tant que registre de packages local, je publie le package, puis je vérifie qu’une commande npm install, ainsi que quelques API ou commandes CLI de validation, fonctionnent comme prévu.
Utilisez Node.js LTS
Vous devriez toujours utiliser Node.js LTS, qui correspondait à la version 12 au moment de la rédaction de cet article. Utiliser des versions de Node.js de pointe, comme les versions 13 et 14, vous aurait sans aucun doute exposé à l’échec lors du changement apporté à is-promise.
Gardez toutefois à l’esprit que, dans ce cas, ce n’est pas tout à fait vrai : le problème a touché les utilisateurs de Node.js 12.16 et versions ultérieures, car la prise en charge expérimentale d’ESM y était activée par défaut. Si vous utilisiez encore la version 12.15 ou une version antérieure, vous n’auriez pas été touché.
Voici une autre leçon à retenir : assurez-vous que tous vos tests passent sur les versions LTS de Node.js. La prise en charge de versions plus récentes, comme Node.js 14, aurait également permis de détecter le problème.
Vous mettez à niveau trop tôt ?
Si vous aviez reçu une pull request de mise à niveau automatique vers la nouvelle version is-promise@2.2.0 et que votre environnement correspondait au profil d’impact décrit ci-dessus, vous auriez également été exposé au problème.
À l’avenir, mieux vaut éviter de se précipiter pour effectuer les mises à niveau, afin de se prémunir contre ce genre de problème, mais aussi contre les packages malveillants. C’est pourquoi les mises à niveau automatiques de Snyk ne se précipitent pas : un délai de 21 jours est appliqué avant que de nouvelles versions soient proposées pour vos projets.
Savez-vous comment fonctionne npx ?
J’ai remarqué que de nombreux problèmes signalés faisaient référence à l’utilisation de npx. Je voudrais donc dissiper une partie de la confusion : les fichiers de verrouillage, notamment package-lock.json et yarn.lock, ne vous seront d’aucune aide ici, car ils ne sont généralement pas publiés avec le package. Et même s’ils l’étaient, ils ne sont pas utilisés lors de l’installation, contrairement à ce que fait npx pendant son exécution.
Vous pouvez toutefois utiliser le fichier de verrouillage npm shrinkwrap (npm-shrinkwrap.json) pour figer vos dépendances, comme je l’ai fait avec is-website-vulnerable. Vous aurez ainsi la garantie de toujours publier le même arbre de dépendances, celui que vous testez, quelles que soient les nouvelles versions publiées dans le registre.
Si vous souhaitez en savoir plus sur le fonctionnement des fichiers de verrouillage, j’ai également écrit un article sur la manière de comprendre les fichiers de verrouillage des packages dans l’écosystème npm.
En résumé
Pour conclure, ce genre de problème arrive. Nous devrions être reconnaissants envers la communauté pour sa mobilisation et envers Forbes Lindesay pour le travail accompli afin de résoudre rapidement les problèmes. Les développeurs et les mainteneurs peuvent également tirer de nombreuses leçons de cet incident, car nous sommes tous des citoyens du monde open source.
Vous souhaitez en savoir plus ? Je vous invite à lire également le compte rendu de l’incident de Forbes et les leçons qu’il en a tirées.
La version corrigée d’is-promise pour la branche 2.x est la version 2.2.2. Une nouvelle version majeure, la 4.0.0, est également disponible si vous souhaitez bénéficier de la prise en charge des modules ES ou de TypeScript.
Lancez-vous dans les compétitions Capture The Flag
Apprenez à résoudre des défis de capture du drapeau en regardant à la demande notre atelier virtuel d’initiation.