Ein SBOM sagt dir, was in einem Artefakt steckt. Wirklich nützlich wird es aber selten beim einzelnen Stand, sondern beim Vergleich. Was hat sich zwischen Release 1.4.0 und 1.5.0 in der Lieferkette geändert? Welche Abhängigkeit ist dazugekommen, welche ist rausgeflogen, welche wurde still auf eine neue Version gehoben? Genau das beantwortet ein SBOM-Diff, und er lässt sich in wenigen Zeilen automatisieren.
Der Reiz liegt im Signal. Ein Changelog erzählt dir, was das Team bewusst geändert hat. Ein SBOM-Diff zeigt dir, was tatsächlich im Image gelandet ist, inklusive transitiver Abhängigkeiten und OS-Paketen, die niemand aktiv angefasst hat.
Zwei SBOMs erzeugen
Ich nehme Syft und lasse für beide Image-Tags ein CycloneDX-SBOM im JSON-Format schreiben. Wichtig ist, dass beide mit demselben Werkzeug und demselben Format erzeugt werden, sonst vergleichst du am Ende Formatunterschiede statt echter Änderungen.
syft scan ghcr.io/acme/api:1.4.0 -o cyclonedx-json=sbom-1.4.0.jsonsyft scan ghcr.io/acme/api:1.5.0 -o cyclonedx-json=sbom-1.5.0.json
Den Diff mit jq bilden
Der vendor-neutrale Weg braucht kein Spezialwerkzeug. Ich ziehe aus jedem SBOM die Menge aus Name und Version, sortiere sie und lasse comm die Differenz bilden.
jq -r '.components[] | "\(.name)@\(.version)"' sbom-1.4.0.json | sort > comps-1.4.0.txtjq -r '.components[] | "\(.name)@\(.version)"' sbom-1.5.0.json | sort > comps-1.5.0.txt# neu in 1.5.0comm -13 comps-1.4.0.txt comps-1.5.0.txt# entfernt in 1.5.0comm -23 comps-1.4.0.txt comps-1.5.0.txt
Ein Versions-Bump taucht in beiden Listen auf, einmal die alte Zeile als entfernt und einmal die neue als hinzugekommen. Für einen Überblick reicht das. Wer sauber nach echten Upgrades gruppieren will, joint über den reinen Namen.
Der zweckgebaute Weg mit cyclonedx-cli
Wenn du ohnehin bei CycloneDX bleibst, nimmt dir die cyclonedx-cli die Gruppierung ab. Sie kennt das Schema und zeigt Versionsänderungen direkt als solche an.
cyclonedx diff sbom-1.4.0.json sbom-1.5.0.json --component-versions
Vom Komponenten-Diff zum Risiko-Diff
Interessanter als die reine Paketliste ist die Frage, ob sich das Risiko verschoben hat. Dafür lasse ich Grype beide SBOMs scannen und vergleiche die CVE-Mengen. So sehe ich, welche Schwachstellen neu dazugekommen sind und welche das Release geschlossen hat.
grype sbom:sbom-1.4.0.json -o json > vulns-1.4.0.jsongrype sbom:sbom-1.5.0.json -o json > vulns-1.5.0.jsonjq -r '.matches[].vulnerability.id' vulns-1.4.0.json | sort -u > cves-1.4.0.txtjq -r '.matches[].vulnerability.id' vulns-1.5.0.json | sort -u > cves-1.5.0.txt# neu eingeschlepptcomm -13 cves-1.4.0.txt cves-1.5.0.txt# behobencomm -23 cves-1.4.0.txt cves-1.5.0.txt
In die Pipeline einbauen
Der Diff entfaltet seinen Wert, wenn er bei jedem Release automatisch läuft. Ich hänge ihn an den Build, speichere beide SBOMs als Artefakt und lasse den Job eine Warnung ausgeben, sobald eine neue kritische Komponente oder ein neuer CVE auftaucht. Das gibt dem Review einen konkreten Anhaltspunkt statt eines diffusen Bauchgefühls.
NEU=$(comm -13 cves-1.4.0.txt cves-1.5.0.txt)if [ -n "$NEU" ]; then echo "Neue Schwachstellen in diesem Release:" echo "$NEU"fi
Ob das den Build bricht oder nur meldet, ist eine Policy-Entscheidung. Am Anfang reicht das Melden. Sobald das Team dem Signal vertraut, kann ein neuer kritischer CVE ohne Ausnahme den Merge blockieren.
Grenzen
Der Diff ist nur so gut wie das SBOM darunter. Wenn Syft eine transitive Abhängigkeit nicht sauber auflöst, fehlt sie in beiden Ständen und der Diff schweigt. Unterschiedliche Namensschemata zwischen Scannern oder Ökosystemen führen zu Phantom-Änderungen, deshalb immer dasselbe Werkzeug verwenden. Und ein Versions-Bump ohne funktionale Änderung erzeugt Rauschen, das man wegfiltern muss, bevor der Diff im Review ankommt.
Trotzdem ist das Verhältnis von Aufwand zu Erkenntnis kaum zu schlagen. Zwei Scans, ein paar Zeilen jq, und du weißt genau, was sich in der Lieferkette bewegt hat. Wie sich das mit Signaturen und Provenance zu einer belastbaren Lieferkette zusammensetzt, habe ich auf digital-business.blog ausführlicher beschrieben.
Hinterlasse einen Kommentar