Vom internen Tool zum TYPO3-Produkt: Was Extension-Hersteller über SBOMs wissen sollten

Wer ein internes Tool zu einer kommerziellen TYPO3-Extension macht, wird zum Software-Hersteller — und der Cyber Resilience Act erwartet von Herstellern, dass sie wissen, was ihr Produkt enthält. Ein praktischer Blick auf Software Bills of Materials für die PHP- und Frontend-Teile einer TYPO3-Extension.

Bei Digital Zombies arbeiten wir kontinuierlich an TYPO3-Upgrades. Mit der Zeit haben wir eigene Prozesse, Skripte und technisches Wissen entwickelt, um diese Projekte planbarer zu machen. Es lag deshalb nahe zu überlegen, ob ein Teil dieses Tools zu einer TYPO3-Extension werden könnte, die auch andere Agenturen nutzen können.

Aus Engineering-Sicht waren die ersten Fragen vertraut. Was soll die Extension automatisieren? Welche TYPO3-Versionen soll sie unterstützen? Wie lässt sie sich in bestehende Projekte integrieren, ohne unnötige Komplexität einzuführen? Wie testen und pflegen wir sie?

Dann tauchte eine weitere Frage auf:

Wenn wir ein internes Tool zu einer kommerziellen TYPO3-Extension machen, verkaufen wir dann einfach nützlichen Code — oder werden wir zum Hersteller eines Softwareprodukts?

Diese Unterscheidung wird durch den europäischen Cyber Resilience Act (CRA) immer wichtiger. Der CRA führt Cybersicherheitsanforderungen für Produkte mit digitalen Elementen ein, die auf dem EU-Markt bereitgestellt werden. Je nachdem, wie sie entwickelt, vermarktet und vertrieben wird, kann eine kommerzielle TYPO3-Extension als ein solches Produkt gelten. Ob der CRA überhaupt für ein typisches TYPO3-Website-Projekt gilt, ist eine separate Frage, die ich in einem Begleitartikel über TYPO3-Website-Projekte und den CRA untersuche.

Dieser Artikel will nicht die rechtliche Einordnung jeder TYPO3-Extension bestimmen. Ich nähere mich dem Thema als Geschäftsführer eines TYPO3-Engineering-Unternehmens, nicht als Jurist. Stattdessen möchte ich mich auf eine praktische technische Konsequenz konzentrieren, die kommerzielle Extension-Entwickler:innen verstehen sollten: die Software Bill of Materials, meist als SBOM abgekürzt.

Was enthält ein SBOM? Wie erstellen wir eins für die PHP- und Frontend-Teile einer TYPO3-Extension? Und wie verbinden wir diese Informationen mit Releases, Schwachstellenmeldungen und den daraus folgenden technischen Entscheidungen?

Wenn interner Code zum Produkt wird

Dieselbe TYPO3-Extension kann in sehr unterschiedlichen kommerziellen Situationen existieren.

Eine Agentur kann eine Extension ausschließlich für ein Kundenprojekt entwickeln. Ein Unternehmen kann ein internes Tool für die eigenen Entwickler:innen erstellen. Ein:e Extension-Autor:in kann Code frei veröffentlichen, ohne ihn zu monetarisieren. Oder die Extension wird als standardisiertes Produkt für viele Kunden lizenziert, verkauft und supportet.

Aus technischer Sicht kann der Code fast identisch aussehen. Aus kommerzieller und potenziell rechtlicher Sicht sind die Situationen verschieden.

Als wir überlegten, Teile unseres internen Upgrade-Tools in eine wiederverwendbare Extension zu überführen, mussten wir über die reine Funktionalität hinausblicken. Software zu verkaufen bedeutet, nicht nur dafür Verantwortung zu übernehmen, ob die aktuelle Version funktioniert, sondern auch dafür, wie sie entwickelt, aktualisiert und supportet wird.

Das wirft Fragen auf wie:

  • Wie lange supporten wir jedes Release?
  • Wie erfahren wir von Schwachstellen in unseren Abhängigkeiten?
  • Wie schnell können wir feststellen, welche Versionen betroffen sind?
  • Wie verteilen wir Sicherheitsupdates?
  • Was passiert, wenn eine Abhängigkeit nicht mehr gepflegt wird?
  • Wie dokumentieren wir die während der Entwicklung getroffenen Entscheidungen?

Das sind auch ohne den CRA sinnvolle Engineering-Fragen. Die Verordnung macht es nur schwerer, sie aufzuschieben.

Eine der ersten Anforderungen ist zu wissen, was das Produkt tatsächlich enthält. Genau hier wird das SBOM relevant.

Was ist ein SBOM?

Eine Software Bill of Materials ist ein strukturiertes Inventar der Softwarekomponenten, die ein Produkt enthält. Man vergleicht sie oft mit einer Zutatenliste, auch wenn eine Softwareanwendung komplizierter ist, weil ihre Komponenten voneinander abhängen.

