Warum ist is-promise entstanden und was können wir daraus lernen?
28. April 2020
0 Min. LesezeitAm 25. April 2020 veröffentlichte der JavaScript-Entwickler und Maintainer Forbes Lindesay Version 2.2.0 der Bibliothek is-promise auf npm. Berichten zufolge führte diese Veröffentlichung zu Fehlern in beliebten Entwickler-Build-Tools, mit denen neue Projekte aufgesetzt werden, darunter Facebooks create-react-app, Googles firebase-tools, angular-cli, und weitere. Forbes behob die mit Version 2.2.0 verbundenen Probleme umgehend und veröffentlichte schließlich etwa drei Stunden später Version 2.2.2.
Schon wieder left-pad? Nicht ganz!
Entgegen der offenbar weitverbreiteten Meinung unter denjenigen, die diese Geschichte teilen, handelt es sich hier nicht um ein Software-Supply-Chain-Problem. Der Fall ist auch nicht mit dem left-pad-Vorfall von 2016 vergleichbar.
Außerdem geht das Problem auf einen harmlosen Fehler zurück, der jedem hätte passieren können. Forbes erwies sich als verantwortungsbewusster Maintainer und lieferte bereits wenige Stunden nach dem Vorfall eine schnelle Lösung. Zudem verfasste er einen Bericht zum Vorfall, den ich Ihnen sehr ans Herz lege.
In diesem Artikel möchte ich erläutern, wie es zu dem Vorfall mit is-promise kam und was wir als Maintainer und Nutzer daraus lernen können.
Wer ist vom is-promise-Vorfall betroffen?
Das npm-Paket is-promise wird von rund 500 direkten Abhängigkeiten verwendet und 12.000.000-mal pro Woche heruntergeladen. Es ist also durchaus ein beliebtes Paket. Allerdings brachte es nicht Tausende Builds von Teams zum Scheitern – betroffen waren vor allem Nutzer, die mithilfe von Entwickler-Tools neue Projekte erstellten.
500 direkte Abhängigkeiten mögen nicht besonders viel klingen. Wenn Sie jedoch wissen, wie komplex das npm-Ökosystem mit seinen verschachtelten Abhängigkeiten ist, erhalten Sie durch GitHubs Statistik von 3.500.000 Projekten, die von is-promise abhängen, eine bessere Vorstellung von seiner Beliebtheit.
Außerdem waren nur Nutzer von Node.js 12.16 und höher betroffen – einer LTS-Version. In den meisten der von mir auf GitHub gesehenen Meldungen (z. B. [1], [2]) werden jedoch Nutzer von Node.js 13 oder 14 genannt. Diese Versionen sind nicht stabil und werden nicht empfohlen, es sei denn, Sie möchten stets die allerneuesten Funktionen nutzen.
Was ist also tatsächlich passiert?
Der Maintainer wollte die Unterstützung für ES Modules (ESM) ergänzen und gleichzeitig sowohl das langjährige CommonJS-Modulsystem (CJS) von Node.js als auch das relativ neue, standardisierte JavaScript-Modulsystem nutzbar machen.
Das geschah mit der ersten Version seit fünf Jahren – 2.1.0 –, deren Änderungen unten zu sehen sind. (Sehen Sie sich den ursprünglichen Vergleich für is-promise auf GitHub an) Kurz gesagt, umfassten die Aktualisierungen:
einen expliziten Default-Export
die Nutzung der relativ neuen Möglichkeit, das Dual-Modul-System in
package.jsonmit dem Schlüsselexportsanzugeben, sowie die Definition des ESM-Schlüsselstypeeine Aktualisierung der TypeScript-Definitionen

