
Unit-Testing für Lizenzen: Die Rechtmäßigkeit deiner node_modules mit Jest überwachen

Moritz
3. März 2023
Hinweis: Dieser Artikel wurde unter Zuhilfenahme von KI aus dem Englischen übersetzt. Hier geht's zum Originaltext.
NPM-Lizenzen als Teil der Test-Suite
Packages aus dem npm-Registry sind das Herzstück der meisten größeren JavaScript-Projekte. Bibliotheken, Frameworks, Tools usw. - alle unsere Projekte stehen auf den Schultern von Giganten Open-Source-Projekten. Den gesamten Code selbst zu schreiben und zu warten wäre unmöglich, selbst für die größten Unternehmen. Aber es gibt einen Preis dafür, und Supply-Chain-Angriffe sind nur eines der Risiken bei der Verwendung von Drittanbieter-Code. In einem kommerziellen Kontext musst du dir immer der rechtlichen Implikationen von Open-Source-Lizenzen bewusst sein. Es gibt eine Vielzahl verschiedener Lizenzen, und sie erlauben alle unterschiedliche Arten der Nutzung. Ich werde explizit nicht auf die Details eingehen (ich bin kein Anwalt, du solltest für diese Angelegenheiten immer Rechtsprofis konsultieren), aber es ist wichtig, die Lizenzen zu verfolgen, die du in deinen Projekten verwendest. Nicht jede Open-Source-Lizenz ist für jeden Anwendungsfall geeignet, und dessen müssen wir uns bewusst sein!
Es gibt 3 Hauptanliegen, die wir behandeln müssen:
- Wir möchten unseren Kunden (oder ihrem Rechtsteam) einen Überblick über alle Lizenzen geben, die wir in ihrem Produkt verwenden (oft eine vertragliche Verpflichtung).
- Wir möchten bestimmte Lizenzen vermeiden, daher müssen wir verhindern, dass Entwickler sie unwissentlich hinzufügen.
- Wir möchten wissen, ob ein Paket aus irgendeinem Grund seine Lizenz geändert hat, und dann prüfen, ob die neue Lizenz immer noch erlaubt ist.
Heute möchte ich zeigen, wie wir diese Probleme angehen können, indem wir Lizenzen zu einem Teil der Test-Suite machen. Diese Test-Suite könnte bei jeder vorgeschlagenen Änderung am Code ausgeführt werden (z.B. als GitHub-Action bei jedem Pull Request), sodass eine unerwünschte Änderung der Lizenzierung frühzeitig zum Fehlschlagen unserer CI führt.
Beispielprojekt
Nehmen wir an, wir haben ein TypeScript-Projekt, das bereits einige Unit-Tests enthält. Es verwendet Jest / ts-jest dafür, aber du kannst die Idee wahrscheinlich für jedes andere Test-Framework anpassen. Wenn du neu bei Jest bist, bieten sie einen ausgezeichneten Setup-Guide. Auch ts-jest hat dich abgedeckt, wenn du TypeScript verwenden möchtest.
Um die gewünschten Informationen einfach zusammenzustellen, werden wir license-checker-rseidelsohn verwenden, das die Pakete aus unserer Paketdatei sammelt und dann Lizenz- und Autor-Informationen aus node_modules extrahiert. Es kann dann eine Liste in verschiedenen Formaten generieren -- in unserem Fall Markdown. Dieses Paket ist auch praktisch, weil es sowohl eine CLI als auch Exports für programmatische Nutzung bietet. So könnte ein CLI-Aufruf aussehen (--direct bedeutet: nur direkte Abhängigkeiten einbeziehen, nicht ihre transitiven Abhängigkeiten):
→ npx license-checker-rseidelsohn --markdown --direct