Ein SBOM kann enthalten:

  • Komponenten- und Paketnamen;
  • exakte Versionen;
  • Paket-Lieferanten oder -Autor:innen;
  • Paket-Identifikatoren;
  • Lizenzinformationen;
  • Prüfsummen;
  • direkte Abhängigkeiten;
  • transitive Abhängigkeiten;
  • Beziehungen zwischen Komponenten.

Das Ergebnis wird normalerweise in einem standardisierten, maschinenlesbaren Format gespeichert. Zwei etablierte Formate sind CycloneDX und SPDX. Für die Beispiele in diesem Artikel verwende ich CycloneDX, weil sein Modell auf Anwendungsfälle rund um Software-Lieferkette und Schwachstellenmanagement ausgelegt ist.

CycloneDX kann Komponenten, Services, Abhängigkeitsbeziehungen, Schwachstellen und Angaben zur Vollständigkeit des Inventars abbilden. Die aktuelle Spezifikation findet sich unter cyclonedx.org/specification/overview.

Für eine TYPO3-Extension ist das wichtig, weil selbst ein relativ kleines Produkt mehr als die eigenen PHP-Klassen enthalten kann. Es kann von TYPO3-Core-Paketen, anderen Extensions, Symfony-Komponenten, externen PHP-Bibliotheken und npm-Paketen abhängen, die für sein Backend-Modul oder seine Frontend-Ausgabe genutzt werden.

Ohne ein strukturiertes Inventar kann die Beantwortung einer einfachen Sicherheitsfrage länger dauern als erwartet:

Enthält eine von Kunden eingesetzte Version unserer Extension die betroffene Komponente?

Ein SBOM macht das Inventar maschinenlesbar, sodass es gespeichert, verglichen und mit Schwachstellen-Monitoring-Systemen verbunden werden kann.

Was sagt der CRA zu SBOMs?

Der Cyber Resilience Act zählt die Erstellung eines SBOM zu den Anforderungen an das Schwachstellen-Handling für Hersteller. Anhang I, Teil II verlangt von Herstellern, Schwachstellen und in ihren Produkten enthaltene Komponenten zu identifizieren und zu dokumentieren — unter anderem durch die Erstellung eines SBOM in einem gängigen, maschinenlesbaren Format. Es muss mindestens die Top-Level-Abhängigkeiten des Produkts abdecken.

Die vollständige Verordnung ist auf EUR-Lex verfügbar.

Das bedeutet nicht, dass jedes SBOM öffentlich zugänglich gemacht werden muss. Es bedeutet auch nicht, dass die Erstellung eines SBOM ausreicht, um CRA-Konformität nachzuweisen. Es ist ein Teil eines umfassenderen Prozesses, der sichere Produktentwicklung, Schwachstellen-Handling, Dokumentation, Sicherheitsupdates und Meldung von Vorfällen einschließt.

Für Extension-Entwickler:innen ist die eigentliche Engineering-Frage daher nicht einfach, wie man eine bestimmte Datei erzeugt. Es ist die Frage, ob wir die in jeder veröffentlichten Version enthaltenen Komponenten zuverlässig identifizieren und reagieren können, wenn eine davon verwundbar wird.

Welches Produkt dokumentieren wir eigentlich?

Bevor wir irgendetwas erzeugen, müssen wir die Produktgrenze festlegen.

Das klingt selbstverständlich, ist aber eine der wichtigsten Entscheidungen im Prozess. Eine kommerzielle TYPO3-Extension und die vollständige TYPO3-Installation, in der sie eingesetzt wird, sind nicht dasselbe Produkt.

Stellen wir uns vor, wir verkaufen eine Extension namens vendor/upgrade-assistant. Ihre composer.json könnte Folgendes enthalten:

composer.json
{
  "name": "vendor/upgrade-assistant",
  "description": "Upgrade analysis and assistance tools for TYPO3",
  "type": "typo3-cms-extension",
  "require": {
    "php": "^8.2 || ^8.3 || ^8.4",
    "typo3/cms-core": "^13.4 || ^14.0",
    "symfony/console": "^7.2"
  }
}

Ein wiederverwendbares Composer-Paket sollte normalerweise kein manuell gepflegtes "version"-Feld enthalten. Composer kann die Release-Version aus dem Git-Tag oder den Repository-Metadaten ableiten.

Die Extension deklariert, welche Versionen sie unterstützt, bestimmt aber nicht, welche exakten TYPO3- oder Symfony-Versionen in einem Kundenprojekt installiert werden. Composer löst diese Versionen zusammen mit den Anforderungen der vollständigen Installation auf.

Damit bleiben uns zwei verschiedene Inventare.

Das SBOM auf Extension-Ebene

Das SBOM auf Extension-Ebene beschreibt das Produkt, das wir vertreiben. Es sollte identifizieren:

  • die Extension und ihre Release-Version;
  • ihre deklarierten Composer-Anforderungen;
  • mit der Extension gebündelte PHP-Bibliotheken;
  • in kompilierte Assets eingebundene JavaScript-Bibliotheken;
  • weiteren Third-Party-Code, der im Paket ausgeliefert wird.