Was ist hier also tatsächlich schiefgelaufen? Das Feld exports war nicht korrekt definiert. Dadurch wurde die folgende Ausnahme ausgelöst, die Nutzer dazu veranlasste, Probleme auf GitHub zu melden:
Was können wir aus diesem Vorfall lernen?
Ich bin überzeugt, dass sowohl Maintainer als auch Nutzer daraus Lehren ziehen können, die uns helfen, uns besser auf künftige Vorfälle dieser Art vorzubereiten. Forbes hat bereits Maßnahmen ergriffen, um als Maintainer solche Probleme künftig zu erkennen und zu verhindern.
Handelt es sich um ein Software-Supply-Chain-Problem?
Dieser unbeabsichtigte Fehler hätte jedem passieren können. Er liegt weder daran, dass is-promise eine Ein-Zeilen-Bibliothek ist, noch ist er an sich auf ein Software-Supply-Chain-Problem zurückzuführen.
Der left-pad-Vorfall deckte Schwächen beim Umgang der npm-Registry mit Paketinhaberschaft, Lebenszyklen und mehr auf. Daraufhin wurden Richtlinien und Maßnahmen eingeführt, um einen solchen Fall künftig zu verhindern. Was hat die npm-Registry unternommen, um das Problem mit is-promise zu beheben? Nichts – denn das war nicht nötig. Wie bereits betont: Hier handelt es sich nicht um ein Software-Supply-Chain-Problem.
Semantische Versionierung ist wichtig
Die Unterstützung für CommonJS durch ESM zu ergänzen oder zu ersetzen, indem die Schlüssel type und exports in package.json verwendet werden, erfordert laut Dokumentation eine inkompatible Änderung. is-promise@2.2.0 wurde jedoch als Minor-Änderung veröffentlicht und dadurch für viele davon abhängige Pakete als neueste Version übernommen.
Unsere zweite Erkenntnis lautet daher: Nehmen Sie die semantische Versionierung ernst – sie hat erhebliche Auswirkungen auf unser Ökosystem.
End-to-End-Tests für Pakete
Eine Möglichkeit, dieses Problem vor der Veröffentlichung zu entdecken, wäre gewesen, End-to-End-Tests für das Paket hinzuzufügen. Damit lässt sich das Paket aus Sicht der Nutzer testen und mit Plausibilitätstests sicherstellen, dass es wie erwartet funktioniert.
Forbes ergänzte diese End-to-End-Tests in der neuesten Version. Für ein Paket namens Pie My Vulns habe ich einen etwas anderen, aber konzeptionell ähnlichen Ansatz für End-to-End-Tests gewählt. In meinen CircleCI-Tests starte ich Verdaccio als lokale Paket-Registry, veröffentliche das Paket und prüfe, ob eine npm install, sowie einige Plausibilitätsprüfungen über APIs oder CLI-Befehle wie erwartet funktionieren.
Verwenden Sie Node.js LTS
Sie sollten immer Node.js LTS verwenden. Zum Zeitpunkt der Erstellung dieses Artikels ist das Version 12. Die Verwendung brandaktueller Node.js-Versionen wie 13 und 14 hätte Sie mit der Änderung an is-promise zweifellos in Schwierigkeiten gebracht.
Beachten Sie jedoch, dass das in diesem Fall nicht ganz zutrifft: Betroffen waren auch Nutzer von Node.js 12.16 und höher, da diese Version mit experimenteller ESM-Unterstützung ohne Aktivierungsschalter ausgeliefert wurde. Wenn Sie noch Version 12.15 oder älter verwendeten, waren Sie von diesem Problem nicht betroffen.
Daraus ergibt sich eine weitere wichtige Erkenntnis: Stellen Sie sicher, dass alle Ihre Tests mit den LTS-Versionen von Node.js erfolgreich durchlaufen. Hier hätte es geholfen, auch spätere Versionen wie Node.js 14 zu unterstützen, um das Problem zu erkennen.
Aktualisieren Sie zu früh?
Wenn Sie eine Pull-Anfrage für ein automatisches Upgrade auf die neue Version is-promise@2.2.0 erhalten hatten und Ihr Setup dem oben beschriebenen Profil entsprach, waren auch Sie für dieses Problem anfällig.
Halten Sie sich künftig mit übereilten Upgrades zurück, um solche Probleme und auch schädliche Pakete zu vermeiden. Deshalb drängt Snyk Auto Upgrades nicht auf schnelle Versionswechsel, sondern wartet 21 Tage, bevor neue Versionen für Ihre Projekte vorgeschlagen werden.
Wissen Sie, wie npx funktioniert?
Mir ist aufgefallen, dass sich viele der gemeldeten Probleme auf die Verwendung von npx bezogen. Deshalb möchte ich einige Missverständnisse ausräumen: Lockfiles, insbesondere package-lock.json oder yarn.lock, helfen hier nicht, da sie normalerweise nicht mit dem Paket veröffentlicht werden. Selbst wenn dies der Fall wäre, würden sie bei der Installation nicht verwendet – und genau das geschieht bei der Ausführung von npx.
Sie können jedoch die Shrinkwrap-Lockdatei von npm (npm-shrinkwrap.json) verwenden, um Ihre Abhängigkeiten festzuschreiben. So habe ich es bei is-website-vulnerable gemacht, damit stets derselbe Abhängigkeitsbaum ausgeliefert wird – der, den ich immer teste, unabhängig davon, ob neue Versionen in der Registry veröffentlicht werden.
Wenn Sie mehr über die Funktionsweise von Lockfiles erfahren möchten: Ich habe auch darüber geschrieben, wie sich Paket-Lockfiles im npm-Ökosystem verstehen lassen.
Zusammenfassung
Abschließend lässt sich sagen: Solche Probleme kommen vor. Wir sollten der Community dafür dankbar sein, dass sie einspringt, und Forbes Lindesay dafür, dass er die Probleme so schnell behoben hat. Auch für uns als Entwickler und Maintainer gibt es viel zu lernen – schließlich gehören wir alle zur Open-Source-Community.
Möchten Sie mehr erfahren? Lesen Sie auch Forbes’ Bericht zum Vorfall und die Erkenntnisse, die er daraus gewonnen hat.
Die korrigierte Version für is-promise ist 2.2.2 für den 2.x-Zweig. Wenn Sie ein Upgrade für die Unterstützung von ES Modules oder TypeScript erwägen, gibt es außerdem die neue Hauptversion 4.0.0.
Starten Sie mit Capture-the-Flag
Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Challenges lösen.