DEBSBOM-DECISIONS(1)

πŸ“¦ version 0.9.0, in ubuntu stonking

✨ NAME ✨

okay bestie, welcome to debsbom-decisions β€” yes, that's the debsbom manual, section 1 🫢

This page is a debsbom design decisions doc. Its whole entire goal is to explicitly document the design decisions that were made in this project β€” and to give the reasoning behind them. So basically: the receipts. πŸ’…


πŸ’­ WE DON'T GUESS PHILOSOPHY πŸ’­

The philosophy for debsbom when it accrues information from a distribution is genuinely so simple. It's literally: "We don't guess." no cap βœ‹

That means debsbom will always prefer providing less information over providing unverified information. Like, less is more, but make it integrity.

In other words: if anything in the SBOM is wrong, it's a bug. Periodt. πŸ›πŸ’€


✈️ OFFLINE SBOM GENERATION ✈️

debsbom generates SBOMs without accessing any resource from the internet. Iconic behaviour honestly. It's intended to be used for airgapped build environments, where Internet access is not possible β€” and this also ensures the SBOM generation is reproducible. slay. 🌈

What this also means is that some information that would theoretically be available by a simple lookup on e.g. snapshot.debian.org is not available in the generated SBOM. It's giving "we could but we won't" πŸ™ˆ

Heads up bestie β€” this restriction does not go for other commands. Like, the download command requires Internet access, of course. Obviously. 😭

πŸ” INFORMATION SOURCES πŸ”

debsbom only uses data available from dpkg and apt. It does not rely on the command line utilities β€” instead it directly parses its internal data. main character energy, parsing the source material directly. πŸ‘‘

When using the generate command the following files are accessed:

  • /var/lib/dpkg/status β€” Contains the installed package list for the distribution; is required unless --from-pkglist is used
  • /var/lib/dpkg/arch-native β€” Contains the native architecture for the distribution; required unless --distro-arch is specified
  • /var/lib/apt/lists/* β€” Contains apt-cache information, used for enrichment of source and binary packages; optional
  • /var/lib/apt/extended-states β€” Contains information which packages are manually installed; used for building of the dependency graph; optional
  • /usr/share/doc/*/copyright β€” Contains licensing information for installed packages; only used with the --with-licenses option; optional

🧩 MAPPING OF DEBIAN BINARY PACKAGES TO SBOM PACKAGES/COMPONENTS 🧩