Das ist die Produktgrenze, über die der Extension-Hersteller die direkteste Kontrolle hat.

Das SBOM auf Projektebene

Das SBOM auf Projektebene wird aus dem vollständig installierten TYPO3-Projekt erzeugt. Es kann enthalten:

  • die exakt installierte TYPO3-Core-Version;
  • die kommerzielle Extension;
  • alle anderen installierten Extensions;
  • den vollständig aufgelösten Composer-Abhängigkeitsbaum;
  • kundenspezifische Pakete;
  • Frontend-Abhängigkeiten auf Projektebene.

Ein Extension-Hersteller kann kein exaktes SBOM auf Projektebene für jeden Kunden erzeugen, weil das Kundenprojekt die finalen Paketversionen auflöst. Umgekehrt hebt ein SBOM auf Projektebene nicht die Verantwortung des Extension-Herstellers auf, zu verstehen, was als Teil der Extension ausgeliefert wird.

Ein Composer-SBOM erzeugen

Das CycloneDX-PHP-Composer-Plugin erzeugt ein SBOM aus den Composer-Paketinformationen. Laut Dokumentation unterstützt es PHP 8.1 oder neuer und Composer 2.3 oder neuer.

Es lässt sich als Entwicklungsabhängigkeit hinzufügen:

Shell
composer require --dev cyclonedx/cyclonedx-php-composer

Nachdem die Abhängigkeiten installiert wurden, lässt sich eine CycloneDX-JSON-Datei erzeugen mit:

Shell
mkdir -p build

composer CycloneDX:make-sbom \
  --output-format=JSON \
  --output-file=build/sbom-backend.cdx.json \
  --omit=dev \
  --spec-version=1.7 \
  --validate

Der Befehlsname und die Optionen oben entsprechen der aktuellen Plugin-Dokumentation unter github.com/CycloneDX/cyclonedx-php-composer.

Das Plugin unterstützt CycloneDX-Spezifikationsversionen bis 1.7 und kann das erzeugte Dokument validieren. Es bezieht seine Informationen aus der composer.json, dem installierten Composer-Setup oder einer funktionierenden composer.lock. Die Dokumentation empfiehlt, wo möglich das installierte Setup zu verwenden.

Die Option --omit=dev bedeutet, dass Entwicklungsabhängigkeiten aus dem erzeugten Inventar ausgeschlossen werden. Das Plugin selbst kann daher während des Builds verwendet werden, ohne als Teil der ausgelieferten Extension aufzutauchen.

Es gibt jedoch ein wichtiges Detail zur Reihenfolge. Wenn das Plugin unter require-dev installiert ist, funktioniert das hier nicht:

Shell
composer install --no-dev
composer CycloneDX:make-sbom

Der erste Befehl lässt das Plugin weg, sodass der zweite Befehl möglicherweise nicht mehr verfügbar ist.

In einer Release-Pipeline können wir stattdessen im Analyse-Job alle Abhängigkeiten installieren, das SBOM erzeugen und dabei Entwicklungsabhängigkeiten aus seinem Inhalt ausschließen und anschließend das Produktions-Artefakt in einem separaten Schritt erstellen:

Shell
composer install --no-interaction --prefer-dist

mkdir -p build

composer CycloneDX:make-sbom \
  --output-format=JSON \
  --output-file=build/sbom-backend.cdx.json \
  --omit=dev \
  --spec-version=1.7 \
  --validate

Eine Alternative ist, das CycloneDX-Plugin global in der CI-Umgebung zu installieren. In diesem Fall muss das Projekt selbst den Generator nicht requiren. Welche Methode auch gewählt wird: Die Tool-Version sollte fixiert oder festgehalten werden, damit der Prozess reproduzierbar bleibt.

Die Release-Version der Extension setzen

In einem Library-Repository wird die Version der Root-Komponente nicht immer wie erwartet erkannt, insbesondere wenn die Pipeline einen Commit ohne den zugehörigen Git-Tag auscheckt.

Das Plugin bietet dafür eine explizite Option --mc-version für die Hauptkomponente:

Shell
composer CycloneDX:make-sbom \
  --output-format=JSON \
  --output-file=build/sbom-backend.cdx.json \
  --omit=dev \
  --spec-version=1.7 \
  --mc-version=1.2.0 \
  --validate

In einer echten Pipeline sollte 1.2.0 aus dem validierten Release-Tag stammen und nicht an mehreren Stellen manuell eingetragen werden.

Die Composer-Einschränkung bei wiederverwendbaren Extensions

Das erzeugte Composer-SBOM ist exakt für die Abhängigkeitsmenge, die in der Umgebung installiert ist, in der der Befehl läuft. Es ist nicht automatisch eine exakte Beschreibung jeder Kundeninstallation.

Angenommen, die Extension requiret:

composer.json
{
  "require": {
    "symfony/console": "^7.2"
  }
}

