Zum Hauptinhalt springen

SBOMs (Software Bill of Materials)

Eine SBOM (Software Bill of Materials) ist die Zutatenliste eines Builds: jede direkte und transitive Abhängigkeit, die in ein Artefakt eingebaut ist, mit Versionen und Hashes. fremforge speichert die SBOMs, die deine CI erzeugt, sodass das Inventar über Builds, Image-Rotationen und Abhängigkeitsdrift hinweg erhalten bleibt.

Wann SBOMs automatisch erzeugt werden

Es muss nichts installiert werden. fremforge erzeugt für jeden Pull Request zentral eine CycloneDX-1.6-SBOM auf seinen eigenen Runnern und bindet diese SBOM an ein Release, wenn du eines veröffentlichst, bei jedem Tag, einschließlich der üblichen SemVer- und CalVer-Formen (v1.2.3, v2026.06).

Beim Release scannen wir bewusst nicht erneut. Eine SBOM ergibt sich aus dem Quellbaum, und der Baum, den du veröffentlichst, wurde bereits als Head des Pull Requests gescannt, der ihn gemergt hat. Eine zweite SBOM wäre also eine byte-identische Datei. Stattdessen markiert das Veröffentlichen eines Releases die SBOM, die für genau diesen Commit bereits vorliegt, als Release-SBOM. Zwei Folgen solltest du kennen:

  • Es geht sofort. Es läuft kein Build, also steht die Release-SBOM bereit, sobald das Release veröffentlicht ist.
  • Die Bindung erfolgt auf dem Server. fremforge löst dein Tag über Forgejo zu seinem Commit auf und gleicht über diesen Commit ab, sodass nichts in einem Build eine Release-SBOM beanspruchen kann, die ihm nicht zusteht.

Veröffentlichst du ein Release für einen Commit, der nie durch einen Pull Request gegangen ist, etwa ein Tag auf einem sehr alten Commit oder in einem Repository, dessen Historie älter ist als das Scannen, gibt es keine SBOM zum Binden. Dieses Release wird als nicht abgedeckt gemeldet statt stillschweigend übersprungen; starte einen Scan für den Commit erneut, um es abzudecken.

Eine SBOM durch ein Release-Tag auslösen

Auf der Kommandozeile:

git tag v1.2.3
git push origin v1.2.3

In der Forgejo-Oberfläche: Öffne das Repository → Releases → Neues Release, setze einen Tag-Namen, der mit v beginnt (z. B. v1.2.3), und klicke auf Release veröffentlichen.

Eine einmalige SBOM für einen beliebigen Commit bekommst du auch über den Tab Actions → SBOM (Syft) → Run workflow.

Warum wird nur ein Release gebunden? Für jeden Pull Request wird eine SBOM erzeugt, aber nur die eines veröffentlichten Commits wird zur Release-SBOM: Prüfer wollen die SBOM „zu dem Zeitpunkt, als wir Release X ausgeliefert haben“. Die Bindung an ein Release hält die Zahl der Release-SBOMs proportional zu deinem tatsächlichen Release-Rhythmus.

Was fremforge annimmt

fremforge erzeugt SBOMs sowohl in CycloneDX 1.6 als auch in SPDX 2.3 und nimmt beide beim Ingest an:

  • CycloneDX JSON (1.6): das Inventar, das jede Funktion von fremforge liest (Release-Bindung, die Prüfung auf leeres Inventar, die Übersicht). Das breitere Tool-Ökosystem (Trivy, Syft, anchore) gibt CycloneDX nativ aus.
  • SPDX JSON (2.3): wird bei jedem PR-Scan im selben Syft-Lauf daneben erzeugt und unter demselben Build gespeichert, sodass die SBOM-Seite für ein Release beide Downloads anbietet. Deine eigenen Uploads dürfen CycloneDX 1.5–1.7 oder SPDX 2.x/3.x sein; der Ingest prüft specVersion / spdxVersion, bevor er speichert.

Das SPDX-Dokument ist ein Exportformat und wird nach bestem Bemühen erzeugt: Bei einem sehr großen Repository, dessen zwei Dokumente zusammen das Auslieferungslimit von 14 MiB überschreiten würden, wird nur die CycloneDX-SBOM gespeichert. So geht das Inventar nie verloren, um Platz für das zweite Format zu schaffen.

Jede SBOM wird gespeichert mit:

  • Dem SHA-256-Digest der SBOM-Datei. Der Uploader sendet ihn, und der Server berechnet ihn neu, sodass ein abgeschnittener oder veränderter Upload abgewiesen wird.
  • Einem optionalen in-toto-Attestierungsbündel (JSONL), mit Cosign signiert, wenn dein Workflow die SBOM signiert. Der Ingest prüft die JSONL-Form und die in-toto-Felder jeder Zeile, bevor er speichert.
  • Dem Artefaktnamen und der Version, genau so, wie dein Workflow sie benennt. Daraus wird der Schlüssel für die Deduplizierung gebildet (gleicher Digest + Name + Version = eine Zeile, nicht mehrere).

