Denkt daran git blame? Irgendwann wird jemand das Programm auf der übernommenen Codebasis ausführen, und dabei werden Entscheidungen zutage treten, die Sie vor Jahren getroffen haben. Die Person wird sich wundern. Vielleicht wurde das Projekt weitergegeben. Vielleicht haben Sie das Unternehmen verlassen. VIELLEICHT, vielleicht wurde das Projekt ja an die Community übergeben, damit diese es weiterentwickelt. Das Problem ist jedoch, dass niemand weiß, warum du so gehandelt hast oder welchen Einschränkungen du unterlagst. Die Frage, die sich dabei stellt, ist ganz einfach: Warum hast du dich für dieses Dep entschieden und nicht für ein anderes?
Die Entscheidungen, die du heute in Bezug auf Abhängigkeiten triffst, sind eine Botschaft an den zukünftigen Entwickler, der die Codebasis übernehmen wird. Diese Botschaft kommt jedoch selten an. Meistens ist es nur eine Zeile in package.json mit einem Versionsbereich wie ^2.4.1 und es gibt keine Erklärung dafür, wie es dorthin gekommen ist. Genau an dieser Stelle scheitern viele Open-Source-Abhängigkeitsverwaltungssysteme still und leise.
Keine Momentaufnahme
Das Hinzufügen eines Pakets bedeutet nicht, dass man sich auf ein statisches Artefakt festlegt. Man übernimmt vielmehr eine Entwicklungsgeschichte. Hinter den Kulissen sind viele bewegliche Teile und Menschen beteiligt, wenn man die neueste und beste Bibliothek einbindet, und viele Pakete bringen transitive Abhängigkeiten mit sich, die von Leuten gepflegt werden, von denen man noch nie gehört hat. Der Installationsbefehl vermittelt nichts davon. Betreuer wechseln, und der Code entwickelt sich weiter. Ein Paket bleibt monatelang still, und oft bemerkt es niemand, bis eine CVE auftaucht und niemand mehr da ist, um sie zu beheben.
Die Lockfile-Datei hilft dabei, zu steuern, was installiert wird. Sie protokolliert den aufgelösten Baum, einschließlich der Versionen und – bei Registrierungs-Paketen – der Integritäts-Hashes, sagt aber nichts darüber aus, warum das alles überhaupt vorhanden ist. Das fehlende „Warum“ kommt später zum Vorschein, meist im ungünstigsten Moment. Jemand versucht, einen Sicherheitspatch auszurollen, und stellt fest, dass die Abhängigkeit, die aktualisiert werden muss, seit Jahren nicht mehr veröffentlicht wurde und immer noch auf eine Node.js-Version abzielt, deren Lebenszyklus längst abgelaufen ist. Nicht gut.
Das ist der Code, den du eines Tages weitergeben wirst.
Ohne Kontext vererbt
Wer Ihre Codebasis übernimmt, übernimmt nicht auch Ihren Denkprozess. Er erhält nur die „Brotkrumen“ – und dabei nur sehr wenig Kontext. Die von Ihnen gewählte Abhängigkeit war angesichts der verfügbaren Zeit, der Frist und der von Ihnen recherchierten Alternativen wahrscheinlich die richtige Entscheidung. Damals ergab das alles Sinn. Die Übergabe zu überstehen, ist jedoch eine ganz andere Geschichte. Wenn niemand die Überlegungen dazu schriftlich festgehalten hat, kann der Nachfolger nicht unterscheiden, ob es sich um eine wohlüberlegte Entscheidung oder um einen Zufall handelt.
Stellen Sie sich eine Abhängigkeit vor, die auf eine alte Version festgelegt ist, mit einer Commit-Meldung, die lautet: pin, do not bump. Eine einschneidende API-Änderung? Ein Lizenzproblem? Ein Speicherleck unter Last? Niemand kann sagen, ob der Grund noch immer zutrifft, also bleibt der Pin bestehen, weil es riskanter erscheint, ihn anzutasten. Ohne den Kontext wirkt eine vernünftige Entscheidung plötzlich wie Nachlässigkeit.
Die nächste Person vertraut also entweder blind auf Ihre Entscheidung oder lässt sie von Grund auf neu prüfen. Beides ist kostspielig. Im ersten Fall besteht das Risiko, dass etwas weitergeführt wird, das sich im Stillen als Fehlentscheidung herausgestellt hat. Im zweiten Fall wird Zeit damit verschwendet, eine Schlussfolgerung, zu der Sie bereits gelangt sind, erneut abzuleiten.
An dieser Stelle kann eine Open-Source-Übergabe etwas heikel werden. Ein Fork enthält den Code, die Commit-Historie und alle Dokumente, die es ins Repo geschafft haben. Er enthält jedoch nicht die Notizen aus dem Notizbuch, die Slack-Threads oder die Ticket-Kommentare, in denen ein Großteil der Überlegungen festgehalten wurde. Die Person, die das Projekt übernimmt, muss diesen Kontext entweder mühsam neu aufbauen oder verliert ihn in einer Flut von Pull-Requests.
Auch im vorgelagerten Bereich finden Übergaben statt. Im Jahr 2018 hat der Betreuer von event-stream, ein beliebtes npm-Paket, übertrug die Veröffentlichungsrechte an einen Freiwilligen, der seine Hilfe angeboten hatte. Am 9. September, laut dem Vorfallbericht von npm, fügte der neue Betreuer hinzu flatmap-stream als Abhängigkeit mit dem Bereich ^0.1.0. Am 5. Oktober, [email protected] wurde mit verschleiertem Code ausgeliefert, der sich erst in der Release-Version der Copay-Bitcoin-Wallet aktivierte, wo er nach privaten Schlüsseln suchte. Jede Installation, die diesen Bereich neu auflöst, ohne dass eine Verknüpfung über eine Sperrdatei besteht, 0.1.0, wurde die schädliche Version entdeckt. Sie blieb dort, bis ein Entwickler sie am 20. November meldete.
Verfall ist still, Dokumentation hingegen nicht
Die Gründe für Ihre Auswahl an Abhängigkeiten altern nicht gleichmäßig. Manche bleiben ein Jahrzehnt lang stabil. Andere nicht. Manche versagen fast sofort. Der Verfall ist meist erst sichtbar, wenn jemand gezielt danach sucht – typischerweise aufgrund eines Sicherheitsscans, weil etwas das EOL (End of Life) erreicht oder im Rahmen eines Audits. Softwarewartung und die Übernahme von Open-Source-Projekten sind dem Kauf eines alten Hauses sehr ähnlich. Niemand schaut sich das Fundament an, bis es anfängt, sich zu verschieben.
npm outdated vergleicht die installierten Versionen mit den von Ihnen angegebenen Bereichen und der neuesten Version, allerdings nur bei direkten Abhängigkeiten, es sei denn, Sie fügen --all. npm view <package> time zeigt an, wann die einzelnen Versionen veröffentlicht wurden. Dabei handelt es sich nicht um eine Bewertung der Qualität, da eine ausgereifte Bibliothek auch jahrelang ohne neue Veröffentlichung auskommen kann und ein viel genutztes Repository dennoch schlecht verwaltet sein kann.
Das Open-Source-Abhängigkeitsmanagement wird in der Regel als Auswahl, Aktualisierung und Sicherung von Paketen beschrieben. Auch die Dokumentation der Gründe gehört zu dieser Liste. Teams, die Code-Basen reibungslos weitergeben, hinterlassen eine Spur: warum eine Abhängigkeit ausgewählt wurde, warum eine Version festgesetzt wurde und wie der Plan aussieht, wenn etwas das Ende seiner Lebensdauer (EOL) erreicht.
Der Teil, den du selbst beeinflussen kannst
Man kann nicht alles kontrollieren, insbesondere nicht, wer ein Paket in sechs Jahren noch pflegt. Man kann nicht kontrollieren, ob ein Projekt, auf das man angewiesen ist, aufgegeben wird, eine neue Richtung einschlägt oder geforkt wird. Was man jedoch kontrollieren kann, ist, wie gut man seinen Prozess dokumentiert. Eine Aufzeichnung der Entscheidungen und der dahinterstehenden Überlegungen wird denjenigen, der den Code später übernimmt, sehr freuen.
Jedes der drei Werkzeuge beantwortet eine andere Frage. Ein ADR (Architecture Decision Record) beantwortet die Frage nach dem „Warum“ – oft in Form einer kurzen Markdown-Datei in docs/adr/ die Erfassung des Kontexts, der in Betracht gezogenen Optionen, der Entscheidung und ihrer Folgen. Eine SBOM (Software Bill of Materials) beantwortet die Frage „Was?“. Es handelt sich um ein maschinenlesbares Verzeichnis – in der Regel im SPDX- oder CycloneDX-Format – der Komponenten eines bestimmten Builds, einschließlich transitiver Abhängigkeiten, sofern diese vom Generator erfasst werden. Die Provenienz beantwortet die Fragen „Wo?“ und „Wie?“. Ein mit npm-Herkunft Von einem unterstützten Cloud-CI/CD-Anbieter stammt eine Bescheinigung, die den Code mit seinem Quell-Repository und dem Build verknüpft. Das zeigt, woher der Code stammt, nicht aber, dass er sicher ist.
Die Dokumentation gibt Aufschluss über die Absicht. Sie kann jedoch keine Garantie für die Pakete selbst bieten. Öffentliche Register bieten Sicherheitsvorkehrungen wie Signaturen, Herkunftsnachweise und die Entfernung bösartiger Pakete, doch vielen Paketen fehlt nach wie vor ein Herkunftsnachweis, und keine dieser Kontrollmaßnahmen gibt Aufschluss darüber, ob die heutigen Herausgeber dieselben sind, denen Sie am Tag der Einführung vertraut haben.
SecureChain verfolgt einen anderen Ansatz. Es erstellt Open-Source-Pakete aus geprüften Quellen in abgesicherten, isolierten Umgebungen neu, scannt sie auf Malware und stellt sie über ein kuratiertes Repository bereit. Die Pakete verfügen über eine SLSA-Level-3-Herkunftsangabe für den Neu-Build sowie über VEX-Daten (Vulnerability Exploitability eXchange), aus denen hervorgeht, welche bekannten Schwachstellen sie betreffen, welche nicht und welche behoben wurden. Je nach Paket enthalten sie zudem eine SBOM ihrer Komponenten.
Irgendwann wird eine Komponente in Ihrem Code-Baum das Ende ihres Lebenszyklus erreichen – den Zeitpunkt, an dem die Verantwortlichen keine Sicherheitspatches mehr bereitstellen –, und die Wahrscheinlichkeit ist groß, dass dies geschieht, nachdem Sie das Unternehmen verlassen haben. Endless Lifecycle Support (ELS) portiert Sicherheitspatches auf unterstützte Versionen, deren Lebenszyklus bereits abgelaufen ist, sodass eine abgedeckte Version, die sich bereits im Produktivbetrieb befindet, weiterhin gepatcht wird. Das Team, das Ihre Codebasis übernimmt, steht nicht vor der Wahl, entweder mit ungepatchter Software weiterzuarbeiten oder unter Zeitdruck eine Notfall-Neuprogrammierung durchzuführen. Dadurch gewinnt es Zeit, um eine wohlüberlegte Entscheidung über die Migration zu treffen, anstatt in Panik handeln zu müssen.
Abschließende Überlegungen
Jede Abhängigkeit, die Sie hinzufügen, ist eine dauerhafte Verpflichtung, die Sie demjenigen auferlegen, der das Projekt später weiterführt. Code überdauert Teams, Rollen und Unternehmen. Indem Sie die Gründe für Ihre Wahl der Abhängigkeiten dokumentieren, nachprüfbare Aufzeichnungen darüber führen, was Sie ausliefern, und Vorkehrungen für den Tag treffen, an dem der Support seitens der Upstream-Entwickler endet, geben Sie der Codebasis, die Sie eines Tages übergeben werden, eine echte Chance, ein Gewinn und keine Falle zu sein.