Die Testumgebung der Extension löst vielleicht Symfony Console 7.4 auf, während eine Kundeninstallation eine andere kompatible Version enthält. Beide erfüllen die deklarierte Einschränkung.

Aus diesem Grund sollte ein aus der CI-Umgebung der Extension erzeugtes SBOM präzise beschrieben werden. Es kann dokumentieren:

  • die von der Extension deklarierten direkten Anforderungen;
  • gebündelten Third-Party-Code;
  • die exakte Abhängigkeitsmenge, mit der das Release getestet wurde.

Es sollte nicht als Nachweis der exakten Versionen dargestellt werden, die in jeder Kundeninstallation laufen.

Für eine kommerzielle Extension, die mehrere TYPO3-Versionen unterstützt, kann es sinnvoll sein, für jede unterstützte Abhängigkeitsmatrix ein separates Test-SBOM zu erzeugen. Diese Dokumente zeigen, welche Kombinationen getestet wurden, während das finale Kundenprojekt weiterhin sein eigenes Inventar auf Projektebene benötigt.

Eine TYPO3-spezifische Option: SBOMinator

Es gibt bereits eine experimentelle TYPO3-Extension, um ein SBOM direkt aus einer TYPO3-Installation zu erzeugen: sbominator/sbom-typo3.

Die Extension entstand während des CloudFest Hackathon 2025. Sie stellt ein TYPO3-Backend-Modul bereit, über das ein:e Administrator:in ein Inventar im SPDX- oder CycloneDX-Format herunterladen kann.

Paket und aktueller Status finden sich unter packagist.org/packages/sbominator/sbom-typo3.

Der dokumentierte Installationsbefehl lautet:

Shell
composer require "sbominator/sbom-typo3:@dev"

Das braucht allerdings eine klare Warnung. Die Maintainer beschreiben die Extension als experimentell und als Proof of Concept. Sie weisen außerdem darauf hin, dass der erzeugte SBOM-Inhalt noch Validierung und Verfeinerung braucht. Die aktuellen Abhängigkeitsdeklarationen verwenden Development-Constraints, daher würde ich sie eher in einer Entwicklungs- oder Testinstallation evaluieren, als sie direkt in ein Produktionsprojekt einzuführen.

SBOMinator ist dennoch hochrelevant, weil es zeigt, wie ein Inventar auf Projektebene innerhalb von TYPO3 zugänglich gemacht werden könnte. Statt eine:n Administrator:in zu bitten, einen Befehl auf dem Server auszuführen, stellt es ein Backend-Modul bereit, das die von der Website genutzten Softwarekomponenten untersucht und das Ergebnis als Download anbietet.

Das löst ein anderes Problem als unser Beispiel der kommerziellen Extension.

MethodePrimärer Geltungsbereich
CycloneDX-Composer-Plugin im Extension-RepositoryExtension-Entwicklung und Release-Abhängigkeitsmenge
npm-SBOM im Extension-RepositoryFrontend-Abhängigkeitsbaum der Extension
SBOMinator innerhalb von TYPO3In einer vollständigen TYPO3-Installation erkannte Komponenten
SBOM aus der Projekt-PipelineAufgelöste Abhängigkeiten eines konkret deployten Releases

Für einen Extension-Hersteller kann SBOMinator den Release-Prozess auf Produktebene nicht ersetzen. Es läuft in der vollständigen TYPO3-Umgebung des Kunden und beschreibt daher eine breitere Installation. Gleichzeitig kann das Composer-SBOM des Herstellers nicht das gesamte Projekt des Kunden beschreiben.

Die Ansätze ergänzen einander. Der Extension-Hersteller dokumentiert, was entwickelt und vertrieben wird. Der Projektbetreiber dokumentiert, was schließlich installiert und deployt wird.

Unsichtbare PHP-Abhängigkeiten vermeiden

Die meisten TYPO3-Extensions sollten Composer ihre PHP-Abhängigkeiten verwalten lassen. Eine Third-Party-Bibliothek in die Extension zu kopieren oder ein internes vendor/-Verzeichnis auszuliefern, erschwert Identifikation, Updates und Schwachstellen-Monitoring.

Wenn externer Code an Orte wie diese kopiert wird, wird er wahrscheinlich nicht automatisch identifiziert:

Nicht automatisch erkannt
Resources/Private/Lib/
Resources/Public/JavaScript/Vendor/
Classes/Utility/ThirdParty/

Das Fehlen eines Composer-Eintrags macht Third-Party-Code nicht zu eigenem Code. Es macht nur seine Herkunft und Version schwerer nachvollziehbar.

Wenn später eine Schwachstelle gemeldet wird, kann die kopierte Bibliothek noch vorhanden sein, obwohl sich niemand erinnert, woher sie stammt. Für ein kommerzielles Produkt, das über mehrere Jahre Sicherheitssupport erhalten soll, entsteht dadurch ein unnötiges Risiko.

Externe PHP-Bibliotheken sollten daher, wo immer praktikabel, für Composer sichtbar bleiben. Lässt sich das Bündeln nicht vermeiden, muss die Komponente über einen anderen kontrollierten Prozess in das Produktinventar aufgenommen werden.