Harte Obergrenze von 5 MiB pro SBOM. Darüber antwortet der Ingest-Endpunkt mit 413 payload-too-large; teile große Monorepo-SBOMs nach Modulen auf, wenn du darauf stößt.

Eine SBOM aus der CI pushen

Das Runner-Image von fremforge enthält Syft und Trivy, sodass die meisten Workflows mit einem kurzen Ausschnitt auskommen:

- name: Generate + push SBOM
  run: |
    syft packages dir:. --output cyclonedx-json=sbom.json
    # The digest is the SHA-256 of the SBOM file itself; the server recomputes it
    # and refuses a mismatch with 400 digest-mismatch.
    DIGEST=$(sha256sum sbom.json | cut -d' ' -f1)
    curl -sS --fail \
      -H "Authorization: Bearer ${{ secrets.FREMFORGE_API_TOKEN }}" \
      -F "sbom=@sbom.json" \
      -F "artifact_name=myapp" \
      -F "artifact_version=${GITHUB_SHA}" \
      -F "sha256_digest=${DIGEST}" \
      -F "format=cyclonedx-json" \
      "https://frem.sh/_app/api/v1/orgs/${ORG}/sboms"

Der PAT braucht den Scope sboms:write; erstelle einen unter Organisationsverwaltung → API-Token und setze dabei den Haken beim Scope für den SBOM-Push.

Zwei optionale Felder halten fest, wo das Artefakt läuft und aus welchem Repository es gebaut wurde:

  • environment: die Umgebungsklasse, in der das Artefakt läuft, eine von sandbox, development, test oder production. Alles andere wird mit invalid-environment abgewiesen.
  • source_repo: das Repository, aus dem das Artefakt gebaut wurde, als owner/repo. Alles andere wird mit invalid-source-repo abgewiesen.
      -F "environment=production" \
      -F "source_repo=acme/payments-api" \

Keines der beiden Felder ändert die Aufbewahrung oder ein Gate. Eine CycloneDX-SBOM mit source_repo stellt außerdem ihren Abhängigkeitsgraphen für die Abhängigkeitspfade dieses Repositorys bereit; auf diesem Weg bekommen Maven- und Gradle-Repositorys sie.

Wo SBOMs in der Admin-Oberfläche erscheinen

Admin → Sicherheit › Komponenten › SBOMs listet jede aufgenommene SBOM auf, gruppiert nach Artefakt. Jede Zeile zeigt:

  • Artefaktname + Version + SHA-256-Digest
  • Format (CycloneDX / SPDX) + Größe
  • Zeitpunkt des Ingests + den Benutzer bzw. PAT, der sie gepusht hat
  • einen Link „Signatur prüfen“, wenn eine Attestierung angehängt ist

Wie SBOMs mit anderen Sicherheitssignalen zusammenwirken

  • Scorecard: bestimmt die Werte Dependencies und Vulnerabilities in der Berechnung des OpenSSF Scorecard.
  • OSV-Empfehlungen: Passt eine OSV-Advisory zu einer Komponente, die in einer SBOM aufgeführt ist, erscheint die Empfehlung mit der an die SBOM gebundenen Auswirkung („betrifft Build myapp@v1.2.3 vom 2026-05-12“).
  • Cosign: Das Signieren von SBOMs nutzt dieselbe Cosign-Vertrauensbasis wie das Signieren von Images. Den Prüfweg findest du unter Cosign.

Aufbewahrung

Release-SBOMs bleiben erhalten, solange deine Organisation besteht. SBOMs von Builds, die nicht veröffentlicht wurden, werden nach deiner Aufbewahrung von Nachweisen für sbom gelöscht: standardmäßig 14 Tage, 0 bis 3650 Tage. Die SBOM des Heads des Standard-Branches, die SBOMs aktueller Images und die SBOMs von Paketdateien bleiben unabhängig von ihrem Alter erhalten. Solange eine Aufbewahrungspflicht (Legal Hold) besteht, wird nichts gelöscht. (Bis zum 2026-10-08 stand in diesem Abschnitt, dass SBOMs den Voreinstellungen der Audit-Aufbewahrung folgen; das war nie der Fall.)

Siehe auch

  • Cosign: Signieren von Images und SBOMs.
  • OSV-Empfehlungen: Schwachstellen, die SBOM-Komponenten zugeordnet sind.
  • Scorecard: SBOMs bestimmen die Werte für Abhängigkeiten und Schwachstellen.
  • API-Referenz: der Ingest-Endpunkt POST /_app/api/v1/orgs/{slug}/sboms.