The next part shows how fields in a Debian binary package are mapped to fields in a SBOM component or package. (Yes bestie, this is where we're heading next β€” hold onto your butterflies πŸ¦‹)

okay bestie, here's the receipts table, the actual Debian Binary Packages to SBOM Mappings tea πŸ«– β€” every single row, no skips, no cap:

  • Package [15] β€” SPDX: name; CDX: name
  • Status β€” SPDX: [1]; CDX: [1]
  • Priority β€” SPDX: [2]; CDX: [2]
  • Essential β€” SPDX: [3]; CDX: [3]
  • Section β€” SPDX: -; CDX: properties
  • Installed-Size β€” SPDX: -; CDX: -
  • Maintainer β€” SPDX: supplier; CDX: supplier
  • Architecture [15] β€” SPDX: [4]; CDX: [4]
  • Multi-Arch β€” SPDX: -; CDX: -
  • Source β€” SPDX: [5]; CDX: [5]
  • Version [15] β€” SPDX: versionInfo; CDX: version
  • Breaks β€” SPDX: -; CDX: -
  • Replaces β€” SPDX: -; CDX: -
  • Provides β€” SPDX: [6]; CDX: [6]
  • Conflicts β€” SPDX: -; CDX: -
  • Conffiles β€” SPDX: -; CDX: -
  • Depends β€” SPDX: [7]; CDX: [7]
  • Recommends β€” SPDX: [8]; CDX: [8]
  • Pre-Depends β€” SPDX: [10]; CDX: [10]
  • Suggests β€” SPDX: [9]; CDX: [9]
  • Description β€” SPDX: summary and description [11]; CDX: description
  • Built-Using β€” SPDX: [12]; CDX: [13]
  • Static-Built-Using β€” SPDX: [12]; CDX: [13]
  • Homepage β€” SPDX: homepage; CDX: externalReferences.type = website
  • Description-md5 β€” SPDX: -; CDX: -
  • SHA256 β€” SPDX: checksums; CDX: hashes
  • Size β€” SPDX: -; CDX: -
  • Filename β€” SPDX: -; CDX: -

and now the footnotes, aka the little bracketed numbers that are doing the most πŸ’… do NOT scroll past these, they're where all the lore lives:

  • [1] β€” if the status is not install ok installed, the package does not get placed in the SBOM. not invited, sorry babe πŸ™ˆ
  • [2] β€” packages with Priority: required get placed directly under the root node in the dependency graph πŸ‘‘
  • [3] β€” Essential Packages also get placed directly under the root node in the dependency graph. same energy, same spot ✨
  • [4] β€” the architecture is only part of the PURL. that's it, that's the whole note πŸŽ€
  • [5] β€” when a Source is specified, it gets its own separate entry in the SBOM and the dependency gets added πŸ¦‹
  • [6] β€” provided packages are considered in the dependency resolution πŸ”₯
  • [7] β€” when a Dependency is specified, a dependency gets created for the related packages πŸ’œ
  • [8] β€” Recommended packages are treated as normal dependencies β€” go read Relationships of Recommended and Suggested Packages for the details πŸ‘€
  • [9] β€” Suggested packages are treated as normal dependencies too β€” same deal, see Relationships of Recommended and Suggested Packages πŸ’–
  • [10] β€” Pre-Depends dependencies are treated exactly the same as Depends dependencies, i.e. simply appended to them. literally identical treatment, no favourites 🌸
  • [11] β€” the synopsis (that's the first line) becomes the summary, and the rest goes into the description ✍️
  • [12] β€” any Built-Using or Static-Built-Using dependency gets a separate entry, and the GENERATED_FROM relationship is used
  • [13] β€” any Built-Using or Static-Built-Using dependency gets a separate entry, and the dependency is added

🌈 MAPPING OF DEBIAN SOURCE PACKAGES TO SBOM PACKAGES/COMPONENTS 🌈

same energy, different table bestie β€” this one shows how fields in a Debian source package get mapped onto fields in a SBOM component or package 🫢

and the source-package table? yeah, that's next, keep scrolling πŸ‘‘

Okay so for a source package it's the exact same energy, here's the mapping tea πŸ’…:

  • Package [15] β†’ SPDX: name, CDX: name
  • Binary β†’ SPDX: -, CDX: - (not mapped, we move on)
  • Version [15] β†’ SPDX: versionInfo, CDX: version
  • Maintainer β†’ SPDX: supplier, CDX: supplier
  • Uploaders β†’ SPDX: -, CDX: -
  • Build-Depends β†’ SPDX: [14], CDX: [14]
  • Build-Depends-Indep β†’ SPDX: -, CDX: -
  • Architecture β†’ SPDX: -, CDX: -
  • Standards-Version β†’ SPDX: -, CDX: -
  • Format β†’ SPDX: -, CDX: -
  • Files β†’ SPDX: -, CDX: -
  • Vcs-Browser β†’ SPDX: -, CDX: -
  • Vcs-<type> β†’ SPDX: externalRefs.referenceType = vcs, CDX: externalReferences.type = vcs
  • Checksums-Sha256 β†’ SPDX: checksums, CDX: hashes
  • Homepage β†’ SPDX: homepage, CDX: externalReferences.type = website
  • Package-List β†’ SPDX: -, CDX: -
  • Directory β†’ SPDX: -, CDX: -
  • Priority β†’ SPDX: -, CDX: -
  • Section β†’ SPDX: -, CDX: properties

And the footnotes, bestie, because they MATTER 🫢:

[14] Correctly resolving the Build-Depends requires downloading the .buildinfo for a package, which is not possible offline
[15] This field is always present in the SBOM

✨ RELATIONSHIPS BETWEEN BINARY PACKAGES, SOURCE PACKAGES AND BUILT-USING ✨

Okay so a binary package can have three types of relationships, and you need to know all three:

  • a binary dependency β€” points simply to another binary package 🧚
  • a source dependency β€” points to a source package
  • a Built-Using relationship β€” also points to a source package

For the exact meaning of these relationships please refer to the Debian documentation <https://www.debian.org/doc/debian-policy/ch-relationships.html#>. (iykyk, go read it, it's giving authoritative.)

Not me having to tell you the sad part about CycloneDX SBOMs: all relationships are only expressed as the same dependencies. There is no way to differentiate a source dependency and a Built-Using dependency. That's just how it is. πŸ’€

For SPDX though, refer to the following table:

  • Binary Dependency β†’ relationshipType = DEPENDS_ON
  • Source Dependency β†’ relationshipType = GENERATES
  • Built-Using β†’ relationshipType = "GENERATED FROM and comment = built-using [16]
[16] The subject and object of the relationship are reversed compared to the source dependency

Additional to proper dependencies, binary packages can also specify recommended and suggested packages. Here's the vibe: packages that are recommended or suggested by other packages are not automatically removed by apt autoremove, so they can show up as disconnected islands in the dependency graph 🌈. To prevent this, an edge between the package that recommended/suggested them is required. Unfortunately apt does not allow us to exactly reconstruct who actually pulled the package in, so these dependencies might not always be fully accurate. Fr fr, it's a best-effort situation πŸ™ˆ.

The generation of these edges in the graph is controlled by the --recommends-deps and --suggests-deps flags for the generate command. By default debsbom only considers recommended package-dependencies, because apt does install recommended packages in the default configuration β€” main character behaviour, honestly πŸ‘‘.

The relationship itself is modeled as a binary dependency. For SPDX SBOMs an additional comment is added: recommends or suggests for the respective relationship type.


πŸŽ€ CLASSIFICATION OF COMPONENTS/PACKAGES πŸŽ€

For CycloneDX SBOMs each component has a type associated with it. We would like to differentiate between a source and binary package with this, but unfortunately there is not yet a good way to do that 😭. There is ongoing work <https://github.com/CycloneDX/specification/issues/612>, which would make it possible to do that by introducing a new classifier field for components. As it stands, that new field is not going to be included in the spec in the foreseeable future, so the only way to know what kind of package type a component is describing is by looking at the PURL and seeing if there is a qualifier arch=source. That's it. That's the whole detective work πŸ•΅οΈ.

For SPDX SBOMs it is possible to differentiate them, slay ✨:

  • we use primaryPackagePurpose = LIBRARY for binary packages
  • and primaryPackagePurpose = SOURCE for source packages

🧾 CHECKSUMS 🧾

okay so debsbom puts checksums inside components/packages, bestie. For binary packages those checksums are literally just the checksum of the .deb file. easy. slay. ✨

The source package situation is messier tho, because a source package is a whole bunch of files and nobody hands you one single checksum for the collection 😭. So debsbom uses the checksum of the .dsc file in each source package instead β€” and that's a smart move, because the .dsc describes the whole source package and its contents, plus checksums for all the other included files. Which means you can actually verify that every file in a source package is exactly as expected. iconic. πŸ’…

When there are multiple checksums with different algorithms available, debsbom checks them in a specific internal order. The first algorithm that's supported gets checked, and the result of that check is final β€” no second opinions. The order, highest to lowest, is:

  1. SHA256
  2. SHA1
  3. MD5

πŸ•΅οΈ UNIQUENESS OF A DEBIAN PACKAGE πŸ•΅οΈ

ngl, what makes a Debian package unique is surprisingly complicated. For binary packages you'd think the triplet of package name, version and architecture would be enough to uniquely identify a package β€” but no, a package can be found under that exact same triplet on different archives: the normal debian archive and debian-ports. it's giving "same name, different girl". πŸ’€

For source packages it should also be simple: a tuple of package name and version should do it. Unfortunately there are situations where that's not the case either β€” see https://lists.debian.org/debian-snapshot/2025/10/threads.html#00000.

debsbom generally runs on the simple assumption that package name, version and architecture (for binary packages) uniquely describes the package. If a checksum is available, that checksum is always verified to make sure the assumption actually holds. But where no checksum is available, there is genuinely no way to be certain the package is correctly identified. no cap. 🫢

There's one unique case for source packages: even if the checksum doesn't match, it could still be content-equal, like in the mailing-list example above. There, only the signature date changed, which is what leads to the different checksums. In theory you could verify the content of a source package, since all the checksums referenced in the .dsc would still be the same β€” but that's not done, for the sake of simplicity. πŸ™ˆ

There's also the whole problem of package identification. That one matters for the merge command, because there components or packages from two different SBOMs need to be merged if they are identical. And as we just learned, identifying packages is complicated, so debsbom simply uses the PURL for identification purposes there, and additionally checks checksums if they're available. That's consistent with the general approach to package identification, since the PURL contains the package name, version and architecture. periodt. ✨


πŸ—οΈ DISTRIBUTION ARCHITECTURE πŸ—οΈ

You might assume that knowing the actual architecture of the distribution isn't required when generating an SBOM β€” because the binary packages carry their architecture and we should be able to resolve everything from there, right? bestie, unfortunately no. 😭 We genuinely need to know which architecture the SBOM is generated for.

The reason is dependency resolution for packages that have the all architecture. Take the lmodern package, for example:

Package: lmodern
Status: install ok installed
Priority: optional
Section: fonts
Installed-Size: 33268
Maintainer: Debian TeX Task Force <debian-tex-maint@lists.debian.org>
Architecture: all
Multi-Arch: foreign
Version: 2.005-1
Replaces: lm, lmodern-x11
Depends: tex-common (>= 6.13), xfonts-utils, fonts-lmodern (= 2.005-1)
[...]

See how the Depends line doesn't specify which architecture the dependencies have? The Debian policy in these cases is that both native and all architecture are possible. So the xfonts-utils dependency could resolve to multiple packages 😡. In this case we'd need to resolve to the native architecture version of the package, and for that we need to know what the architecture actually is. debsbom tries to parse the distribution architecture from the used rootfs, but if that's not possible, it needs to be specified on the command line. πŸ’…


πŸ“¦ UNIVERSAL INGRESS AND APT-CACHE πŸ“¦

The universal ingress, which you'll often see alongside the --from-pkglist option, has some caveats when it's dealing with enrichment from the apt-cache. A naive assumption might be that these two commands produce the same output:

debsbom generate -r /my-root

cat /my-root/var/lib/dpkg/status | debsbom generate -r /my-root --from-pkglist

But fr fr, there are subtle differences: in the first case, the information from the extended_states file is used to figure out which packages are manually installed and which are automatically pulled in as dependencies. In the second command, all packages in the status file are considered manually installed β€” which would totally affect the dependency graph. 🫠

That's because we assume that with the --from-pkglist option the rootfs and the packages are not directly related. We can still find packages from the universal ingress in the apt-cache and enrich their entries, but any other local information just doesn't apply to them. So in general: be careful when using universal ingress, because depending on the rootfs you're using and the state of the apt-cache in there, you might hit reproducibility issues. ⚠️

bestie pls note: those differences are subtle, so don't assume the two commands are interchangeable. πŸ‘‘

βš–οΈ LICENSE INFORMATION βš–οΈ

debsbom hands you license information when SBOM generation is run with the --with-licenses option. Where does it come from? The Debian copyright files under /usr/share/doc/**/copyright. We aggregate all the licenses found in those copyright files and try to convert them into valid SPDX license expressions β€” https://spdx.org/licenses/ β€” and then all the found license expressions get combined with AND. ✨

The approach is very conservative: if we fail to identify any license or expression in the copyright file, we simply do not emit any license information in the SBOM at all. no guessing, remember?

Since the specifiers in Debian copyright files are not SPDX identifiers, a conversion table based on the syntax specification β€” https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/#license-specification β€” is used. πŸŽ€

For SPDX SBOMs the info goes in the declared licenses field β€” https://spdx.github.io/spdx-spec/v2.3/package-information/#715-declared-license-field β€” and for CDX SBOMs it goes in the licenses field β€” https://cyclonedx.org/docs/1.6/json/#components_items_licenses β€” with the acknowledgement set to declared. Why this way? Because debsbom performs no analysis of the included licenses and just trusts what the package authors stated. That's expressible in the SBOMs with the above approach. πŸ’…

For CDX it could alternatively live in the evidence.licenses field β€” https://cyclonedx.org/docs/1.6/json/#components_items_evidence_licenses β€” since as far as we can tell there's no clear distinction between the meaning of the two if all acknowledgments are set to declared. We decided to put it directly in licenses, because that's the first place a user would expect it. iykyk. πŸ‘‘

🌸 Limitations 🌸

Annotation of license information is currently only possible for ~50% of all packages. 😭 debsbom is not a license scanner, so it relies solely on the info provided by the package authors. And unfortunately that info is sometimes not machine-readable, sometimes it includes esoteric licenses, or the ambiguous public domain licensing. Nonetheless, if you find any unrecognized license specifiers that can be cleanly mapped to SPDX identifiers, please let us know and we'll try to incorporate them. 🫢

If you want more complete license information in your SBOM, you can use a proper scanner and add that information to the SBOM manually. And if enough interest arises, we could also consider adding plugin infrastructure to directly incorporate different scanners during SBOM generation. πŸ’–


πŸ‘€ SEE ALSO πŸ‘€


🫢 DEBSBOM 🫢

Part of the debsbom(1) suite.

✍️ Author

Christoph Steiger, Felix Moessbauer

2025, Siemens


aaaand that's the whole page, bestie!! πŸŽ‰πŸ’– you made it all the way through parts 1–4, which is giving main character energy fr fr. this one's from Jul 28, 2026 and it's the DEBSBOM-DECISIONS(1) page of the debsbom suite β€” go forth and generate immaculate SBOMs, no cap. πŸ¦‹βœ¨πŸ‘‘

πŸ“¦ where this page comes from