Das Frontend-SBOM erzeugen

Ein Composer-SBOM beschreibt nicht die npm-Pakete, die zum Bauen des Backend-Moduls oder der Frontend-Funktionalität einer Extension verwendet werden.

Aktuelle npm-Versionen stellen einen nativen Befehl npm sbom bereit. Vorausgesetzt, eine package-lock.json ist vorhanden und die Abhängigkeiten wurden mit npm ci installiert, lässt sich ein CycloneDX-Dokument für eine Extension erzeugen mit:

Shell
mkdir -p build

npm sbom \
  --sbom-format=cyclonedx \
  --sbom-type=library \
  > build/sbom-frontend.cdx.json

Für eine wiederverwendbare TYPO3-Extension ist library der passende Typ. Für ein vollständig deploytes TYPO3-Projekt wäre application in der Regel genauer.

Der Befehl und die verfügbaren Werte sind bei npm dokumentiert: docs.npmjs.com/cli/v11/commands/npm-sbom.

Wenn die Pipeline das Dokument bewusst aus der package-lock.json erzeugen will, ohne den installierten node_modules-Baum zu verwenden, unterstützt npm:

Shell
npm sbom \
  --package-lock-only \
  --sbom-format=cyclonedx \
  --sbom-type=library \
  > build/sbom-frontend.cdx.json

Dieser Befehl ist gültig, aber das Ergebnis enthält weniger Metadaten. npm dokumentiert, dass der Lock-file-only-Modus Informationen wie Beschreibungen, Homepages und Engine-Anforderungen nicht aus den Abhängigkeitspaketen liest.

Warum --omit=dev in die Irre führen kann

npm unterstützt ebenfalls --omit=dev:

Shell
npm sbom \
  --sbom-format=cyclonedx \
  --sbom-type=library \
  --omit=dev \
  > build/sbom-frontend.cdx.json

Für eine ausgelieferte TYPO3-Extension würde ich diese Option nicht automatisch verwenden.

Frontend-Build-Abhängigkeiten werden häufig unter devDependencies deklariert, aber ihr Code kann trotzdem in das mit der Extension ausgelieferte JavaScript gebündelt sein. Vite, webpack oder esbuild können Hunderte npm-Module in wenige kompilierte Dateien überführen. Der Kunde erhält keine node_modules, aber die resultierenden Assets können dennoch Code aus diesen Paketen enthalten.

Ein Sass-Compiler, der nur SCSS transformiert, gehört zur Build-Umgebung. Eine JavaScript-Bibliothek, die in die ausgelieferte Ausgabe gebündelt wird, wird Teil des resultierenden Produkts. Beide können unter devDependencies stehen, obwohl sie unterschiedliche Rollen spielen.

Die Klassifizierung durch den Paketmanager allein definiert daher nicht die Produktgrenze. Der Release-Prozess muss bestimmen, welche Abhängigkeiten Code zum ausgelieferten Artefakt beitragen.

Ein minimales npm-Beispiel

Für eine Extension mit der folgenden package.json:

package.json
{
  "name": "@vendor/upgrade-assistant",
  "version": "1.2.0",
  "private": true,
  "dependencies": {
    "lodash": "4.17.21"
  }
}

lässt sich der Abhängigkeitsbaum reproduzierbar installieren mit:

Shell
npm ci

Ein CycloneDX-SBOM vom Typ library lässt sich anschließend erzeugen mit:

Shell
mkdir -p build

npm sbom \
  --sbom-format=cyclonedx \
  --sbom-type=library \
  > build/sbom-frontend.cdx.json

Die Einstellung "private": true ist nützlich, wenn die package.json nur existiert, um die TYPO3-Extension zu bauen, und nicht versehentlich selbst in der npm-Registry veröffentlicht werden soll.

Der obige Befehl wurde mit npm 11.9.0 verifiziert. Er erzeugte ein gültiges CycloneDX-Dokument, klassifizierte die Root-Komponente als library und nahm die gelockte Abhängigkeit mit ihrer exakten Version auf.

Unsere geplante Architektur: Releases, SBOMs und Schwachstellen verbinden

Als wir über die Architektur unserer Upgrade-Assistance-Extension nachdachten, wollten wir die SBOM-Erzeugung nicht zu einem isolierten Pipeline-Befehl machen, der zwei Dateien produziert, die niemand mehr ansieht.

Der nützliche Teil beginnt, wenn die einzelnen Bausteine verbunden werden.

Eine kommerzielle TYPO3-Extension kann über Composer verwaltete PHP-Abhängigkeiten und über npm verwaltete Frontend-Abhängigkeiten enthalten. Diese ergeben getrennte Abhängigkeitsbäume und damit getrennte SBOM-Dateien. Zugleich beschreiben beide Inventare Teile desselben Produkt-Releases.

