npm Shrinkwrap neu aufgelegt: npm-Abhängigkeiten mit Package-Lock und Yarn.Lock fixieren
Assaf Hefetz
10. Januar 2018
0 Min. Lesezeit2016 machte das Abhängigkeitsmanagement weltweit Schlagzeilen: Ein unbekannter Entwickler entfernte ein winziges Node.js-Paket namens left-pad aus der Registry. Dadurch gingen Tausende von Projekten kaputt, die von diesem Paket abhingen, und einige der größten Websites der Welt fielen aus. npm, Inc. musste eingreifen und das Paket wiederherstellen, um dem Chaos ein Ende zu setzen.
Diese Geschichte zeigt, wie wichtig Abhängigkeiten sein können. Fehlen sie oder weichen sie von den Erwartungen ab, kann vieles kaputtgehen.
Schluss mit ShrinkWrap: Elegantes Fixieren von Abhängigkeiten in Node
Abhängigkeiten zu fixieren oder auf eine bestimmte Version festzulegen, ist in Ruby, Python und anderen Ökosystemen eine weitverbreitete Best Practice. Dabei wird die Version eines Pakets samt seiner Abhängigkeiten festgeschrieben. So wird bei der Bereitstellung eines Projekts stets dieselbe Version jeder Abhängigkeit installiert – für zuverlässige und vorhersehbare Installationen.
In Node.js war das Fixieren von Abhängigkeiten bis vor Kurzem deutlich weniger verbreitet. Die Lösung hieß npm Shrinkwrap, brachte jedoch einige Probleme mit sich. So kann die Datei shrinkwrap.json, die sensible Informationen zu Ihren Abhängigkeiten enthält, beim Veröffentlichen eines Pakets mitgeliefert werden. Außerdem macht Shrinkwrap es schwieriger, neue Abhängigkeiten hinzuzufügen. Hinzu kommt ein Sicherheitsrisiko: Shrinkwrap ist anfällig für Remote-Code-Ausführung, wenn keine HTTPS-URLs verwendet werden.
Die npm-Community begrüßte kürzlich eine neuere und elegantere Lösung, die in npm 5 integriert ist: package-lock.json. Wer Yarn verwendet, konnte bereits auf yarn.lock zurückgreifen, das die Fixierung von Abhängigkeiten in Node integriert. Wir sehen uns die beiden Lösungen gleich genauer an.
Doch zuerst: Sollten Sie Abhängigkeiten fixieren?
Vor- und Nachteile des Fixierens Ihrer Abhängigkeiten
Viele Fachleute und sogar der Leitfaden der US-Regierung für Softwarehersteller, „Before you ship“, empfehlen, alle Projektabhängigkeiten auf bestimmte Versionen festzulegen. Die Gründe:
Inkompatible Änderungen – Neue Versionen von Abhängigkeiten können Änderungen enthalten, die nicht von Ihrem Code unterstützt werden oder mit Ihrer konkreten Implementierung inkompatibel sind.
Fehler und Probleme – Eine neue Version kann Probleme mit sich bringen, die den Paketentwicklern entgangen sind. Da Sie nicht bei jeder Aktualisierung jede Abhängigkeit in Ihrem Code testen können, sollten Sie standardmäßig eine Version verwenden, die sich zuvor in Ihrer Umgebung bewährt hat.
Vorhersehbarkeit – Agile Entwicklung und Continuous Delivery beruhen darauf, Software automatisch und vorhersehbar bereitzustellen. Paketaktualisierungen, die zu inkonsistentem oder unvorhersehbarem Verhalten führen können, stehen diesem Ziel entgegen.
Das Fixieren von Paketen hat jedoch auch Nachteile:
Sicherheit – Snyk bietet ein Tool zum Scannen auf Sicherheitslücken in Open-Source-Software. Daher sind uns die Risiken von Abhängigkeiten bestens bekannt. Unsere Daten zeigen, dass in der Node.js-Community mindestens 76 % der Projekte Pakete mit bekannten Sicherheitslücken verwenden. Wenn Sie Ihre Abhängigkeiten fixieren, schreiben Sie auch alle darin enthaltenen Sicherheitslücken fest. Wird ein Sicherheitsproblem entdeckt und veröffentlicht der Paketautor eine Korrektur, verwenden Sie weiterhin die alte, anfällige Version.
Drittanbietermodule – Wenn Sie ein Drittanbietermodul entwickeln, von dem andere Projekte abhängen, zwingt das Fixieren Ihrer Abhängigkeiten Ihre Nutzer dazu, dieselben Versionen zu verwenden. Die Dokumentation zur Python-Paketierung bezeichnet das beispielsweise als übermäßig restriktiv und nicht als Best Practice. Vielen Dank an Dustin Ingram für diesen Hinweis.
Auch wenn das Sicherheitsrisiko erheblich ist, werden die meisten Entwickler kaum auf die Vorteile vorhersehbarer Installationen und Bereitstellungen verzichten wollen. Unsere Empfehlung: Fixieren Sie Abhängigkeiten, aber integrieren Sie das Scannen auf Sicherheitslücken in Ihren Build-Prozess.
Mit geeigneten Sicherheitstests und einer Überwachung erfahren Sie, wenn eine Abhängigkeit veraltet ist oder Sicherheitslücken entdeckt werden, und können sie dann aktualisieren. Im Idealfall aktualisieren Sie alle Open-Source-Komponenten Ihres Projekts sofort, sobald neue Versionen erscheinen – so sind Sie bestmöglich vor Fehlern und Sicherheitslücken geschützt. Zumindest bei Bekanntwerden einer Sicherheitslücke zu aktualisieren, ist eine akzeptable zweitbeste Lösung.
Abhängigkeiten mit package-lock fixieren
package-lock.json ist eine neue Funktion in npm 5. Sie beschreibt den genauen Abhängigkeitsbaum, der bei vorherigen Installationen erstellt wurde. So lässt sich bei späteren Installationen derselbe Abhängigkeitsbaum erzeugen – unabhängig von zwischenzeitlichen Aktualisierungen der Abhängigkeiten.
npm 5 erstellt die Datei package-lock automatisch. Sie sollte in die Versionsverwaltung aufgenommen werden. Das kann zwar lästig sein, bringt aber zwei wichtige Vorteile:
Wenn Sie package-lock committen, erhalten Sie eine zusätzliche Sicherheitsebene. Mit SHA-Integritätswerten können Sie überprüfen, ob tatsächlich genau das installiert wird, was Sie erwarten.
Wenn Sie package-lock committen, werden Installationen schneller, da npm die Metadaten bereits installierter Pakete nicht erneut auflösen muss.
Das Format von package-lock.json ist identisch mit dem von npm-shrinkwrap.json. Ist npm-shrinkwrap.json vorhanden, wird package-lock.json vollständig ignoriert.
Hier sind einige der Variablen in der Datei package-lock.json:
name – package-lock.json definiert eine Sperre für ein bestimmtes Paket. Hier legen Sie den Paketnamen fest.
version – Die Version, die Sie festschreiben möchten.
lockfileVersion – Die Version der Datei package-lock, beginnend mit 1.
packageIntegrity – Ein Integritätswert, der aus package.json erstellt wird. Er kann mit Modulen wie ssri erzeugt werden.
preserveSymlinks – Gibt an, ob die Installation mit der Umgebungsvariable NODE_PRESERVE_SYMLINKS durchgeführt wurde.
dependencies – Eine Zuordnung von Paketnamen zu Abhängigkeitsobjekten. Abhängigkeitsobjekte haben folgende Eigenschaften:
version – Ein eindeutiger Bezeichner für dieses Paket, mit dem eine neue Version davon abgerufen werden kann. Weitere Informationen finden Sie in der package-lock-Dokumentation unter Versionsangaben für verschiedene Paketquellen.
bundled – Gibt an, ob die Abhängigkeit gebündelt ist und, falls ja, ob sie aus dem übergeordneten Modul extrahiert werden soll, anstatt als separate Abhängigkeit installiert zu werden.
dev – Gibt an, ob es sich bei dieser Abhängigkeit um eine Entwicklungsabhängigkeit oder um eine transitive Abhängigkeit davon handelt.
Weitere Eigenschaften sind integrity, resolved, optional und dependencies (Unterabhängigkeiten dieser Abhängigkeit).
Weitere Informationen finden Sie in der Dokumentation zu package-lock.json. Lesen Sie außerdem den Beitrag von JiÅà PospÃÅ¡il mit Best Practices zum Fixieren von Dateien in npm 5.
Abhängigkeiten mit yarn.lock fixieren
Yarn verfügt wie Ruby über eine integrierte Funktion zum Fixieren von Abhängigkeiten. Damit Installationen auf verschiedenen Rechnern einheitlich ablaufen, benötigt Yarn genaue Informationen darüber, welche Versionen der einzelnen Abhängigkeiten installiert wurden.
Yarn erstellt automatisch die Datei yarn.lock im Stammverzeichnis Ihres Projekts. Wie die Datei package-lock von npm sollte auch yarn.lock in die Versionsverwaltung aufgenommen werden.
Die Datei sieht so aus (Beispiel aus der Dokumentation zu yarn.Lock).
Wenn Sie Abhängigkeiten hinzufügen, aktualisieren oder entfernen, aktualisiert Yarn die Datei yarn.lock automatisch. Bearbeiten Sie sie nicht direkt. Yarn berücksichtigt nur die yarn.lock-Datei auf der obersten Ebene und ignoriert yarn.lock-Dateien innerhalb von Abhängigkeiten. Die Datei auf der obersten Ebene enthält alles, was zum Fixieren der Versionen aller Pakete im Abhängigkeitsbaum erforderlich ist.
Weitere Informationen finden Sie in der Dokumentation zu yarn.lock oder in diesem ausführlichen Leitfaden von Pluralsight, der Yarn und unter anderem die Funktion yarn.lock erklärt.
Fazit
Abhängigkeiten sind wichtig. Um Überraschungen zu vermeiden – und weil Sie bei jeder Paketinstallation möglicherweise andere Versionen der verschachtelten Abhängigkeiten erhalten –, fixieren die meisten Frontend-Entwickler ihre Abhängigkeiten auf bestimmte Versionen. npm-Nutzer hatten bisher das Nachsehen. Damit ist jetzt Schluss: npm 5 bietet die neue Funktion package-lock.
In npm gibt es jetzt mindestens zwei praktische Möglichkeiten, Pakete zu fixieren: die Datei package-lock.json in npm 5, die Shrinkwrap als elegantere Lösung ersetzt, und den integrierten Mechanismus zum Fixieren von Abhängigkeiten in Yarn, bekannt als yarn.lock.
Unser Rat: Fixieren Sie Ihre Abhängigkeiten, aber behalten Sie sie im Blick. Richten Sie mit einem Tool wie Snyk einen Prozess ein, der Ihre Abhängigkeiten kontinuierlich auf Sicherheitslücken überwacht. Gehen Sie außerdem regelmäßig Ihren Abhängigkeitsbaum durch und aktualisieren Sie veraltete Abhängigkeiten aktiv – für bestmöglichen Schutz vor Fehlern und zukünftigen Sicherheitslücken.