Quellcode scannen

kunnus sbom repo durchläuft einen Quellcode-Baum, erkennt jedes vorhandene Paket-Ökosystem und erzeugt eine einzige CycloneDX-1.6-SBOM für alle. Keine Konfigurationsdatei, kein Setup pro Sprache.

shell
cd my-project
kunnus sbom repo --output sbom.cdx.json

Ohne --output geht die SBOM nach stdout und lässt sich pipen; alle Logausgaben gehen nach stderr. Das Pfad-Argument ist optional und zeigt standardmäßig auf das aktuelle Verzeichnis — kunnus sbom repo ~/src/firmware funktioniert genauso.

Was erkannt wird

Die Erkennung ist markerbasiert: kunnus sucht die Manifest- und Lockfiles jedes Ökosystems (package.json, go.mod, Cargo.lock, pom.xml, requirements.txt, …) und aktiviert die passenden Extraktoren. Ein Scan deckt über 20 Sprach-Ökosysteme ab — npm/pnpm/yarn/bun, Python (requirements, poetry, pdm, Pipfile, uv, conda), Go, Rust, Java (Maven, Gradle), .NET, PHP, Ruby, Swift (SwiftPM, CocoaPods), Haskell, Lua, R und C/C++ (Conan) — plus Embedded-Firmware-Manifeste und vendorte C/C++-Bibliotheksverzeichnisse.

Monorepos brauchen nichts Besonderes: Jedes irgendwo im Baum gefundene Ökosystem landet in derselben SBOM, und doppelte Deklarationen über Teilprojekte hinweg kollabieren in der Deduplizierung.

Scan einschränken

Um nur bestimmte Ökosysteme zu scannen, nutzen Sie --ecosystem (wiederholbar). Einzelne scalibr-Plugins lassen sich mit --enable / --disable hinzufügen oder entfernen.

shell
kunnus sbom repo --ecosystem npm --ecosystem python

Hashes und Lizenzen

Wo ein Lockfile Content-Digests enthält (npm, yarn, pnpm, bun, poetry, uv, Cargo, Conan, ESP-IDF, …), übernimmt kunnus sie als Komponenten-Hashes in die SBOM — eine Anforderung der BSI TR-03183-2. Lizenzen stammen aus offline verfügbaren Paketdaten; für Ökosysteme ohne Lizenzangaben im Lockfile (Go, Rust, .NET) schließt das Opt-in-Flag --online-licenses die Lücke via deps.dev. Dieses Flag ist das einzige Scan-Feature mit Netzwerkzugriff.

Abhängigkeitsbeziehungen

Wo ein Lockfile den aufgelösten Graphen pinnt, gewinnt kunnus die echten Kanten zwischen den Komponenten und schreibt sie in das CycloneDX-Feld dependencies[] — das Element „Component Dependency Relationship“ der CISA-Mindestangaben, nicht bloß eine flache Liste. Sechs Lockfile-Formate tragen diese Daten heute: Cargo.lock, composer.lock, Gemfile.lock, package-lock.json / npm-shrinkwrap.json (pfad-basierte v2/v3-Maps; v1 wird nicht geparst), packages.lock.json (NuGet) und renv.lock. Alles andere steuert nur die Zugehörigkeit zur Root-Komponente bei — und die SBOM erklärt diese Unvollständigkeit in compositions[], statt einen Graphen vorzutäuschen, den sie nicht hat. Eine Kante, deren Ziel zu keiner Komponente im Dokument passt, wird verworfen; eine Referenz wird nie erfunden.

Deklarierte Ranges vs. Lockfile-Pins

Bei Cargo, NuGet und Python deklariert das Manifest einen Constraint (>=2.0, [3.0.0,4.0.0)), während das Lockfile daneben pinnt, was tatsächlich aufgelöst wurde. Beides zu emittieren zählt die Komponente doppelt und erfindet ein Phantom mit einer nie veröffentlichten Version und einer CPE, die auf kein Advisory passt — bei einem realen Rust-Workspace waren das 74 zusätzliche Komponenten. Der aufgelöste Pin gewinnt, der deklarierte Zwilling entfällt — aber nur so weit, wie die Autorität des Lockfiles reicht: Ein Cargo.lock, das jedes Manifest im Workspace abdeckt, deaktiviert den Manifest-Extraktor komplett; bei .NET und Python entfällt die Deklaration pro Pfad, und nur dann, wenn das Manifest unter einem Lockfile-Verzeichnis liegt, dessen Lockfile denselben Namen pinnt. Ist ein Projekt gelockt und ein anderes nicht, behält das ungelockte seine Deklarationen.

Die vollständige Flag-Referenz liegt im Repository: docs/cli.md auf GitHub.