Unsere geplante Architektur beruhte daher auf einer Release-Identität, die Folgendes verbindet:

  • die Extension-Version;
  • den Git-Commit;
  • das ausgelieferte ZIP- oder Composer-Paket;
  • das Composer-SBOM;
  • das npm-SBOM;
  • die unterstützten TYPO3-Versionen;
  • bekannte Schwachstellen;
  • das Ergebnis unserer Engineering-Bewertung;
  • Sicherheitsupdates, die das betroffene Release ersetzen.

Ein vereinfachter Release-Job könnte enthalten:

release.sh
composer install --no-interaction --prefer-dist
npm ci
npm run build

mkdir -p build

composer CycloneDX:make-sbom \
  --output-format=JSON \
  --output-file=build/sbom-backend.cdx.json \
  --omit=dev \
  --spec-version=1.7 \
  --mc-version=1.2.0 \
  --validate

npm sbom \
  --sbom-format=cyclonedx \
  --sbom-type=library \
  > build/sbom-frontend.cdx.json

Dieses Beispiel setzt voraus, dass:

  • das CycloneDX-Composer-Plugin als Entwicklungsabhängigkeit installiert ist;
  • composer.lock und package-lock.json vorhanden sind;
  • npm den Befehl sbom unterstützt;
  • Version 1.2.0 gebaut wird;
  • die Frontend-Assets erzeugt werden, bevor das Release-Artefakt paketiert wird.

Das Ergebnis ist nicht ein SBOM, sondern zwei gültige CycloneDX-Dokumente:

build/
upgrade-assistant-1.2.0-backend.cdx.json
upgrade-assistant-1.2.0-frontend.cdx.json

Diese Dateien einfach aneinanderzuhängen würde kein gültiges kombiniertes SBOM ergeben. Stattdessen sollten beide Dokumente mit demselben Release-Datensatz verbunden werden.

Konzeptionell könnte dieser Datensatz enthalten:

release-record.json
{
  "product": "vendor/upgrade-assistant",
  "version": "1.2.0",
  "commit": "4f82c91",
  "artifacts": {
    "package": "upgrade-assistant-1.2.0.zip",
    "backendSbom": "upgrade-assistant-1.2.0-backend.cdx.json",
    "frontendSbom": "upgrade-assistant-1.2.0-frontend.cdx.json"
  }
}

Das ist kein SBOM-Format und sollte nicht als solches dargestellt werden. Es ist ein Beispiel für die Metadaten, die nötig sind, um das Produkt-Release mit den zugehörigen Artefakten zu verbinden.

Das wichtige architektonische Prinzip ist, dass eine Schwachstelle niemals nur auf ein abstraktes Paket verweisen sollte. Sie sollte uns zu den betroffenen Produkt-Releases führen und von dort zu den Kunden oder Installationen, die ein Update benötigen könnten.

Die Dateien zu erzeugen ist der einfachere Teil

Composer und npm können bereits die meisten Informationen für ein grundlegendes Abhängigkeitsinventar liefern. Die schwierigere Frage ist, was passiert, wenn mehrere Monate nach dem Extension-Release eine neue Schwachstelle veröffentlicht wird.

Die SBOM-Dateien benachrichtigen uns nicht automatisch. Sie enthalten Komponenteninformationen, aber irgendetwas muss dieses Inventar dennoch kontinuierlich mit aktuellen Schwachstellenquellen abgleichen.

Ein vollständiger Prozess erfordert daher eine weitere Schicht:

  1. die Pipeline erzeugt die SBOMs;
  2. die SBOMs werden mit dem Produkt-Release gespeichert;
  3. ihre Komponenten werden in ein Monitoring-System importiert;
  4. das System prüft regelmäßig aktuelle Schwachstellendatenbanken;
  5. ein relevanter Treffer erzeugt eine Benachrichtigung;
  6. ein:e Entwickler:in bewertet, ob die Extension tatsächlich betroffen ist;
  7. Bewertung und resultierende Maßnahme werden dokumentiert;
  8. ein Update wird vorbereitet und mit dem betroffenen Release verbunden.

Auch das war für uns eine der interessantesten architektonischen Fragen. Ein SBOM einmalig zu erzeugen ist relativ einfach. Sechs Monate später eine verlässliche Benachrichtigung zu erhalten, wenn eine neu entdeckte Schwachstelle eine transitive Abhängigkeit betrifft, erfordert ein dauerhaftes System.

Die Monitoring-Plattform muss mehr kennen als den aktuellen Abhängigkeitsbaum. Sie muss historische Releases vorhalten, weil Kunden auch nach der Veröffentlichung von Version 1.2.0 noch Version 1.1.0 einsetzen können.

Eine Schwachstelle kann daher zu unterschiedlichen Bewertungen führen:

ReleaseKomponenteErgebnisMaßnahme
1.0.0Betroffene AbhängigkeitBetroffenSicherheitsupdate erforderlich
1.1.0Betroffene AbhängigkeitNicht ausnutzbarBewertung dokumentiert
1.2.0Aktualisierte AbhängigkeitNicht betroffenKeine Maßnahme erforderlich