Wir können das Ergebnis jetzt in eine Markdown-Datei schreiben (→ npx license-checker-rseidelsohn --markdown > LICENSES.md). Wenn diese Datei im Repository unseres Projekts liegt, rendert GitHub schönes HTML für uns. Für 99% unserer Projekte löst das den Anwendungsfall #1, das Rechtsteam unseres Kunden erhält Zugang zu dieser GitHub-URL und wir sind fertig. Um die Datei aktuell zu halten, können wir auch eine GitHub-Action wie folgt verwenden:
Dadurch wird ein PR geöffnet, wenn die LICENSES.md geändert werden muss.
Das war einfach. Jetzt zum spannenden Teil:
Unit-Tests für die Rechtmäßigkeit
Legen wir alle anderen Prüfungen in einen neuen Unit-Test, nennen wir ihn licenses.test.ts (wir verwenden wieder TypeScript, was dafür nicht notwendig ist, wenn du nicht möchtest). In diesem Test müssen wir ein paar Dinge tun:
Zuerst importieren wir den Checker und promisify ihn, damit wir später async/await verwenden können:
Das Ergebnis vom Checker ist ein Objekt mit packageName@version als Schlüssel und weiteren Informationen als Werte:
Da wir nicht alles davon brauchen, aber die Versionsnummer im Schlüssel ist, sollten wir es wahrscheinlich aufräumen, bevor wir es verwenden. Lass uns all das in beforeAll machen:
Als ersten Test wollen wir herausfinden, ob aktuelle Pakete nicht erlaubte Lizenzen verwenden. Als Beispiel erlaube ich nur ["UNLICENSED", "MIT", "BSD-3-Clause", "Apache-2.0"]. Der eigentliche Test verwendet einen benutzerdefinierten Matcher, damit wir die Ausgabe fehlgeschlagener Test-Nachrichten besser kontrollieren können. Wir müssen TypeScript auch mitteilen, was es von unserem benutzerdefinierten Matcher erwarten kann.
Jetzt ist der erste Test sehr einfach:
Dieser Test wird jedes Paket mit unserem benutzerdefinierten Matcher überprüfen und fehlschlagen, wenn seine Lizenz(en) nicht in der Erlaubnisliste sind. Das löst unseren Anwendungsfall #2.
Für unseren zweiten Test wollen wir die Menge der verschiedenen Lizenzen im Projekt zukunftssicher machen, um zu erkennen, ob ein Paket hinzugefügt, entfernt oder seine Lizenz geändert wurde. Wir können dies mit einem Snapshot-Test tun:
Für diejenigen, die nicht wissen, was ein Snapshot-Test ist: Wenn er zum ersten Mal ausgeführt wird, speichert er einen aktuellen Snapshot des Ergebnisses in einer Datei __snapshots__/licenses.test.ts.snap. Diese Datei muss dann in das Repository eingecheckt werden. Jedes Mal, wenn der Test in Zukunft läuft, wird sein Ergebnis mit diesem Snapshot verglichen. Wenn es sich geändert hat, schlägt der Test fehl und Jest teilt es dir mit.
Gut, lass uns unsere Tests ausführen: → npm run test

Alles sieht gut aus, alle unsere Pakete entsprechen unserer LICENSE_ALLOW_LIST und wir haben jetzt eine Snapshot-Datei, die wir committen können:
Schauen wir, was passiert, wenn wir eine Abhängigkeit hinzufügen: → npm install @react-hookz/web → npm run test

Aus dieser Ausgabe können wir sehen, dass die Lizenz von @react-hookz/web erlaubt ist (der erste Test bestand!), aber sie ist noch nicht im Snapshot. Beheben wir das, indem wir npm run test -- -u ausführen:

Fügen wir eine weitere Abhängigkeit hinzu: → npm install rimraf → npm run test:

Uh oh! rimraf verwendet die ISC-Lizenz, die nicht in unserer Erlaubnisliste ist und unser erster Test hat das erkannt: "ISC" ist keine erlaubte Lizenz (Abhängigkeit: rimraf). Wir können jetzt entscheiden, ob wir dieses Paket entfernen und etwas anderes verwenden oder die Erlaubnisliste des Tests ändern wollen.
Zusammenfassung:
Das sollte funktionieren! Alle 3 Anwendungsfälle werden durch ein paar Zeilen TypeScript und YAML abgedeckt. Du kannst ein vollständig funktionierendes Beispiel-Repo (inklusive GitHub-Actions) hier einsehen: https://github.com/peerigon/blog-license-check-demo
Wenn du nur die Test-Datei willst, hier ist sie:
open source licenses
unit testing
node_modules
Weitere Themen

Michael, 16.04.2025
Green Hosting im Nachhaltigkeitsvergleich
Sustainability
Green Software
Consulting
Hostingsec

Lea, 04.04.2025
Vue clever nutzen – Wiederverwendbarkeit für Einsteiger:innen
Vue
JavaScript
Reusability
DRY Principle
Components
Composables

Francesca, Ricarda, 02.04.2025
Die 10 häufigsten Fehler bei der Entwicklung digitaler Produkte – und wie du sie vermeidest
MVP development
UX/UI design
product vision
agile process
user engagement
product development