Diese historische Verknüpfung ist einer der Gründe, warum es für ein kommerzielles Produkt nicht ausreicht, composer audit oder npm audit nur gegen den aktuellen Main-Branch laufen zu lassen. Diese Befehle können wertvolle Informationen über den aktuellen Checkout liefern, überwachen aber nicht automatisch jedes zuvor ausgelieferte Release.

Vom Schwachstellen-Treffer zur Engineering-Entscheidung

Eine Schwachstellendatenbank kann einen möglichen Treffer zwischen einem Advisory und einer Paketversion identifizieren. Sie kann nicht immer bestimmen, ob die kommerzielle Extension tatsächlich betroffen ist.

Zum Beispiel:

  • die verwundbare Funktion wird vielleicht nicht genutzt;
  • der betroffene Codepfad ist vielleicht nicht erreichbar;
  • die Schwachstelle erfordert vielleicht eine Konfiguration, die die Extension nicht unterstützt;
  • die Abhängigkeit wird vielleicht nur während des Builds verwendet;
  • das ausgelieferte Asset enthält vielleicht nicht den betroffenen Teil;
  • in der Extension existiert vielleicht bereits eine Mitigation.

Auch das Gegenteil gilt. Ein Dependency-Scanner kann eine Schwachstelle in unserem eigenen PHP- oder JavaScript-Code nicht erkennen, einfach weil dieser Code kein externes Paket-Advisory hat.

Die Benachrichtigung ist daher der Beginn eines Engineering-Prozesses, nicht sein Abschluss.

Für jeden relevanten Treffer müssen wir bestimmen:

  • welche Releases die Komponente enthalten;
  • ob die betroffene Funktionalität vorhanden ist;
  • ob sie im vorgesehenen Einsatz der Extension erreichbar ist;
  • ob ein Update oder eine Mitigation verfügbar ist;
  • ob ein neues Extension-Release nötig ist;
  • welche Kunden oder Installationen informiert werden müssen.

CycloneDX unterstützt außerdem VEX, den Vulnerability Exploitability eXchange. Mit VEX lässt sich dokumentieren, ob ein Produkt von einer bekannten Schwachstelle betroffen ist, und das Ergebnis der Bewertung erklären: cyclonedx.org/capabilities/vex.

Das ist wichtig, weil das wiederholte Verwerfen von Scanner-Funden ohne Dokumentation keinen verlässlichen Sicherheitsprozess ergibt. Wenn wir zu dem Schluss kommen, dass eine Extension nicht betroffen ist, sollte dieser Schluss mit der Schwachstelle, den relevanten Produkt-Releases und der technischen Begründung der Entscheidung verbunden sein.

Die Herausforderung der Benachrichtigung

Die Schwachstellen-Benachrichtigungsschicht verdient besondere Aufmerksamkeit, weil die Qualität des gesamten Prozesses von ihr abhängt.

Ein Benachrichtigungssystem, das zu viele irrelevante Alerts erzeugt, wird irgendwann ignoriert. Ein System, das nur direkte Abhängigkeiten prüft, übersieht möglicherweise eine Schwachstelle weiter unten im Abhängigkeitsbaum. Ein System, das nur das neueste Release überwacht, lässt Kunden, die unterstützte ältere Versionen einsetzen, ungeschützt.

Ein sinnvoller Monitoring-Prozess sollte daher in der Lage sein:

  • Standard-Paket-Identifikatoren wie Package URLs zu erkennen;
  • sowohl Composer- als auch npm-Komponenten zu verarbeiten;
  • historische Produkt-Releases vorzuhalten;
  • direkte und transitive Abhängigkeiten zu unterscheiden;
  • doppelte Alerts für dieselbe Schwachstelle zu vermeiden;
  • aktualisierte oder zurückgezogene Advisories zu berücksichtigen;
  • betroffene Produkte dem verantwortlichen Engineering-Team zuzuordnen;
  • den Bewertungs- und Behebungsstatus zu speichern;
  • erneut zu benachrichtigen, wenn sich die verfügbaren Informationen ändern.

Hier entfaltet ein SBOM operativen Wert. Es gibt dem Monitoring-System ein standardisiertes Inventar, aber das System muss neue Schwachstelleninformationen dennoch in eine umsetzbare Engineering-Aufgabe überführen.

Für ein Unternehmen, das mehrere Produkte pflegt, kann daraus ein zentraler Service werden statt eines separaten Prozesses in jedem Repository. Jede Release-Pipeline sendet ihre SBOMs an die Plattform, während die Plattform das kontinuierliche Monitoring, die Benachrichtigungen und die dokumentierten Bewertungen übernimmt.

Wo SBOMinator in diese Architektur passt

Die experimentelle Extension sbominator/sbom-typo3 nähert sich demselben Thema aus der Perspektive der installierten Website. Sie kann CycloneDX- oder SPDX-Inventare aus einer TYPO3-Installation heraus erzeugen.

Das liefert einen weiteren nützlichen Input für die Architektur.

Die Pipeline des Extension-Herstellers beschreibt die Abhängigkeiten, die mit einem bestimmten Produkt-Release verbunden sind. SBOMinator versucht, die Komponenten zu beschreiben, die in der vollständigen TYPO3-Installation des Kunden vorhanden sind.

Diese Perspektiven lassen sich verbinden:

  • der Hersteller weiß, welche Abhängigkeiten zur kommerziellen Extension gehören;
  • der Projektbetreiber weiß, welche exakten Paketversionen in der Installation aufgelöst wurden;
  • die Monitoring-Plattform weiß, welche Releases und Installationen betroffen sein könnten;
  • die Engineering-Bewertung bestimmt, ob eine Maßnahme erforderlich ist.

SBOMinator wird derzeit als experimenteller Proof of Concept beschrieben, daher muss seine Ausgabe noch validiert werden, bevor sie Teil eines produktiven Compliance-Prozesses wird. Dennoch zeigt es, wie TYPO3 selbst an einer größeren Software-Inventar-Architektur teilnehmen kann.

Die interessante langfristige Möglichkeit ist nicht nur das manuelle Herunterladen eines SBOM aus dem TYPO3-Backend. Es ist die sichere Übertragung des Inventars an eine zentrale Plattform, die es über die gesamte Lebensdauer der Installation überwachen kann.

Von Software-Herstellern wird heute mehr verlangt

Vor dem Cyber Resilience Act konnte sich ein:e kommerzielle:r Extension-Entwickler:in vor allem darauf konzentrieren, die Funktionalität zu entwickeln, neue Versionen zu veröffentlichen und zu reagieren, wenn Kunden Probleme meldeten.

Für ein professionell gepflegtes Softwareprodukt reicht das nicht mehr aus.

Ein Hersteller braucht zunehmend einen strukturierten Prozess für:

  • die Identifikation aller im Produkt enthaltenen Komponenten;
  • das Monitoring von Schwachstellen nach einem Release;
  • die Bewertung, ob das Produkt betroffen ist;
  • die Bereitstellung von Sicherheitsupdates;
  • die Dokumentation technischer Entscheidungen;
  • die Festlegung und Kommunikation eines Supportzeitraums;
  • die Benachrichtigung der zuständigen Stellen, wenn nötig.

Das bedeutet mehr Arbeit, als vor dem CRA üblicherweise von einem:einer TYPO3-Extension-Entwickler:in erwartet wurde. Es bedeutet auch, dass Wartungs- und Sicherheitsverantwortung bei der Definition des Preis- und Supportmodells einer kommerziellen Extension berücksichtigt werden müssen.

Das zugrunde liegende Ziel halte ich jedoch nicht für unangemessen. Ein Kunde, der ein Softwareprodukt kauft, sollte erwarten dürfen, dass dessen Hersteller weiß, welche Komponenten es enthält, neu veröffentlichte Sicherheitsinformationen verfolgt und ein Update bereitstellen kann, wenn eine relevante Schwachstelle entdeckt wird.

Der CRA formalisiert Verantwortlichkeiten, die reife Software-Hersteller ohnehin ernst nehmen sollten. Seine Umsetzung wird technische und organisatorische Herausforderungen schaffen, besonders für kleinere Extension-Anbieter, aber die Absicht hinter diesen Anforderungen ist nachvollziehbar.

Fazit

Eine kommerzielle TYPO3-Extension zu erstellen bedeutet heute mehr, als ein nützliches Feature zu entwickeln und ein Paket zu veröffentlichen.

Die Backend- und Frontend-Abhängigkeiten müssen identifiziert werden. Die erzeugten SBOMs müssen mit einem konkreten Release verbunden werden. Historische Releases müssen sichtbar bleiben. Schwachstelleninformationen müssen kontinuierlich überwacht werden, und ein Alert muss zu einer dokumentierten Engineering-Bewertung und, wo nötig, zu einem Sicherheitsupdate führen.

Das war die architektonische Richtung, die wir für unsere eigene Extension in Betracht gezogen haben:

Jedes Produkt-Release mit seinen Backend- und Frontend-SBOMs verbinden, diese Inventare kontinuierlich überwachen und relevante Schwachstelleninformationen in einen umsetzbaren Engineering-Prozess überführen.

Der Cyber Resilience Act verlangt von Software-Herstellern mehr, als viele Extension-Entwickler:innen vor 2024 getan haben. Das schafft zusätzlichen Aufwand, hat aber auch ein wertvolles Ziel: Produkte sollten für ihre Hersteller nicht unsichtbar werden, nachdem sie verkauft wurden. Diesem Grundsatz stimmen wir zu.

Ein SBOM macht Software nicht von allein sicher, aber wenn es mit Releases, Schwachstellen-Monitoring, Benachrichtigungen und Engineering-Entscheidungen verbunden ist, wird es zur Grundlage eines verantwortungsvolleren Produktlebenszyklus.

Nächster ArtikelGilt der Cyber Resilience Act für ein typisches TYPO3-Website-Projekt?