[PATCH 0/3] Add SBOM generation with debsbom

98 views
Skip to first unread message

Christoph Steiger

unread,
Sep 9, 2025, 4:05:44 AMSep 9
to isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com, Christoph Steiger
This patchset adds proper SBOM generation in the two standard formats
SPDX and CycloneDX during the rootfs generation process.

The generation is itself is handled by a SBOM generator `debsbom` [1]
which is developed as an open source project at Siemens. It is still
early in development, but it has enough features for what we require
in isar. The required dependencies which are not yet available as
Debian packages were minimally packaged directly in isar too.

This is a followup of the previous RFC [2]. Since then the series has
changed a lot. The SBOM generation was moved from a simple OE lib to
`debsbom`. This also meant the introduction of a separate chroot was
necessary. The SBOM generation process was also moved from the image
step to the rootfs step, along with a lot of minor changes and
improvements.

[1] https://github.com/siemens/debsbom
[2] https://groups.google.com/g/isar-users/c/8L-CF4BJY0I/m/p0N3o_zfAAAJ


Christoph Steiger (3):
meta: package python libraries for SBOM generation
meta: package python3-debsbom
meta: add SBOM generation with debsbom

meta/classes/image.bbclass | 2 +-
meta/classes/rootfs.bbclass | 6 +-
meta/classes/sbom.bbclass | 60 +++++++++++++++++++
meta/classes/sdk.bbclass | 2 +-
.../sbom-chroot/sbom-chroot.bb | 31 ++++++++++
.../python3-beartype/files/rules | 8 +++
.../python3-beartype_0.19.0.bb | 29 +++++++++
.../files/pybuild.testfiles | 1 +
.../python3-cyclonedx-python-lib/files/rules | 8 +++
.../python3-cyclonedx-python-lib_9.1.0.bb | 56 +++++++++++++++++
...icense-description-in-pyproject.toml.patch | 28 +++++++++
.../python3-debsbom/files/rules | 8 +++
.../python3-debsbom/python3-debsbom_0.0.1.bb | 54 +++++++++++++++++
.../python3-packageurl-python/files/rules | 8 +++
.../python3-packageurl-python_0.16.0.bb | 33 ++++++++++
.../python3-py-serializable/files/rules | 8 +++
.../python3-py-serializable_2.0.0.bb | 42 +++++++++++++
.../python3-spdx-tools/files/rules | 25 ++++++++
.../python3-spdx-tools_0.8.3.bb | 56 +++++++++++++++++
19 files changed, 462 insertions(+), 3 deletions(-)
create mode 100644 meta/classes/sbom.bbclass
create mode 100644 meta/recipes-devtools/sbom-chroot/sbom-chroot.bb
create mode 100644 meta/recipes-support/python3-beartype/files/rules
create mode 100644 meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
create mode 100644 meta/recipes-support/python3-cyclonedx-python-lib/files/pybuild.testfiles
create mode 100644 meta/recipes-support/python3-cyclonedx-python-lib/files/rules
create mode 100644 meta/recipes-support/python3-cyclonedx-python-lib/python3-cyclonedx-python-lib_9.1.0.bb
create mode 100644 meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch
create mode 100644 meta/recipes-support/python3-debsbom/files/rules
create mode 100644 meta/recipes-support/python3-debsbom/python3-debsbom_0.0.1.bb
create mode 100644 meta/recipes-support/python3-packageurl-python/files/rules
create mode 100644 meta/recipes-support/python3-packageurl-python/python3-packageurl-python_0.16.0.bb
create mode 100644 meta/recipes-support/python3-py-serializable/files/rules
create mode 100644 meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
create mode 100644 meta/recipes-support/python3-spdx-tools/files/rules
create mode 100644 meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb

--
2.39.5

Christoph Steiger

unread,
Sep 9, 2025, 4:05:47 AMSep 9
to isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com, Christoph Steiger
Package python libraries for SBOM generation in isar. The packages are
unfortunately not (yet) packaged in Debian, thats why we need to do it
here. With these libraries it is now possible to easily create CDX and
SPDX SBOMs in different file formats.

Signed-off-by: Christoph Steiger <christop...@siemens.com>
---
.../python3-beartype/files/rules | 8 +++
.../python3-beartype_0.19.0.bb | 29 ++++++++++
.../files/pybuild.testfiles | 1 +
.../python3-cyclonedx-python-lib/files/rules | 8 +++
.../python3-cyclonedx-python-lib_9.1.0.bb | 56 +++++++++++++++++++
.../python3-packageurl-python/files/rules | 8 +++
.../python3-packageurl-python_0.16.0.bb | 33 +++++++++++
.../python3-py-serializable/files/rules | 8 +++
.../python3-py-serializable_2.0.0.bb | 42 ++++++++++++++
.../python3-spdx-tools/files/rules | 25 +++++++++
.../python3-spdx-tools_0.8.3.bb | 56 +++++++++++++++++++
11 files changed, 274 insertions(+)
create mode 100644 meta/recipes-support/python3-beartype/files/rules
create mode 100644 meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
create mode 100644 meta/recipes-support/python3-cyclonedx-python-lib/files/pybuild.testfiles
create mode 100644 meta/recipes-support/python3-cyclonedx-python-lib/files/rules
create mode 100644 meta/recipes-support/python3-cyclonedx-python-lib/python3-cyclonedx-python-lib_9.1.0.bb
create mode 100644 meta/recipes-support/python3-packageurl-python/files/rules
create mode 100644 meta/recipes-support/python3-packageurl-python/python3-packageurl-python_0.16.0.bb
create mode 100644 meta/recipes-support/python3-py-serializable/files/rules
create mode 100644 meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
create mode 100644 meta/recipes-support/python3-spdx-tools/files/rules
create mode 100644 meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb

diff --git a/meta/recipes-support/python3-beartype/files/rules b/meta/recipes-support/python3-beartype/files/rules
new file mode 100644
index 00000000..0ca517a1
--- /dev/null
+++ b/meta/recipes-support/python3-beartype/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = beartype
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb b/meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
new file mode 100644
index 00000000..34f56b30
--- /dev/null
+++ b/meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
@@ -0,0 +1,29 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/beartype-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), dh-python, python3-all, python3-setuptools, pybuild-plugin-pyproject, python3-hatchling"
+DEBIAN_DEPENDS = "python3"
+# this is 01/01/1980, any earlier and zip in the wheel building process will not accept it
+DEBIAN_CHANGELOG_TIMESTAMP = "315532800"
+DESCRIPTION = "Unbearably fast near-real-time hybrid runtime-static type-checking in pure Python."
+
+SRC_URI = "\
+ https://github.com/beartype/beartype/archive/refs/tags/v0.19.0.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "e7ad00eebf527d60f30e0b391209b561dabd2074b608c50e26c94c2d8250a6cd"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-cyclonedx-python-lib/files/pybuild.testfiles b/meta/recipes-support/python3-cyclonedx-python-lib/files/pybuild.testfiles
new file mode 100644
index 00000000..cc736a36
--- /dev/null
+++ b/meta/recipes-support/python3-cyclonedx-python-lib/files/pybuild.testfiles
@@ -0,0 +1 @@
+pyproject.toml
diff --git a/meta/recipes-support/python3-cyclonedx-python-lib/files/rules b/meta/recipes-support/python3-cyclonedx-python-lib/files/rules
new file mode 100644
index 00000000..fe72dd1a
--- /dev/null
+++ b/meta/recipes-support/python3-cyclonedx-python-lib/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = cyclonedx-python-lib
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-cyclonedx-python-lib/python3-cyclonedx-python-lib_9.1.0.bb b/meta/recipes-support/python3-cyclonedx-python-lib/python3-cyclonedx-python-lib_9.1.0.bb
new file mode 100644
index 00000000..62c23476
--- /dev/null
+++ b/meta/recipes-support/python3-cyclonedx-python-lib/python3-cyclonedx-python-lib_9.1.0.bb
@@ -0,0 +1,56 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+DEPENDS = "python3-packageurl-python python3-py-serializable"
+
+S = "${WORKDIR}/cyclonedx_python_lib-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
+ dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ pybuild-plugin-pyproject, \
+ python3-poetry, \
+ python3-py-serializable, \
+ python3-packageurl-python, \
+ python3-sortedcontainers, \
+ python3-ddt, \
+ python3-defusedxml, \
+ python3-license-expression, \
+ python3-jsonschema, \
+ python3-lxml, \
+ "
+
+DEBIAN_DEPENDS = "python3, \
+ python3-py-serializable, \
+ python3-packageurl-python, \
+ python3-sortedcontainers, \
+ python3-ddt, \
+ python3-defusedxml, \
+ python3-license-expression, \
+ python3-jsonschema, \
+ python3-lxml, \
+ "
+
+DESCRIPTION = "Library for serializing and deserializing Python Objects to and from JSON and XML."
+
+SRC_URI = "\
+ https://github.com/CycloneDX/cyclonedx-python-lib/releases/download/v9.1.0/cyclonedx_python_lib-9.1.0.tar.gz \
+ file://rules \
+ file://pybuild.testfiles \
+ "
+SRC_URI[sha256sum] = "86935f2c88a7b47a529b93c724dbd3e903bc573f6f8bd977628a7ca1b5dadea1"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ cp "${WORKDIR}"/pybuild.testfiles "${S}"/debian
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-packageurl-python/files/rules b/meta/recipes-support/python3-packageurl-python/files/rules
new file mode 100644
index 00000000..50e1b74c
--- /dev/null
+++ b/meta/recipes-support/python3-packageurl-python/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = packageurl-python
+export PYBUILD_SYSTEM = distutils
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-packageurl-python/python3-packageurl-python_0.16.0.bb b/meta/recipes-support/python3-packageurl-python/python3-packageurl-python_0.16.0.bb
new file mode 100644
index 00000000..773fd93b
--- /dev/null
+++ b/meta/recipes-support/python3-packageurl-python/python3-packageurl-python_0.16.0.bb
@@ -0,0 +1,33 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/packageurl_python-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
+ dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ "
+
+DEBIAN_DEPENDS = "python3"
+
+DESCRIPTION = "A purl aka. Package URL parser and builder"
+
+SRC_URI = "\
+ https://github.com/package-url/packageurl-python/releases/download/v0.16.0/packageurl_python-0.16.0.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "69e3bf8a3932fe9c2400f56aaeb9f86911ecee2f9398dbe1b58ec34340be365d"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-py-serializable/files/rules b/meta/recipes-support/python3-py-serializable/files/rules
new file mode 100644
index 00000000..0cf845dd
--- /dev/null
+++ b/meta/recipes-support/python3-py-serializable/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = py-serializable
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb b/meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
new file mode 100644
index 00000000..9e75062a
--- /dev/null
+++ b/meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
@@ -0,0 +1,42 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/py_serializable-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
+ dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ pybuild-plugin-pyproject, \
+ python3-poetry, \
+ python3-defusedxml, \
+ python3-lxml, \
+ xmldiff \
+ "
+
+DEBIAN_DEPENDS = "python3, \
+ python3-defusedxml, \
+ python3-lxml, \
+ xmldiff \
+ "
+
+DESCRIPTION = "Library for serializing and deserializing Python Objects to and from JSON and XML."
+
+SRC_URI = "\
+ https://github.com/madpah/serializable/releases/download/v2.0.0/py_serializable-2.0.0.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "e9e6491dd7d29c31daf1050232b57f9657f9e8a43b867cca1ff204752cf420a5"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-spdx-tools/files/rules b/meta/recipes-support/python3-spdx-tools/files/rules
new file mode 100644
index 00000000..ac87528a
--- /dev/null
+++ b/meta/recipes-support/python3-spdx-tools/files/rules
@@ -0,0 +1,25 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = spdx-tools
+export PYBUILD_SYSTEM = distutils
+
+# skip tests that require hard-to-package dependencies and tests that rely on relative file paths
+# TODO: figure out a way to make these tests work
+export PYBUILD_TEST_ARGS=--ignore tests/spdx3/validation/json_ld/test_shacl_validation.py \
+ -k 'not test_examples \
+ and not test_parse_from_file \
+ and not test_annotation_parser \
+ and not test_snippet_parser \
+ and not test_creation_info_parser \
+ and not test_json_ld_writer \
+ and not test_extracted_licensing_info_parser \
+ and not test_parse_file \
+ and not test_package_parser \
+ and not test_relationship_parser \
+ and not test_graph_parsing_function \
+ and not test_license_expression_parser \
+ '
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb b/meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb
new file mode 100644
index 00000000..2b81d6fe
--- /dev/null
+++ b/meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb
@@ -0,0 +1,56 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/tools-python-${PV}"
+
+DEPENDS = "python3-beartype"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
+ dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ python3-beartype, \
+ python3-semantic-version, \
+ python3-license-expression, \
+ python3-pytest, \
+ python3-rdflib, \
+ python3-uritools, \
+ python3-ply, \
+ python3-click, \
+ python3-xmltodict, \
+ python3-yaml, \
+ "
+
+DEBIAN_DEPENDS = "python3, \
+ python3-beartype, \
+ python3-semantic-version, \
+ python3-license-expression, \
+ python3-pytest, \
+ python3-rdflib, \
+ python3-uritools, \
+ python3-ply, \
+ python3-click, \
+ python3-xmltodict, \
+ python3-yaml, \
+ "
+
+DESCRIPTION = "SPDX parser and tools."
+
+SRC_URI = "\
+ https://github.com/spdx/tools-python/archive/refs/tags/v0.8.3.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "17cb0140adbaefb58819c9d5d56060dc6a70c673a854fa9bd882ecfa4e062a7f"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
--
2.39.5

Christoph Steiger

unread,
Sep 9, 2025, 4:05:47 AMSep 9
to isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com, Christoph Steiger
Package the python tool debsbom for SBOM generation for Debian based
distributions.

Signed-off-by: Christoph Steiger <christop...@siemens.com>
---
...icense-description-in-pyproject.toml.patch | 28 ++++++++++
.../files/debsbom-0.1.0.tar.gz | Bin 0 -> 9383 bytes
.../python3-debsbom/files/rules | 8 +++
.../python3-debsbom/python3-debsbom_0.1.0.bb | 52 ++++++++++++++++++
4 files changed, 88 insertions(+)
create mode 100644 meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch
create mode 100644 meta/recipes-support/python3-debsbom/files/debsbom-0.1.0.tar.gz
create mode 100644 meta/recipes-support/python3-debsbom/files/rules
create mode 100644 meta/recipes-support/python3-debsbom/python3-debsbom_0.1.0.bb

diff --git a/meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch b/meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch
new file mode 100644
index 00000000..c9137e25
--- /dev/null
+++ b/meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch
@@ -0,0 +1,28 @@
+From 8f926ab0ed1585656ba7de80a82cc802c3ccbdbf Mon Sep 17 00:00:00 2001
+From: Christoph Steiger <christop...@siemens.com>
+Date: Mon, 8 Sep 2025 17:17:49 +0200
+Subject: [PATCH 1/1] Use old license description in pyproject.toml
+
+Older setuptools versions may require a different license field.
+
+Signed-off-by: Christoph Steiger <christop...@siemens.com>
+---
+ pyproject.toml | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/pyproject.toml b/pyproject.toml
+index cc34bdb..701da4a 100644
+--- a/pyproject.toml
++++ b/pyproject.toml
+@@ -22,7 +22,7 @@ maintainers = [
+ ]
+ description = "Generate SBOMs for Debian-based distributions."
+ readme = "README.md"
+-license = "MIT"
++license = {text = "MIT"}
+ classifiers = [
+ "Intended Audience :: Developers",
+ "Operating System :: POSIX :: Linux",
+--
+2.39.5
+
diff --git a/meta/recipes-support/python3-debsbom/files/debsbom-0.1.0.tar.gz b/meta/recipes-support/python3-debsbom/files/debsbom-0.1.0.tar.gz
new file mode 100644
index 0000000000000000000000000000000000000000..7dcc7e43a06e06bad8d097a03294661d51b5e105
GIT binary patch
literal 9383
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

literal 0
HcmV?d00001

diff --git a/meta/recipes-support/python3-debsbom/files/rules b/meta/recipes-support/python3-debsbom/files/rules
new file mode 100644
index 00000000..a414114d
--- /dev/null
+++ b/meta/recipes-support/python3-debsbom/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = debsbom
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-debsbom/python3-debsbom_0.1.0.bb b/meta/recipes-support/python3-debsbom/python3-debsbom_0.1.0.bb
new file mode 100644
index 00000000..a1e54e97
--- /dev/null
+++ b/meta/recipes-support/python3-debsbom/python3-debsbom_0.1.0.bb
@@ -0,0 +1,52 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/git"
+
+DEPENDS = "python3-cyclonedx-python-lib python3-spdx-tools python3-packageurl-python"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
+ dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ pybuild-plugin-pyproject, \
+ python3-packageurl-python, \
+ python3-cyclonedx-python-lib, \
+ python3-spdx-tools, \
+ python3-beartype, \
+ python3-license-expression, \
+ python3-uritools, \
+ python3-py-serializable, \
+ python3-defusedxml, \
+ python3-sortedcontainers, \
+ python3-debian, \
+ python3-requests, \
+ "
+
+DEBIAN_DEPENDS = "python3-cyclonedx-python-lib, \
+ python3-spdx-tools, \
+ python3-packageurl-python, \
+ python3-requests, \
+ python3-debian, \
+ "
+
+DESCRIPTION = "debsbom generates SBOMs for Debian based distributions."
+
+SRC_URI = "git://github.com/siemens/debsbom.git;protocol=https;branch=main; \
+ file://rules \
+ file://0001-Use-old-license-description-in-pyproject.toml.patch \
+ "
+SRCREV = "8fb87567514d461e2db49e0485b890ef108c824a"

Christoph Steiger

unread,
Sep 9, 2025, 4:05:49 AMSep 9
to isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com, Christoph Steiger
Generate SBOMs for every rootfs that is created. These SBOMs are placed
in the image deploy directory.

For the generation a small chroot with debsbom installed is created and
from that the rootfs of the image is scanned.

The sbom generation is bound to the rootfs feature `generate-sbom`
which is activated per default now.

Signed-off-by: Christoph Steiger <christop...@siemens.com>
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
meta/classes/image.bbclass | 2 +-
meta/classes/rootfs.bbclass | 6 +-
meta/classes/sbom.bbclass | 60 ++++++++++++++++++
meta/classes/sdk.bbclass | 2 +-
.../sbom-chroot/sbom-chroot.bb | 31 +++++++++
.../files/debsbom-0.1.0.tar.gz | Bin 9383 -> 0 bytes
...sbom_0.1.0.bb => python3-debsbom_0.0.1.bb} | 2 +
7 files changed, 100 insertions(+), 3 deletions(-)
create mode 100644 meta/classes/sbom.bbclass
create mode 100644 meta/recipes-devtools/sbom-chroot/sbom-chroot.bb
delete mode 100644 meta/recipes-support/python3-debsbom/files/debsbom-0.1.0.tar.gz
rename meta/recipes-support/python3-debsbom/{python3-debsbom_0.1.0.bb => python3-debsbom_0.0.1.bb} (98%)

diff --git a/meta/classes/image.bbclass b/meta/classes/image.bbclass
index bd1b8552..57e66632 100644
--- a/meta/classes/image.bbclass
+++ b/meta/classes/image.bbclass
@@ -66,7 +66,7 @@ inherit multiarch
inherit essential

ROOTFSDIR = "${IMAGE_ROOTFS}"
-ROOTFS_FEATURES += "clean-package-cache clean-pycache generate-manifest export-dpkg-status clean-log-files clean-debconf-cache"
+ROOTFS_FEATURES += "clean-package-cache clean-pycache generate-manifest export-dpkg-status clean-log-files clean-debconf-cache generate-sbom"
# when using a custom initrd, do not generate one as part of the image rootfs
ROOTFS_FEATURES += "${@ '' if d.getVar('INITRD_IMAGE') == '' else 'no-generate-initrd'}"
ROOTFS_PACKAGES += "${IMAGE_PREINSTALL} ${@isar_multiarch_packages('IMAGE_INSTALL', d)}"
diff --git a/meta/classes/rootfs.bbclass b/meta/classes/rootfs.bbclass
index 7b7859b9..0476f0d2 100644
--- a/meta/classes/rootfs.bbclass
+++ b/meta/classes/rootfs.bbclass
@@ -3,6 +3,8 @@

inherit deb-dl-dir

+inherit sbom
+
ROOTFS_ARCH ?= "${DISTRO_ARCH}"
ROOTFS_DISTRO ?= "${DISTRO}"
ROOTFS_PACKAGES ?= ""
@@ -429,6 +431,8 @@ rootfs_cleanup_base_apt() {
EOSUDO
}

+ROOTFS_POSTPROCESS_COMMAND += "${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'do_generate_sbom', '', d)}"
+
do_rootfs_postprocess[vardeps] = "${ROOTFS_POSTPROCESS_COMMAND}"
do_rootfs_postprocess[network] = "${TASK_USE_SUDO}"
python do_rootfs_postprocess() {
@@ -512,7 +516,7 @@ python do_rootfs() {
}
addtask rootfs before do_build

-do_rootfs_postprocess[depends] = "base-apt:do_cache isar-apt:do_cache_config"
+do_rootfs_postprocess[depends] = "base-apt:do_cache isar-apt:do_cache_config ${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'sbom-chroot:do_sbomchroot_deploy', '', d)}"

SSTATETASKS += "do_rootfs_install"
SSTATECREATEFUNCS += "rootfs_install_sstate_prepare"
diff --git a/meta/classes/sbom.bbclass b/meta/classes/sbom.bbclass
new file mode 100644
index 00000000..3ae0a610
--- /dev/null
+++ b/meta/classes/sbom.bbclass
@@ -0,0 +1,60 @@
+# This software is a part of ISAR.
+# Copyright (C) 2025 Siemens
+#
+# SPDX-License-Identifier: MIT
+
+# sbom type to generate, accepted are "cdx" or "spdx"
+SBOM_TYPES ?= "spdx cdx"
+
+SBOM_DEBSBOM_TYPE_ARGS = "${@"-t " + " -t ".join(d.getVar("SBOM_TYPES").split())}"
+
+# general user variables
+SBOM_DISTRO_SUPPLIER ?= "ISAR"
+SBOM_DISTRO_NAME ?= "ISAR-Debian-GNU-Linux"
+SBOM_DISTRO_VERSION ?= "1"
+SBOM_DISTRO_SUMMARY ?= "Linux distribution built with ISAR"
+SBOM_DOCUMENT_UUID ?= ""
+
+# SPDX specific user variables
+SBOM_SPDX_NAMESPACE_PREFIX ?= "https://spdx.org/spdxdocs"
+
+DEPLOY_DIR_SBOM = "${DEPLOY_DIR_IMAGE}"
+
+SBOM_DIR = "${DEPLOY_DIR}/sbom"
+SBOM_CHROOT = "${SBOM_DIR}/sbom-chroot"
+
+# adapted from the isar-cip-core image_uuid.bbclass
+def generate_document_uuid(d):
+ import uuid
+
+ base_hash = d.getVar("BB_TASKHASH")
+ if base_hash is None:
+ bb.warn("no BB_TASKHASH available, SBOM UUID is not reproducible")
+ return uuid.uuid4()
+ return str(uuid.UUID(base_hash[:32], version=4))
+
+def sbom_doc_uuid(d):
+ if not d.getVar("SBOM_DOCUMENT_UUID"):
+ d.setVar("SBOM_DOCUMENT_UUID", generate_document_uuid(d))
+
+generate_sbom() {
+ sudo mkdir -p ${SBOM_CHROOT}/mnt/rootfs ${SBOM_CHROOT}/mnt/deploy-dir
+
+ TIMESTAMP=$(date --iso-8601=s -d @${SOURCE_DATE_EPOCH})
+ bwrap \
+ --unshare-user \
+ --unshare-pid \
+ --bind ${SBOM_CHROOT} / \
+ --bind ${ROOTFSDIR} /mnt/rootfs \
+ --bind ${DEPLOY_DIR_SBOM} /mnt/deploy-dir \
+ -- debsbom generate ${SBOM_DEBSBOM_TYPE_ARGS} -r /mnt/rootfs -o /mnt/deploy-dir/'${PN}-${DISTRO}-${MACHINE}' \
+ --distro-name '${SBOM_DISTRO_NAME}' --distro-supplier '${SBOM_DISTRO_SUPPLIER}' \
+ --distro-version '${SBOM_DISTRO_VERSION}' --cdx-serialnumber '${SBOM_DOCUMENT_UUID}' \
+ --spdx-namespace '${SBOM_SPDX_NAMESPACE_PREFIX}'-'${SBOM_DOCUMENT_UUID}' \
+ --timestamp $TIMESTAMP
+}
+
+python do_generate_sbom() {
+ sbom_doc_uuid(d)
+ bb.build.exec_func("generate_sbom", d)
+}
diff --git a/meta/classes/sdk.bbclass b/meta/classes/sdk.bbclass
index 46436d97..644b0623 100644
--- a/meta/classes/sdk.bbclass
+++ b/meta/classes/sdk.bbclass
@@ -55,7 +55,7 @@ def get_rootfs_distro(d):
ROOTFS_ARCH:class-sdk = "${HOST_ARCH}"
ROOTFS_DISTRO:class-sdk = "${@get_rootfs_distro(d)}"
ROOTFS_PACKAGES:class-sdk = "sdk-files ${SDK_TOOLCHAIN} ${SDK_PREINSTALL} ${@isar_multiarch_packages('SDK_INSTALL', d)}"
-ROOTFS_FEATURES:append:class-sdk = " clean-package-cache generate-manifest export-dpkg-status"
+ROOTFS_FEATURES:append:class-sdk = " clean-package-cache generate-manifest export-dpkg-status generate-sbom"
ROOTFS_MANIFEST_DEPLOY_DIR:class-sdk = "${DEPLOY_DIR_SDKCHROOT}"
ROOTFS_DPKGSTATUS_DEPLOY_DIR:class-sdk = "${DEPLOY_DIR_SDKCHROOT}"

diff --git a/meta/recipes-devtools/sbom-chroot/sbom-chroot.bb b/meta/recipes-devtools/sbom-chroot/sbom-chroot.bb
new file mode 100644
index 00000000..6f27f842
--- /dev/null
+++ b/meta/recipes-devtools/sbom-chroot/sbom-chroot.bb
@@ -0,0 +1,31 @@
+# This software is a part of ISAR.
+#
+# Copyright (C) 2025 Siemens
+
+LICENSE = "gpl-2.0"
+LIC_FILES_CHKSUM = "file://${LAYERDIR_core}/licenses/COPYING.GPLv2;md5=751419260aa954499f7abaabaa882bbe"
+
+PV = "1.0"
+
+inherit rootfs
+inherit sbom
+
+ROOTFS_ARCH = "${HOST_ARCH}"
+ROOTFS_DISTRO = "${HOST_DISTRO}"
+ROOTFS_BASE_DISTRO = "${HOST_BASE_DISTRO}"
+
+ROOTFS_FEATURES = "no-generate-initrd"
+
+# additional packages for the SBOM chroot
+SBOM_IMAGE_INSTALL = "python3-debsbom"
+
+DEPENDS = "python3-debsbom"
+
+ROOTFSDIR = "${WORKDIR}/rootfs"
+ROOTFS_PACKAGES = "${SBOM_IMAGE_INSTALL}"
+
+do_sbomchroot_deploy[dirs] = "${SBOM_DIR}"
+do_sbomchroot_deploy() {
+ ln -Tfsr "${ROOTFSDIR}" "${SBOM_CHROOT}"
+}
+addtask do_sbomchroot_deploy before do_build after do_rootfs
diff --git a/meta/recipes-support/python3-debsbom/files/debsbom-0.1.0.tar.gz b/meta/recipes-support/python3-debsbom/files/debsbom-0.1.0.tar.gz
deleted file mode 100644
index 7dcc7e43a06e06bad8d097a03294661d51b5e105..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
diff --git a/meta/recipes-support/python3-debsbom/python3-debsbom_0.1.0.bb b/meta/recipes-support/python3-debsbom/python3-debsbom_0.0.1.bb
similarity index 98%
rename from meta/recipes-support/python3-debsbom/python3-debsbom_0.1.0.bb
rename to meta/recipes-support/python3-debsbom/python3-debsbom_0.0.1.bb
index a1e54e97..05a5ec6b 100644
--- a/meta/recipes-support/python3-debsbom/python3-debsbom_0.1.0.bb
+++ b/meta/recipes-support/python3-debsbom/python3-debsbom_0.0.1.bb
@@ -11,6 +11,8 @@ S = "${WORKDIR}/git"

DEPENDS = "python3-cyclonedx-python-lib python3-spdx-tools python3-packageurl-python"

+S = "${WORKDIR}/git"
+
MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
DPKG_ARCH = "all"
DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
--
2.39.5

Christoph Steiger

unread,
Sep 9, 2025, 4:19:58 AMSep 9
to isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com
This is a leftover from one of my internal first prototypes. I remove
this in the following commit. Rebasing is hard sometimes. This will
disappear in the eventual v2.

MOESSBAUER, Felix

unread,
Sep 11, 2025, 6:07:10 AMSep 11
to Steiger, Christoph, isar-...@googlegroups.com, Kiszka, Jan, cedric.h...@siemens.com, Hillier, Gernot
On Tue, 2025-09-09 at 10:05 +0200, Christoph Steiger wrote:
> Generate SBOMs for every rootfs that is created. These SBOMs are placed
> in the image deploy directory.
>
> For the generation a small chroot with debsbom installed is created and
> from that the rootfs of the image is scanned.
>
> The sbom generation is bound to the rootfs feature `generate-sbom`
> which is activated per default now.
>
> Signed-off-by: Christoph Steiger <christop...@siemens.com>
> Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
> ---
> meta/classes/image.bbclass | 2 +-
> meta/classes/rootfs.bbclass | 6 +-
> meta/classes/sbom.bbclass | 60 ++++++++++++++++++
> meta/classes/sdk.bbclass | 2 +-
> .../sbom-chroot/sbom-chroot.bb | 31 +++++++++
> .../files/debsbom-0.1.0.tar.gz | Bin 9383 -> 0 bytes
> ...sbom_0.1.0.bb => python3-debsbom_0.0.1.bb} | 2 +

Hi, you renamed that file between p2 and p3. That probably is not
intentional as well. Maybe you could send out a cleanup of this series
along with an update of the debsbom upstream ref?

Felix

--
Siemens AG
Linux Expert Center
Friedrich-Ludwig-Bauer-Str. 3
85748 Garching, Germany

srinuv...@siemens.com

unread,
Sep 11, 2025, 9:01:54 AMSep 11
to isar-...@googlegroups.com, christop...@siemens.com, felix.mo...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com, srinuvasan
From: srinuvasan <srinuv...@siemens.com>

Includes additional fixes alongside the following patch series
https://groups.google.com/g/isar-users/c/4JntAI3gL1s

srinuvasan (1):
meta: derive ROOTFS_DISTRO correctly in sbom-chroot's
do_prepare_rootfs

meta/recipes-devtools/sbom-chroot/sbom-chroot.bb | 5 -----
1 file changed, 5 deletions(-)

--
2.39.5

Srinuvasan Arjunan

unread,
Sep 12, 2025, 4:17:13 AMSep 12
to isar-users
Hi Steiger,

In this patch series, the first patch (meta: package Python dependencies for SBOM generation) does not work on foreign architectures. I have fixed those cross-build issues — shall I send a v2 of this patch in the series?  

Many thanks,
Srinu

Srinuvasan Arjunan

unread,
Sep 12, 2025, 5:34:35 AMSep 12
to isar-users
Hi,

        When generating SBOM for foreign architectures (e.g., arm64), some Python packages fail to build. One such example is python3-cyclonedx-python-lib. During arm64 builds it throws the below error:

| The following packages have unmet dependencies:
|  sbuild-build-depends-main-dummy:arm64 : Depends: dh-python:arm64
|                                          Depends: python3-all:arm64 but it is not going to be installed
|                                          Depends: python3-setuptools:arm64
|                                          Depends: pybuild-plugin-pyproject:arm64
|                                          Depends: python3-hatchling:arm64 but it is not installable


The fix is to add the :native suffix for Python build dependencies. For example:


DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
    dh-python, \
    python3-all:native, \
    python3-setuptools, \
    pybuild-plugin-pyproject, \
    python3-poetry:native, \
    python3-py-serializable:native, \
    python3-packageurl-python:native, \
    python3-sortedcontainers, \
    python3-ddt:native, \
    python3-defusedxml:native, \
    python3-license-expression:native, \
    python3-jsonschema:native, \
    python3-lxml:native, \
"

In short, Python recipes required for the build must explicitly use the :native suffix to avoid cross-architecture dependency resolution issues.

Similar fixes are needed for all the python recipes in this patch.

Many thanks,
Srinu

MOESSBAUER, Felix

unread,
Sep 12, 2025, 7:02:56 AMSep 12
to srinuv...@gmail.com, isar-...@googlegroups.com
On Fri, 2025-09-12 at 02:34 -0700, Srinuvasan Arjunan wrote:
> Hi,
>
>         When generating SBOM for foreign architectures (e.g., arm64), some Python packages fail to build. One such example is python3-cyclonedx-python-lib. During arm64 builds it throws the below error:
>
> | The following packages have unmet dependencies:
> |  sbuild-build-depends-main-dummy:arm64 : Depends: dh-python:arm64
> |                                          Depends: python3-all:arm64 but it is not going to be installed
> |                                          Depends: python3-setuptools:arm64
> |                                          Depends: pybuild-plugin-pyproject:arm64
> |                                          Depends: python3-hatchling:arm64 but it is not installable
>
>

Hi, this happens because we cross-compile the arch:all package (which
we shoudn't) and also bitbake dependencies are not propagated
correctly. Fixing all that has been taken care of in the following
patches:

- handle DPKG_ARCH=all case for transitive deps
- dpkg-raw: add files to source package

These are not yet applied (but hopefully they will be soon).

Srinuvasan Arjunan

unread,
Sep 15, 2025, 2:18:28 AMSep 15
to isar-users
   Thanks for the update.

   Many thanks,
   Srinu 

Christoph Steiger

unread,
Sep 17, 2025, 2:33:48 AMSep 17
to isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com, Christoph Steiger
This patchset adds proper SBOM generation in the two standard formats
SPDX and CycloneDX during the rootfs generation process.

The generation is itself is handled by a SBOM generator `debsbom` [1]
which is developed as an open source project at Siemens. It is still
early in development, but it has enough features for what we require
in isar. The required dependencies which are not yet available as
Debian packages were minimally packaged directly in isar too.

This is a followup of the previous RFC [2]. Since then the series has
changed a lot. The SBOM generation was moved from a simple OE lib to
`debsbom`. This also meant the introduction of a separate chroot was
necessary. The SBOM generation process was also moved from the image
step to the rootfs step, along with a lot of minor changes and
improvements.

[1] https://github.com/siemens/debsbom
[2] https://groups.google.com/g/isar-users/c/8L-CF4BJY0I/m/p0N3o_zfAAAJ

Changes since v1:

- remove tarball
- refactor packaging (auto-derive python dependencies)
- only build missing packages (varies on bookworm, trixie, noble)
- add ubuntu support
- only generate sboms for supported distributions (bookworm/jammy and
onwards)
- update debsbom (includes bug fixes and more information for source
packages)

Christoph Steiger (3):
meta: package python libraries for SBOM generation
meta: package python3-debsbom
meta: add SBOM generation with debsbom

Felix Moessbauer (1):
override distro vendor in SBOM on Ubuntu

meta-isar/conf/distro/ubuntu-common.inc | 2 +
meta/classes/image.bbclass | 8 ++-
meta/classes/rootfs.bbclass | 7 ++-
meta/classes/sbom.bbclass | 62 +++++++++++++++++++
meta/classes/sdk.bbclass | 2 +-
.../sbom-chroot/sbom-chroot.bb | 30 +++++++++
.../python3-beartype/files/rules | 8 +++
.../python3-beartype_0.19.0.bb | 29 +++++++++
.../files/pybuild.testfiles | 1 +
.../python3-cyclonedx-lib/files/rules | 8 +++
.../python3-cyclonedx-lib_9.1.0.bb | 48 ++++++++++++++
...icense-description-in-pyproject.toml.patch | 28 +++++++++
.../python3-debsbom/files/rules | 8 +++
.../python3-debsbom/python3-debsbom_0.0.1.bb | 44 +++++++++++++
.../python3-packageurl/files/rules | 8 +++
.../python3-packageurl_0.16.0.bb | 33 ++++++++++
.../python3-py-serializable/files/rules | 8 +++
.../python3-py-serializable_2.0.0.bb | 38 ++++++++++++
.../python3-spdx-tools/files/rules | 25 ++++++++
.../python3-spdx-tools_0.8.3.bb | 46 ++++++++++++++
20 files changed, 440 insertions(+), 3 deletions(-)
create mode 100644 meta/classes/sbom.bbclass
create mode 100644 meta/recipes-devtools/sbom-chroot/sbom-chroot.bb
create mode 100644 meta/recipes-support/python3-beartype/files/rules
create mode 100644 meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/files/pybuild.testfiles
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/files/rules
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/python3-cyclonedx-lib_9.1.0.bb
create mode 100644 meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch
create mode 100644 meta/recipes-support/python3-debsbom/files/rules
create mode 100644 meta/recipes-support/python3-debsbom/python3-debsbom_0.0.1.bb
create mode 100644 meta/recipes-support/python3-packageurl/files/rules
create mode 100644 meta/recipes-support/python3-packageurl/python3-packageurl_0.16.0.bb
create mode 100644 meta/recipes-support/python3-py-serializable/files/rules
create mode 100644 meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
create mode 100644 meta/recipes-support/python3-spdx-tools/files/rules
create mode 100644 meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb

--
2.39.5

Christoph Steiger

unread,
Sep 17, 2025, 2:33:51 AMSep 17
to isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com, Christoph Steiger
Package python libraries for SBOM generation in isar. The packages are
unfortunately not (yet) packaged in Debian, thats why we need to do it
here. With these libraries it is now possible to easily create CDX and
SPDX SBOMs in different file formats.

Signed-off-by: Christoph Steiger <christop...@siemens.com>
---
.../python3-beartype/files/rules | 8 ++++
.../python3-beartype_0.19.0.bb | 29 +++++++++++
.../files/pybuild.testfiles | 1 +
.../python3-cyclonedx-lib/files/rules | 8 ++++
.../python3-cyclonedx-lib_9.1.0.bb | 48 +++++++++++++++++++
.../python3-packageurl/files/rules | 8 ++++
.../python3-packageurl_0.16.0.bb | 33 +++++++++++++
.../python3-py-serializable/files/rules | 8 ++++
.../python3-py-serializable_2.0.0.bb | 38 +++++++++++++++
.../python3-spdx-tools/files/rules | 25 ++++++++++
.../python3-spdx-tools_0.8.3.bb | 46 ++++++++++++++++++
11 files changed, 252 insertions(+)
create mode 100644 meta/recipes-support/python3-beartype/files/rules
create mode 100644 meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/files/pybuild.testfiles
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/files/rules
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/python3-cyclonedx-lib_9.1.0.bb
create mode 100644 meta/recipes-support/python3-packageurl/files/rules
create mode 100644 meta/recipes-support/python3-packageurl/python3-packageurl_0.16.0.bb
create mode 100644 meta/recipes-support/python3-py-serializable/files/rules
create mode 100644 meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
create mode 100644 meta/recipes-support/python3-spdx-tools/files/rules
create mode 100644 meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb

diff --git a/meta/recipes-support/python3-beartype/files/rules b/meta/recipes-support/python3-beartype/files/rules
new file mode 100644
index 00000000..0ca517a1
--- /dev/null
+++ b/meta/recipes-support/python3-beartype/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = beartype
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb b/meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
new file mode 100644
index 00000000..b8bc2708
--- /dev/null
+++ b/meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
@@ -0,0 +1,29 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/beartype-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), dh-python, python3-all, python3-setuptools, pybuild-plugin-pyproject, python3-hatchling"
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+# this is 01/01/1980, any earlier and zip in the wheel building process will not accept it
+DEBIAN_CHANGELOG_TIMESTAMP = "315532800"
+DESCRIPTION = "Unbearably fast near-real-time hybrid runtime-static type-checking in pure Python."
+
+SRC_URI = "\
+ https://github.com/beartype/beartype/archive/refs/tags/v0.19.0.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "e7ad00eebf527d60f30e0b391209b561dabd2074b608c50e26c94c2d8250a6cd"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-cyclonedx-lib/files/pybuild.testfiles b/meta/recipes-support/python3-cyclonedx-lib/files/pybuild.testfiles
new file mode 100644
index 00000000..cc736a36
--- /dev/null
+++ b/meta/recipes-support/python3-cyclonedx-lib/files/pybuild.testfiles
@@ -0,0 +1 @@
+pyproject.toml
diff --git a/meta/recipes-support/python3-cyclonedx-lib/files/rules b/meta/recipes-support/python3-cyclonedx-lib/files/rules
new file mode 100644
index 00000000..fe72dd1a
--- /dev/null
+++ b/meta/recipes-support/python3-cyclonedx-lib/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = cyclonedx-python-lib
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-cyclonedx-lib/python3-cyclonedx-lib_9.1.0.bb b/meta/recipes-support/python3-cyclonedx-lib/python3-cyclonedx-lib_9.1.0.bb
new file mode 100644
index 00000000..738ed1b3
--- /dev/null
+++ b/meta/recipes-support/python3-cyclonedx-lib/python3-cyclonedx-lib_9.1.0.bb
@@ -0,0 +1,48 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+DEPENDS:append:bookworm = " python3-packageurl python3-py-serializable"
+DEPENDS:append:noble = " python3-packageurl python3-py-serializable"
+
+S = "${WORKDIR}/cyclonedx_python_lib-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
+ dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ pybuild-plugin-pyproject, \
+ python3-poetry, \
+ python3-py-serializable, \
+ python3-packageurl, \
+ python3-sortedcontainers, \
+ python3-ddt, \
+ python3-defusedxml, \
+ python3-license-expression, \
+ python3-jsonschema, \
+ python3-lxml, \
+ "
+
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+
+DESCRIPTION = "Library for serializing and deserializing Python Objects to and from JSON and XML."
+
+SRC_URI = "\
+ https://github.com/CycloneDX/cyclonedx-python-lib/releases/download/v9.1.0/cyclonedx_python_lib-9.1.0.tar.gz \
+ file://rules \
+ file://pybuild.testfiles \
+ "
+SRC_URI[sha256sum] = "86935f2c88a7b47a529b93c724dbd3e903bc573f6f8bd977628a7ca1b5dadea1"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ cp "${WORKDIR}"/pybuild.testfiles "${S}"/debian
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-packageurl/files/rules b/meta/recipes-support/python3-packageurl/files/rules
new file mode 100644
index 00000000..50e1b74c
--- /dev/null
+++ b/meta/recipes-support/python3-packageurl/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = packageurl-python
+export PYBUILD_SYSTEM = distutils
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-packageurl/python3-packageurl_0.16.0.bb b/meta/recipes-support/python3-packageurl/python3-packageurl_0.16.0.bb
new file mode 100644
index 00000000..27209429
--- /dev/null
+++ b/meta/recipes-support/python3-packageurl/python3-packageurl_0.16.0.bb
@@ -0,0 +1,33 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/packageurl_python-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
+ dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ "
+
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+
+DESCRIPTION = "A purl aka. Package URL parser and builder"
+
+SRC_URI = "\
+ https://github.com/package-url/packageurl-python/releases/download/v0.16.0/packageurl_python-0.16.0.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "69e3bf8a3932fe9c2400f56aaeb9f86911ecee2f9398dbe1b58ec34340be365d"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-py-serializable/files/rules b/meta/recipes-support/python3-py-serializable/files/rules
new file mode 100644
index 00000000..0cf845dd
--- /dev/null
+++ b/meta/recipes-support/python3-py-serializable/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = py-serializable
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb b/meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
new file mode 100644
index 00000000..5bc48c0f
--- /dev/null
+++ b/meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
@@ -0,0 +1,38 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/py_serializable-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = " \
+ dh-sequence-python3, \
+ pybuild-plugin-pyproject, \
+ python3-all, \
+ python3-defusedxml, \
+ python3-lxml, \
+ python3-poetry-core, \
+ python3-setuptools, \
+ xmldiff, \
+"
+
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+
+DESCRIPTION = "Library for serializing and deserializing Python Objects to and from JSON and XML."
+
+SRC_URI = "\
+ https://github.com/madpah/serializable/releases/download/v2.0.0/py_serializable-2.0.0.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "e9e6491dd7d29c31daf1050232b57f9657f9e8a43b867cca1ff204752cf420a5"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-spdx-tools/files/rules b/meta/recipes-support/python3-spdx-tools/files/rules
new file mode 100644
index 00000000..ac87528a
--- /dev/null
+++ b/meta/recipes-support/python3-spdx-tools/files/rules
@@ -0,0 +1,25 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = spdx-tools
+export PYBUILD_SYSTEM = distutils
+
+# skip tests that require hard-to-package dependencies and tests that rely on relative file paths
+# TODO: figure out a way to make these tests work
+export PYBUILD_TEST_ARGS=--ignore tests/spdx3/validation/json_ld/test_shacl_validation.py \
+ -k 'not test_examples \
+ and not test_parse_from_file \
+ and not test_annotation_parser \
+ and not test_snippet_parser \
+ and not test_creation_info_parser \
+ and not test_json_ld_writer \
+ and not test_extracted_licensing_info_parser \
+ and not test_parse_file \
+ and not test_package_parser \
+ and not test_relationship_parser \
+ and not test_graph_parsing_function \
+ and not test_license_expression_parser \
+ '
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb b/meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb
new file mode 100644
index 00000000..30d090a9
--- /dev/null
+++ b/meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb
@@ -0,0 +1,46 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/tools-python-${PV}"
+
+DEPENDS:append:bookworm = " python3-beartype"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ python3-beartype, \
+ python3-semantic-version, \
+ python3-license-expression, \
+ python3-pytest <!nocheck>, \
+ python3-rdflib, \
+ python3-uritools, \
+ python3-ply, \
+ python3-click, \
+ python3-xmltodict, \
+ python3-yaml, \
+ "
+
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+DEB_BUILD_PROFILES += "nocheck"
+DEB_BUILD_OPTIONS += "nocheck"
+
+DESCRIPTION = "SPDX parser and tools."
+
+SRC_URI = "\
+ https://github.com/spdx/tools-python/archive/refs/tags/v0.8.3.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "17cb0140adbaefb58819c9d5d56060dc6a70c673a854fa9bd882ecfa4e062a7f"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
--
2.39.5

Christoph Steiger

unread,
Sep 17, 2025, 2:33:57 AMSep 17
to isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com, Christoph Steiger
Generate SBOMs for every rootfs that is created. These SBOMs are placed
in the image deploy directory.

For the generation a small chroot with debsbom installed is created and
from that the rootfs of the image is scanned.

The sbom generation is bound to the rootfs feature `generate-sbom`
which is activated per default now.

Signed-off-by: Christoph Steiger <christop...@siemens.com>
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
meta/classes/image.bbclass | 8 ++-
meta/classes/rootfs.bbclass | 7 ++-
meta/classes/sbom.bbclass | 62 +++++++++++++++++++
meta/classes/sdk.bbclass | 2 +-
.../sbom-chroot/sbom-chroot.bb | 30 +++++++++
5 files changed, 106 insertions(+), 3 deletions(-)
create mode 100644 meta/classes/sbom.bbclass
create mode 100644 meta/recipes-devtools/sbom-chroot/sbom-chroot.bb

diff --git a/meta/classes/image.bbclass b/meta/classes/image.bbclass
index bd1b8552..220f5aa3 100644
--- a/meta/classes/image.bbclass
+++ b/meta/classes/image.bbclass
@@ -66,7 +66,13 @@ inherit multiarch
inherit essential

ROOTFSDIR = "${IMAGE_ROOTFS}"
-ROOTFS_FEATURES += "clean-package-cache clean-pycache generate-manifest export-dpkg-status clean-log-files clean-debconf-cache"
+ROOTFS_FEATURES += "clean-package-cache clean-pycache generate-manifest export-dpkg-status clean-log-files clean-debconf-cache generate-sbom"
+# only supported from bookworm / jammy on
+ROOTFS_FEATURES:remove:buster = "generate-sbom"
+ROOTFS_FEATURES:remove:bullseye = "generate-sbom"
+ROOTFS_FEATURES:remove:jammy = "generate-sbom"
+ROOTFS_FEATURES:remove:focal = "generate-sbom"
+
# when using a custom initrd, do not generate one as part of the image rootfs
ROOTFS_FEATURES += "${@ '' if d.getVar('INITRD_IMAGE') == '' else 'no-generate-initrd'}"
ROOTFS_PACKAGES += "${IMAGE_PREINSTALL} ${@isar_multiarch_packages('IMAGE_INSTALL', d)}"
diff --git a/meta/classes/rootfs.bbclass b/meta/classes/rootfs.bbclass
index 7b7859b9..98f5b24c 100644
--- a/meta/classes/rootfs.bbclass
+++ b/meta/classes/rootfs.bbclass
@@ -3,6 +3,8 @@

inherit deb-dl-dir

+inherit sbom
+
ROOTFS_ARCH ?= "${DISTRO_ARCH}"
ROOTFS_DISTRO ?= "${DISTRO}"
ROOTFS_PACKAGES ?= ""
@@ -350,6 +352,9 @@ cache_dbg_pkgs() {
fi
}

+# The sbom generator needs the apt-cache, hence run before cleaning it
+ROOTFS_POSTPROCESS_COMMAND += "${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'do_generate_sbom', '', d)}"
+
ROOTFS_POSTPROCESS_COMMAND += "${@bb.utils.contains('ROOTFS_FEATURES', 'clean-package-cache', 'rootfs_postprocess_clean_package_cache', '', d)}"
rootfs_postprocess_clean_package_cache() {
sudo -E chroot '${ROOTFSDIR}' \
@@ -512,7 +517,7 @@ python do_rootfs() {
}
addtask rootfs before do_build

-do_rootfs_postprocess[depends] = "base-apt:do_cache isar-apt:do_cache_config"
+do_rootfs_postprocess[depends] = "base-apt:do_cache isar-apt:do_cache_config ${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'sbom-chroot:do_sbomchroot_deploy', '', d)}"

SSTATETASKS += "do_rootfs_install"
SSTATECREATEFUNCS += "rootfs_install_sstate_prepare"
diff --git a/meta/classes/sbom.bbclass b/meta/classes/sbom.bbclass
new file mode 100644
index 00000000..60c89877
--- /dev/null
+++ b/meta/classes/sbom.bbclass
@@ -0,0 +1,62 @@
+# This software is a part of ISAR.
+# Copyright (C) 2025 Siemens
+#
+# SPDX-License-Identifier: MIT
+
+# sbom type to generate, accepted are "cdx" or "spdx"
+SBOM_TYPES ?= "spdx cdx"
+
+SBOM_DEBSBOM_TYPE_ARGS = "${@"-t " + " -t ".join(d.getVar("SBOM_TYPES").split())}"
+
+# general user variables
+SBOM_DISTRO_SUPPLIER ?= "ISAR"
+SBOM_DISTRO_NAME ?= "ISAR-Debian-GNU-Linux"
+SBOM_DISTRO_VERSION ?= "1"
+SBOM_DISTRO_SUMMARY ?= "Linux distribution built with ISAR"
+SBOM_BASE_DISTRO_VENDOR ??= "debian"
+ --distro-version '${SBOM_DISTRO_VERSION}' --base-distro-vendor '${SBOM_BASE_DISTRO_VENDOR}' \
+ --cdx-serialnumber '${SBOM_DOCUMENT_UUID}' \
+ --spdx-namespace '${SBOM_SPDX_NAMESPACE_PREFIX}'-'${SBOM_DOCUMENT_UUID}' \
+ --timestamp $TIMESTAMP
+}
+
+python do_generate_sbom() {
+ sbom_doc_uuid(d)
+ bb.build.exec_func("generate_sbom", d)
+}
diff --git a/meta/classes/sdk.bbclass b/meta/classes/sdk.bbclass
index 46436d97..644b0623 100644
--- a/meta/classes/sdk.bbclass
+++ b/meta/classes/sdk.bbclass
@@ -55,7 +55,7 @@ def get_rootfs_distro(d):
ROOTFS_ARCH:class-sdk = "${HOST_ARCH}"
ROOTFS_DISTRO:class-sdk = "${@get_rootfs_distro(d)}"
ROOTFS_PACKAGES:class-sdk = "sdk-files ${SDK_TOOLCHAIN} ${SDK_PREINSTALL} ${@isar_multiarch_packages('SDK_INSTALL', d)}"
-ROOTFS_FEATURES:append:class-sdk = " clean-package-cache generate-manifest export-dpkg-status"
+ROOTFS_FEATURES:append:class-sdk = " clean-package-cache generate-manifest export-dpkg-status generate-sbom"
ROOTFS_MANIFEST_DEPLOY_DIR:class-sdk = "${DEPLOY_DIR_SDKCHROOT}"
ROOTFS_DPKGSTATUS_DEPLOY_DIR:class-sdk = "${DEPLOY_DIR_SDKCHROOT}"

diff --git a/meta/recipes-devtools/sbom-chroot/sbom-chroot.bb b/meta/recipes-devtools/sbom-chroot/sbom-chroot.bb
new file mode 100644
index 00000000..a9afcbbe
--- /dev/null
+++ b/meta/recipes-devtools/sbom-chroot/sbom-chroot.bb
@@ -0,0 +1,30 @@
+# This software is a part of ISAR.
+#
+# Copyright (C) 2025 Siemens
+
+LICENSE = "gpl-2.0"
+LIC_FILES_CHKSUM = "file://${LAYERDIR_core}/licenses/COPYING.GPLv2;md5=751419260aa954499f7abaabaa882bbe"
+
+PV = "1.0"
+
+inherit rootfs
+
+ROOTFS_ARCH = "${HOST_ARCH}"
+ROOTFS_DISTRO = "${HOST_DISTRO}"
+ROOTFS_BASE_DISTRO = "${HOST_BASE_DISTRO}"
+
+ROOTFS_FEATURES = "no-generate-initrd"
+
+# additional packages for the SBOM chroot
+SBOM_IMAGE_INSTALL = "python3-debsbom"
+
+DEPENDS = "python3-debsbom"
+
+ROOTFSDIR = "${WORKDIR}/rootfs"
+ROOTFS_PACKAGES = "${SBOM_IMAGE_INSTALL}"
+
+do_sbomchroot_deploy[dirs] = "${SBOM_DIR}"
+do_sbomchroot_deploy() {
+ ln -Tfsr "${ROOTFSDIR}" "${SBOM_CHROOT}"
+}
+addtask do_sbomchroot_deploy before do_build after do_rootfs
--
2.39.5

Christoph Steiger

unread,
Sep 17, 2025, 2:34:06 AMSep 17
to isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com
From: Felix Moessbauer <felix.mo...@siemens.com>

When generating an SBOM for Ubuntu, the vendor component of the PURL
needs to be ubuntu (instead of debian). We now set it accordingly.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
meta-isar/conf/distro/ubuntu-common.inc | 2 ++
1 file changed, 2 insertions(+)

diff --git a/meta-isar/conf/distro/ubuntu-common.inc b/meta-isar/conf/distro/ubuntu-common.inc
index 88a3b4f0..3e930c14 100644
--- a/meta-isar/conf/distro/ubuntu-common.inc
+++ b/meta-isar/conf/distro/ubuntu-common.inc
@@ -45,3 +45,5 @@ SYSTEMD_BOOTLOADER_INSTALL:jammy = "systemd:${DISTRO_ARCH}"

# snapshot mirror for reproducible builds
DISTRO_APT_SNAPSHOT_PREMIRROR ??= "(http|https)://archive.ubuntu.com/(.*) https://snapshot.ubuntu.com/\2/${ISAR_APT_SNAPSHOT_DATE}\n"
+
+SBOM_BASE_DISTRO_VENDOR ?= "ubuntu"
--
2.39.5

Christoph Steiger

unread,
Sep 17, 2025, 3:34:03 AMSep 17
to isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com, Christoph Steiger
Package the python tool debsbom for SBOM generation for Debian based
distributions.

Signed-off-by: Christoph Steiger <christop...@siemens.com>
---
...icense-description-in-pyproject.toml.patch | 28 ++++++++++++
.../python3-debsbom/files/rules | 8 ++++
.../python3-debsbom/python3-debsbom_0.0.1.bb | 44 +++++++++++++++++++
3 files changed, 80 insertions(+)
create mode 100644 meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch
create mode 100644 meta/recipes-support/python3-debsbom/files/rules
create mode 100644 meta/recipes-support/python3-debsbom/python3-debsbom_0.0.1.bb

diff --git a/meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch b/meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch
new file mode 100644
diff --git a/meta/recipes-support/python3-debsbom/files/rules b/meta/recipes-support/python3-debsbom/files/rules
new file mode 100644
index 00000000..a414114d
--- /dev/null
+++ b/meta/recipes-support/python3-debsbom/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = debsbom
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-debsbom/python3-debsbom_0.0.1.bb b/meta/recipes-support/python3-debsbom/python3-debsbom_0.0.1.bb
new file mode 100644
index 00000000..6f88c185
--- /dev/null
+++ b/meta/recipes-support/python3-debsbom/python3-debsbom_0.0.1.bb
@@ -0,0 +1,44 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/git"
+
+DEPENDS = "python3-spdx-tools"
+DEPENDS:append:bookworm = " python3-packageurl python3-cyclonedx-lib"
+DEPENDS:append:noble = " python3-packageurl python3-cyclonedx-lib"
+
+S = "${WORKDIR}/git"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ pybuild-plugin-pyproject, \
+ python3-packageurl, \
+ python3-cyclonedx-lib, \
+ python3-spdx-tools, \
+ python3-debian, \
+ python3-requests, \
+ "
+
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+
+DESCRIPTION = "debsbom generates SBOMs for Debian based distributions."
+
+SRC_URI = "git://github.com/siemens/debsbom.git;protocol=https;branch=main; \
+ file://rules \
+ file://0001-Use-old-license-description-in-pyproject.toml.patch \
+ "
+SRCREV = "c9f0a028fec9c11ab6978ad27d5bed8c12bb8e53"

Zhihang Wei

unread,
Oct 20, 2025, 4:44:41 AM (11 days ago) Oct 20
to Christoph Steiger, isar-...@googlegroups.com, jan.k...@siemens.com, felix.mo...@siemens.com, gernot....@siemens.com, cedric.h...@siemens.com
Hello,

When testing this patch set on full CI, the following test failed:
- citest.py:ReproTest.test_repro_signed

Error log follows:
[stdlog] 2025-10-07 12:02:43,784 avocado.app cibuilder L0322 ERROR|
ERROR: mc:qemuarm64-bookworm:isar-image-base-1.0-r0
do_rootfs_postprocess:
ExecutionError('/build/isar_wzh_devel_2/8/build/tmp/work/debian-bookworm-arm64/isar-image-base-qemuarm64/1.0-r0/temp/run.generate_sbom.2562126',
255, None, None)
[stdlog] 2025-10-07 12:02:43,788 avocado.app cibuilder L0322 ERROR|
ERROR: Logfile of failure stored in:
/build/isar_wzh_devel_2/8/build/tmp/work/debian-bookworm-arm64/isar-image-base-qemuarm64/1.0-r0/temp/log.do_rootfs_postprocess.2562126

[stdlog] 2025-10-07 12:02:43,788 avocado.test cibuilder L0320 INFO | Log
data follows:
[stdlog] 2025-10-07 12:02:43,789 avocado.app cibuilder L0322 ERROR|
ERROR: Task
(mc:qemuarm64-bookworm:/build/isar_wzh_devel_2/8/meta-isar/recipes-core/images/isar-image-base.bb:do_rootfs_postprocess)
failed with exit code '1'
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing python function do_rootfs_postprocess
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing shell function rootfs_do_mounts
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Shell function rootfs_do_mounts finished
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing shell function rootfs_do_qemu
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Shell function rootfs_do_qemu finished
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing shell function image_posprocess_disable_systemd_firstboot
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
Created symlink /etc/systemd/system/systemd-firstboot.service -> /dev/null.
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Shell function image_posprocess_disable_systemd_firstboot finished
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing shell function image_postprocess_sshd_key_regen
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
find:
'/build/isar_wzh_devel_2/8/build/tmp/work/debian-bookworm-arm64/isar-image-base-qemuarm64/1.0-r0/rootfs/etc/ssh/':
No such file or directory
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Shell function image_postprocess_sshd_key_regen finished
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing shell function image_postprocess_machine_id
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
uninitialized
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Shell function image_postprocess_machine_id finished
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing shell function image_postprocess_mark
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
WARNING: You are using external layers that will not be considered in
the build_id. Consider changing ISAR_RELEASE_CMD.
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
BUILD_ID="v0.11-79-g7dcbb204"
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
VARIANT="Isar target filesystem"
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
VARIANT_VERSION="1.0"
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Shell function image_postprocess_mark finished
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing shell function image_postprocess_configure
[stdlog] 2025-10-07 12:02:43,811 avocado.test cibuilder L0320 INFO | |
DEBUG: Shell function image_postprocess_configure finished
[stdlog] 2025-10-07 12:02:43,812 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing python function do_generate_sbom
[stdlog] 2025-10-07 12:02:43,812 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing shell function generate_sbom
[stdlog] 2025-10-07 12:02:43,812 avocado.test cibuilder L0320 INFO | |
ERROR:debsbom.cli:'Origin'
[stdlog] 2025-10-07 12:02:43,812 avocado.test cibuilder L0320 INFO | |
debsbom: error: 'Origin'
[stdlog] 2025-10-07 12:02:43,812 avocado.test cibuilder L0320 INFO | |
WARNING: exit code 255 from a shell command.
[stdlog] 2025-10-07 12:02:43,812 avocado.test cibuilder L0320 INFO | |
DEBUG: Python function do_generate_sbom finished
[stdlog] 2025-10-07 12:02:43,812 avocado.test cibuilder L0320 INFO | |
DEBUG: Executing shell function rootfs_do_umounts
[stdlog] 2025-10-07 12:02:43,812 avocado.test cibuilder L0320 INFO | |
DEBUG: Shell function rootfs_do_umounts finished
[stdlog] 2025-10-07 12:02:43,812 avocado.test cibuilder L0320 INFO | |
DEBUG: Python function do_rootfs_postprocess finished
[stdlog] 2025-10-07 12:02:43,812 avocado.test cibuilder L0320 INFO |
NOTE: recipe isar-image-base-1.0-r0: task do_rootfs_postprocess: Failed

I think it is complaining about some packages does not have the optional
"Origin" field. This has been fixed in debsbom repo with
034fc6af775a796cd67f5e4bfca013e99f7b8a5f, but your python-sbom receipe
pulls a earlier version which does not contain this patch.

You can redo the test on your machine using avocado:
1. Have a clean clone of isar, checkout to branch next and apply your
patches:
$ git clone -b next https://github.com/ilbers/isar.git
$ cd isar
$ git am /path-to/0001-my-contribution-to-isar.patch
2.Run kas shell, setup CI prerequisites (avocado, qemu) and cleanup:

$ ./kas/kas-container shell kas/isar.yaml --command \
    "rm -rf /work/build/conf && /work/scripts/ci_setup.sh"
3.Run the failed test:
$ cd /work/testsuite
$ avocado run citest.py:ReproTest.test_repro_signed

Best regards,
Zhihang

MOESSBAUER, Felix

unread,
Oct 20, 2025, 6:16:50 AM (11 days ago) Oct 20
to Steiger, Christoph, isar-...@googlegroups.com, w...@ilbers.de, Kiszka, Jan, cedric.h...@siemens.com, Hillier, Gernot
Hi, thanks for testing this. I noticed another bug on HOST_ARCH !=
DISTRO_ARCH when creating a derived distro (e.g. in isar-cip-core). All
these will be fixed in a v3. We plan to cut a v0.3 release this week
and then send out a patch series with the fixes.

By then, we also will have a "merging" support in the sbom tooling to
merge the SBOMs of the initrd, imaging and rootfs into a uniform
document. This should finally solve the issue of missing dependencies
in the BOM.

>
> You can redo the test on your machine using avocado:
> 1. Have a clean clone of isar, checkout to branch next and apply your
> patches:
> $ git clone -b next https://github.com/ilbers/isar.git
> $ cd isar
> $ git am /path-to/0001-my-contribution-to-isar.patch
> 2.Run kas shell, setup CI prerequisites (avocado, qemu) and cleanup:
>
> $ ./kas/kas-container shell kas/isar.yaml --command \
>     "rm -rf /work/build/conf && /work/scripts/ci_setup.sh"
> 3.Run the failed test:
> $ cd /work/testsuite
> $ avocado run citest.py:ReproTest.test_repro_signed

Many thanks, that helps a lot. Maybe this could also be added to the
contributing guide (if not already) as some of these steps were not
obvious for me.

Best regards,
Felix

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:39 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com, Felix Moessbauer
This patchset adds proper SBOM generation in the two standard formats
SPDX and CycloneDX during the rootfs generation process.

The generation is itself is handled by a SBOM generator `debsbom` [1]
which is developed as an open source project at Siemens. It is still
early in development, but it has enough features for what we require
in isar. The required dependencies which are not yet available as
Debian packages were minimally packaged directly in isar too.

This is a followup of the previous RFC [2]. Since then the series has
changed a lot. The SBOM generation was moved from a simple OE lib to
`debsbom`. This also meant the introduction of a separate chroot was
necessary. The SBOM generation process was also moved from the image
step to the rootfs step, along with a lot of minor changes and
improvements.

[1] https://github.com/siemens/debsbom
[2] https://groups.google.com/g/isar-users/c/8L-CF4BJY0I/m/p0N3o_zfAAAJ

Changes since v2:

- fix issues when HOST_ARCH != DISTRO_ARCH on derived distributions
- update debsbom to v0.3.0, which fixes the Origin: bug reported in v2
- generate SBOM for imager as well and create merged sbom of .wic image
- resend imager manifest + wic manifest patches to reduce conflicts

Note, that the patches p1-p5 are most important as they add basic SBOM
support. The remaining patches address the imager + .wic bom part,
which also can be merged later on.

Changes since v1:

- remove tarball
- refactor packaging (auto-derive python dependencies)
- only build missing packages (varies on bookworm, trixie, noble)
- add ubuntu support
- only generate sboms for supported distributions (bookworm/jammy and
onwards)
- update debsbom (includes bug fixes and more information for source
packages)

Christoph Steiger (3):
meta: package python libraries for SBOM generation
meta: package python3-debsbom
meta: add SBOM generation with debsbom

Felix Moessbauer (7):
refactor: move get_rootfs_distro from sdk into rootfs
override distro vendor in SBOM on Ubuntu
add support to add imager dependencies to BOM
wic: create uniform manifest describing all image components
qemuamd64: add IMAGER_BOM entries
imager: create SBOM of IMAGER_BOM packages
wic: create uniform SBOM describing all image components

doc/user_manual.md | 1 +
meta-isar/conf/distro/ubuntu-common.inc | 2 +
meta-isar/conf/machine/qemuamd64.conf | 1 +
meta/classes/image-tools-extension.bbclass | 29 +++++++++
meta/classes/image.bbclass | 14 +++-
meta/classes/imagetypes_wic.bbclass | 30 +++++++++
meta/classes/initramfs.bbclass | 3 +-
meta/classes/rootfs.bbclass | 16 ++++-
meta/classes/sbom.bbclass | 64 +++++++++++++++++++
meta/classes/sdk.bbclass | 10 +--
.../sbom-chroot/sbom-chroot.bb | 30 +++++++++
.../python3-beartype/files/rules | 8 +++
.../python3-beartype_0.19.0.bb | 29 +++++++++
.../files/pybuild.testfiles | 1 +
.../python3-cyclonedx-lib/files/rules | 8 +++
.../python3-cyclonedx-lib_9.1.0.bb | 48 ++++++++++++++
...icense-description-in-pyproject.toml.patch | 28 ++++++++
.../python3-debsbom/files/rules | 8 +++
.../python3-debsbom/python3-debsbom_0.3.0.bb | 45 +++++++++++++
.../python3-packageurl/files/rules | 8 +++
.../python3-packageurl_0.16.0.bb | 33 ++++++++++
.../python3-py-serializable/files/rules | 8 +++
.../python3-py-serializable_2.0.0.bb | 38 +++++++++++
.../python3-spdx-tools/files/rules | 25 ++++++++
.../python3-spdx-tools_0.8.3.bb | 46 +++++++++++++
25 files changed, 521 insertions(+), 12 deletions(-)
create mode 100644 meta/classes/sbom.bbclass
create mode 100644 meta/recipes-devtools/sbom-chroot/sbom-chroot.bb
create mode 100644 meta/recipes-support/python3-beartype/files/rules
create mode 100644 meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/files/pybuild.testfiles
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/files/rules
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/python3-cyclonedx-lib_9.1.0.bb
create mode 100644 meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch
create mode 100644 meta/recipes-support/python3-debsbom/files/rules
create mode 100644 meta/recipes-support/python3-debsbom/python3-debsbom_0.3.0.bb
create mode 100644 meta/recipes-support/python3-packageurl/files/rules
create mode 100644 meta/recipes-support/python3-packageurl/python3-packageurl_0.16.0.bb
create mode 100644 meta/recipes-support/python3-py-serializable/files/rules
create mode 100644 meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
create mode 100644 meta/recipes-support/python3-spdx-tools/files/rules
create mode 100644 meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb

--
2.51.0

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:42 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com, Felix Moessbauer
This helper is rootfs (not sdk) specific and can be re-used to reliably
compute the distro of a rootfs (also transient ones like the ones used
by sbuild). As the SDK always is included into a rootfs, we just move it
there and make it reusable.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
meta/classes/rootfs.bbclass | 9 +++++++++
meta/classes/sdk.bbclass | 8 --------
2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/meta/classes/rootfs.bbclass b/meta/classes/rootfs.bbclass
index 4d73bfec..6413c057 100644
--- a/meta/classes/rootfs.bbclass
+++ b/meta/classes/rootfs.bbclass
@@ -26,6 +26,15 @@ ROOTFS_PACKAGE_SUFFIX ?= "${PN}-${DISTRO}-${DISTRO_ARCH}"
# path to deploy stubbed versions of initrd update scripts during do_rootfs_install
ROOTFS_STUBS_DIR = "/usr/local/isar-sbin"

+# helper to compute the rootfs distro also under cross building
+def get_rootfs_distro(d):
+ host_arch = d.getVar('HOST_ARCH')
+ distro_arch = d.getVar('DISTRO_ARCH')
+ if host_arch == distro_arch:
+ return d.getVar('DISTRO')
+ else:
+ return d.getVar('HOST_DISTRO')
+
# Useful environment variables:
export E = "${@ isar_export_proxies(d)}"
export DEBIAN_FRONTEND = "noninteractive"
diff --git a/meta/classes/sdk.bbclass b/meta/classes/sdk.bbclass
index 46436d97..00cae0da 100644
--- a/meta/classes/sdk.bbclass
+++ b/meta/classes/sdk.bbclass
@@ -43,14 +43,6 @@ SDK_PREINSTALL += " \
devscripts \
equivs"

-def get_rootfs_distro(d):
- host_arch = d.getVar('HOST_ARCH')
- distro_arch = d.getVar('DISTRO_ARCH')
- if host_arch == distro_arch:
- return d.getVar('DISTRO')
- else:
- return d.getVar('HOST_DISTRO')
-
# rootfs/image overrides for the SDK
ROOTFS_ARCH:class-sdk = "${HOST_ARCH}"
ROOTFS_DISTRO:class-sdk = "${@get_rootfs_distro(d)}"
--
2.51.0

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:42 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com
From: Christoph Steiger <christop...@siemens.com>

Package python libraries for SBOM generation in isar. The packages are
unfortunately not (yet) packaged in Debian, thats why we need to do it
here. With these libraries it is now possible to easily create CDX and
SPDX SBOMs in different file formats.

Signed-off-by: Christoph Steiger <christop...@siemens.com>
---
.../python3-beartype/files/rules | 8 ++++
.../python3-beartype_0.19.0.bb | 29 +++++++++++
.../files/pybuild.testfiles | 1 +
.../python3-cyclonedx-lib/files/rules | 8 ++++
.../python3-cyclonedx-lib_9.1.0.bb | 48 +++++++++++++++++++
.../python3-packageurl/files/rules | 8 ++++
.../python3-packageurl_0.16.0.bb | 33 +++++++++++++
.../python3-py-serializable/files/rules | 8 ++++
.../python3-py-serializable_2.0.0.bb | 38 +++++++++++++++
.../python3-spdx-tools/files/rules | 25 ++++++++++
.../python3-spdx-tools_0.8.3.bb | 46 ++++++++++++++++++
11 files changed, 252 insertions(+)
create mode 100644 meta/recipes-support/python3-beartype/files/rules
create mode 100644 meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/files/pybuild.testfiles
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/files/rules
create mode 100644 meta/recipes-support/python3-cyclonedx-lib/python3-cyclonedx-lib_9.1.0.bb
create mode 100644 meta/recipes-support/python3-packageurl/files/rules
create mode 100644 meta/recipes-support/python3-packageurl/python3-packageurl_0.16.0.bb
create mode 100644 meta/recipes-support/python3-py-serializable/files/rules
create mode 100644 meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
create mode 100644 meta/recipes-support/python3-spdx-tools/files/rules
create mode 100644 meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb

diff --git a/meta/recipes-support/python3-beartype/files/rules b/meta/recipes-support/python3-beartype/files/rules
new file mode 100644
index 00000000..0ca517a1
--- /dev/null
+++ b/meta/recipes-support/python3-beartype/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = beartype
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb b/meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
new file mode 100644
index 00000000..b8bc2708
--- /dev/null
+++ b/meta/recipes-support/python3-beartype/python3-beartype_0.19.0.bb
@@ -0,0 +1,29 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/beartype-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), dh-python, python3-all, python3-setuptools, pybuild-plugin-pyproject, python3-hatchling"
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+# this is 01/01/1980, any earlier and zip in the wheel building process will not accept it
+DEBIAN_CHANGELOG_TIMESTAMP = "315532800"
+DESCRIPTION = "Unbearably fast near-real-time hybrid runtime-static type-checking in pure Python."
+
+SRC_URI = "\
+ https://github.com/beartype/beartype/archive/refs/tags/v0.19.0.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "e7ad00eebf527d60f30e0b391209b561dabd2074b608c50e26c94c2d8250a6cd"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-cyclonedx-lib/files/pybuild.testfiles b/meta/recipes-support/python3-cyclonedx-lib/files/pybuild.testfiles
new file mode 100644
index 00000000..cc736a36
--- /dev/null
+++ b/meta/recipes-support/python3-cyclonedx-lib/files/pybuild.testfiles
@@ -0,0 +1 @@
+pyproject.toml
diff --git a/meta/recipes-support/python3-cyclonedx-lib/files/rules b/meta/recipes-support/python3-cyclonedx-lib/files/rules
new file mode 100644
index 00000000..fe72dd1a
--- /dev/null
+++ b/meta/recipes-support/python3-cyclonedx-lib/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = cyclonedx-python-lib
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-cyclonedx-lib/python3-cyclonedx-lib_9.1.0.bb b/meta/recipes-support/python3-cyclonedx-lib/python3-cyclonedx-lib_9.1.0.bb
new file mode 100644
index 00000000..738ed1b3
--- /dev/null
+++ b/meta/recipes-support/python3-cyclonedx-lib/python3-cyclonedx-lib_9.1.0.bb
@@ -0,0 +1,48 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+DEPENDS:append:bookworm = " python3-packageurl python3-py-serializable"
+DEPENDS:append:noble = " python3-packageurl python3-py-serializable"
+
+S = "${WORKDIR}/cyclonedx_python_lib-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
+ dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ pybuild-plugin-pyproject, \
+ python3-poetry, \
+ python3-py-serializable, \
+ python3-packageurl, \
+ python3-sortedcontainers, \
+ python3-ddt, \
+ python3-defusedxml, \
+ python3-license-expression, \
+ python3-jsonschema, \
+ python3-lxml, \
+ "
+
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+
+DESCRIPTION = "Library for serializing and deserializing Python Objects to and from JSON and XML."
+
+SRC_URI = "\
+ https://github.com/CycloneDX/cyclonedx-python-lib/releases/download/v9.1.0/cyclonedx_python_lib-9.1.0.tar.gz \
+ file://rules \
+ file://pybuild.testfiles \
+ "
+SRC_URI[sha256sum] = "86935f2c88a7b47a529b93c724dbd3e903bc573f6f8bd977628a7ca1b5dadea1"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ cp "${WORKDIR}"/pybuild.testfiles "${S}"/debian
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-packageurl/files/rules b/meta/recipes-support/python3-packageurl/files/rules
new file mode 100644
index 00000000..50e1b74c
--- /dev/null
+++ b/meta/recipes-support/python3-packageurl/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = packageurl-python
+export PYBUILD_SYSTEM = distutils
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-packageurl/python3-packageurl_0.16.0.bb b/meta/recipes-support/python3-packageurl/python3-packageurl_0.16.0.bb
new file mode 100644
index 00000000..27209429
--- /dev/null
+++ b/meta/recipes-support/python3-packageurl/python3-packageurl_0.16.0.bb
@@ -0,0 +1,33 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/packageurl_python-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "debhelper (>= 11~), \
+ dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ "
+
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+
+DESCRIPTION = "A purl aka. Package URL parser and builder"
+
+SRC_URI = "\
+ https://github.com/package-url/packageurl-python/releases/download/v0.16.0/packageurl_python-0.16.0.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "69e3bf8a3932fe9c2400f56aaeb9f86911ecee2f9398dbe1b58ec34340be365d"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-py-serializable/files/rules b/meta/recipes-support/python3-py-serializable/files/rules
new file mode 100644
index 00000000..0cf845dd
--- /dev/null
+++ b/meta/recipes-support/python3-py-serializable/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = py-serializable
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb b/meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
new file mode 100644
index 00000000..5bc48c0f
--- /dev/null
+++ b/meta/recipes-support/python3-py-serializable/python3-py-serializable_2.0.0.bb
@@ -0,0 +1,38 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/py_serializable-${PV}"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = " \
+ dh-sequence-python3, \
+ pybuild-plugin-pyproject, \
+ python3-all, \
+ python3-defusedxml, \
+ python3-lxml, \
+ python3-poetry-core, \
+ python3-setuptools, \
+ xmldiff, \
+"
+
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+
+DESCRIPTION = "Library for serializing and deserializing Python Objects to and from JSON and XML."
+
+SRC_URI = "\
+ https://github.com/madpah/serializable/releases/download/v2.0.0/py_serializable-2.0.0.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "e9e6491dd7d29c31daf1050232b57f9657f9e8a43b867cca1ff204752cf420a5"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
diff --git a/meta/recipes-support/python3-spdx-tools/files/rules b/meta/recipes-support/python3-spdx-tools/files/rules
new file mode 100644
index 00000000..ac87528a
--- /dev/null
+++ b/meta/recipes-support/python3-spdx-tools/files/rules
@@ -0,0 +1,25 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = spdx-tools
+export PYBUILD_SYSTEM = distutils
+
+# skip tests that require hard-to-package dependencies and tests that rely on relative file paths
+# TODO: figure out a way to make these tests work
+export PYBUILD_TEST_ARGS=--ignore tests/spdx3/validation/json_ld/test_shacl_validation.py \
+ -k 'not test_examples \
+ and not test_parse_from_file \
+ and not test_annotation_parser \
+ and not test_snippet_parser \
+ and not test_creation_info_parser \
+ and not test_json_ld_writer \
+ and not test_extracted_licensing_info_parser \
+ and not test_parse_file \
+ and not test_package_parser \
+ and not test_relationship_parser \
+ and not test_graph_parsing_function \
+ and not test_license_expression_parser \
+ '
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb b/meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb
new file mode 100644
index 00000000..30d090a9
--- /dev/null
+++ b/meta/recipes-support/python3-spdx-tools/python3-spdx-tools_0.8.3.bb
@@ -0,0 +1,46 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/tools-python-${PV}"
+
+DEPENDS:append:bookworm = " python3-beartype"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ python3-beartype, \
+ python3-semantic-version, \
+ python3-license-expression, \
+ python3-pytest <!nocheck>, \
+ python3-rdflib, \
+ python3-uritools, \
+ python3-ply, \
+ python3-click, \
+ python3-xmltodict, \
+ python3-yaml, \
+ "
+
+DEBIAN_DEPENDS = "\${python3:Depends}, \${misc:Depends}"
+DEB_BUILD_PROFILES += "nocheck"
+DEB_BUILD_OPTIONS += "nocheck"
+
+DESCRIPTION = "SPDX parser and tools."
+
+SRC_URI = "\
+ https://github.com/spdx/tools-python/archive/refs/tags/v0.8.3.tar.gz \
+ file://rules \
+ "
+SRC_URI[sha256sum] = "17cb0140adbaefb58819c9d5d56060dc6a70c673a854fa9bd882ecfa4e062a7f"
+
+do_prepare_build[cleandirs] += "${S}/debian"
+do_prepare_build() {
+ deb_debianize
+}
--
2.51.0

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:43 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com
From: Christoph Steiger <christop...@siemens.com>

Package the python tool debsbom for SBOM generation for Debian based
distributions.

Signed-off-by: Christoph Steiger <christop...@siemens.com>
---
...icense-description-in-pyproject.toml.patch | 28 ++++++++++++
.../python3-debsbom/files/rules | 8 ++++
.../python3-debsbom/python3-debsbom_0.3.0.bb | 45 +++++++++++++++++++
3 files changed, 81 insertions(+)
create mode 100644 meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch
create mode 100644 meta/recipes-support/python3-debsbom/files/rules
create mode 100644 meta/recipes-support/python3-debsbom/python3-debsbom_0.3.0.bb

diff --git a/meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch b/meta/recipes-support/python3-debsbom/files/0001-Use-old-license-description-in-pyproject.toml.patch
new file mode 100644
new file mode 100644
index 00000000..a414114d
--- /dev/null
+++ b/meta/recipes-support/python3-debsbom/files/rules
@@ -0,0 +1,8 @@
+#!/usr/bin/make -f
+
+#export DH_VERBOSE = 1
+export PYBUILD_NAME = debsbom
+export PYBUILD_SYSTEM = pyproject
+
+%:
+ dh $@ --with python3 --buildsystem=pybuild
diff --git a/meta/recipes-support/python3-debsbom/python3-debsbom_0.3.0.bb b/meta/recipes-support/python3-debsbom/python3-debsbom_0.3.0.bb
new file mode 100644
index 00000000..6aec2429
--- /dev/null
+++ b/meta/recipes-support/python3-debsbom/python3-debsbom_0.3.0.bb
@@ -0,0 +1,45 @@
+# This software is a part of ISAR.
+# Copyright (c) Siemens, 2025
+#
+# SPDX-License-Identifier: MIT
+
+inherit dpkg
+
+FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
+
+S = "${WORKDIR}/git"
+
+DEPENDS = "python3-spdx-tools"
+DEPENDS:append:bookworm = " python3-packageurl python3-cyclonedx-lib"
+DEPENDS:append:noble = " python3-packageurl python3-cyclonedx-lib"
+
+S = "${WORKDIR}/git"
+
+MAINTAINER = "Christoph Steiger <christop...@siemens.com>"
+DPKG_ARCH = "all"
+DEBIAN_BUILD_DEPENDS = "dh-python, \
+ python3-all, \
+ python3-setuptools, \
+ pybuild-plugin-pyproject, \
+ python3-packageurl, \
+ python3-cyclonedx-lib, \
+ python3-spdx-tools, \
+ python3-debian, \
+ python3-requests, \
+ python3-zstandard, \
+ "
+
+DEBIAN_DEPENDS = "python3-apt, \${python3:Depends}, \${misc:Depends}"
+
+DESCRIPTION = "debsbom generates SBOMs for Debian based distributions."
+
+SRC_URI = "git://github.com/siemens/debsbom.git;protocol=https;branch=main; \
+ file://rules \
+ file://0001-Use-old-license-description-in-pyproject.toml.patch \
+ "
+SRCREV = "477381cc46e9921cab6a0fd8e875fd409e9edafe"

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:46 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com, Felix Moessbauer
From: Christoph Steiger <christop...@siemens.com>

Generate SBOMs for every rootfs that is created. These SBOMs are placed
in the image deploy directory.

For the generation a small chroot with debsbom installed is created and
from that the rootfs of the image is scanned.

The sbom generation is bound to the rootfs feature `generate-sbom`
which is activated per default now.

Signed-off-by: Christoph Steiger <christop...@siemens.com>
Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
meta/classes/image.bbclass | 8 ++-
meta/classes/initramfs.bbclass | 3 +-
meta/classes/rootfs.bbclass | 7 +-
meta/classes/sbom.bbclass | 64 +++++++++++++++++++
meta/classes/sdk.bbclass | 2 +-
.../sbom-chroot/sbom-chroot.bb | 30 +++++++++
6 files changed, 110 insertions(+), 4 deletions(-)
create mode 100644 meta/classes/sbom.bbclass
create mode 100644 meta/recipes-devtools/sbom-chroot/sbom-chroot.bb

diff --git a/meta/classes/image.bbclass b/meta/classes/image.bbclass
index bd1b8552..220f5aa3 100644
--- a/meta/classes/image.bbclass
+++ b/meta/classes/image.bbclass
@@ -66,7 +66,13 @@ inherit multiarch
inherit essential

ROOTFSDIR = "${IMAGE_ROOTFS}"
-ROOTFS_FEATURES += "clean-package-cache clean-pycache generate-manifest export-dpkg-status clean-log-files clean-debconf-cache"
+ROOTFS_FEATURES += "clean-package-cache clean-pycache generate-manifest export-dpkg-status clean-log-files clean-debconf-cache generate-sbom"
+# only supported from bookworm / jammy on
+ROOTFS_FEATURES:remove:buster = "generate-sbom"
+ROOTFS_FEATURES:remove:bullseye = "generate-sbom"
+ROOTFS_FEATURES:remove:jammy = "generate-sbom"
+ROOTFS_FEATURES:remove:focal = "generate-sbom"
+
# when using a custom initrd, do not generate one as part of the image rootfs
ROOTFS_FEATURES += "${@ '' if d.getVar('INITRD_IMAGE') == '' else 'no-generate-initrd'}"
ROOTFS_PACKAGES += "${IMAGE_PREINSTALL} ${@isar_multiarch_packages('IMAGE_INSTALL', d)}"
diff --git a/meta/classes/initramfs.bbclass b/meta/classes/initramfs.bbclass
index 658ef0ac..e9b66646 100644
--- a/meta/classes/initramfs.bbclass
+++ b/meta/classes/initramfs.bbclass
@@ -21,11 +21,12 @@ INITRAMFS_FULLNAME = "${PN}-${DISTRO}-${MACHINE}"
# Bill-of-material
ROOTFS_MANIFEST_DEPLOY_DIR = "${DEPLOY_DIR_IMAGE}"
ROOTFS_PACKAGE_SUFFIX = "${INITRAMFS_FULLNAME}"
+SBOM_DISTRO_NAME:append = "-initramfs"

DEPENDS += "${INITRAMFS_INSTALL}"

ROOTFSDIR = "${INITRAMFS_ROOTFS}"
-ROOTFS_FEATURES = "generate-manifest"
+ROOTFS_FEATURES = "generate-manifest generate-sbom"
ROOTFS_PACKAGES = "initramfs-tools ${INITRAMFS_PREINSTALL} ${INITRAMFS_INSTALL}"

inherit rootfs
diff --git a/meta/classes/rootfs.bbclass b/meta/classes/rootfs.bbclass
index 6413c057..13b04a8c 100644
--- a/meta/classes/rootfs.bbclass
+++ b/meta/classes/rootfs.bbclass
@@ -3,6 +3,8 @@

inherit deb-dl-dir

+inherit sbom
+
ROOTFS_ARCH ?= "${DISTRO_ARCH}"
ROOTFS_DISTRO ?= "${DISTRO}"
ROOTFS_PACKAGES ?= ""
@@ -450,6 +452,9 @@ cache_dbg_pkgs() {
fi
}

+# The sbom generator needs the apt-cache, hence run before cleaning it
+ROOTFS_POSTPROCESS_COMMAND += "${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'do_generate_sbom', '', d)}"
+
ROOTFS_POSTPROCESS_COMMAND += "${@bb.utils.contains('ROOTFS_FEATURES', 'clean-package-cache', 'rootfs_postprocess_clean_package_cache', '', d)}"
rootfs_postprocess_clean_package_cache() {
sudo -E chroot '${ROOTFSDIR}' \
@@ -614,7 +619,7 @@ python do_rootfs() {
}
addtask rootfs before do_build

-do_rootfs_postprocess[depends] = "base-apt:do_cache isar-apt:do_cache_config"
+do_rootfs_postprocess[depends] = "base-apt:do_cache isar-apt:do_cache_config ${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'sbom-chroot:do_sbomchroot_deploy', '', d)}"

SSTATETASKS += "do_rootfs_install"
SSTATECREATEFUNCS += "rootfs_install_sstate_prepare"
diff --git a/meta/classes/sbom.bbclass b/meta/classes/sbom.bbclass
new file mode 100644
index 00000000..fd41296c
--- /dev/null
+++ b/meta/classes/sbom.bbclass
@@ -0,0 +1,64 @@
+# This software is a part of ISAR.
+# Copyright (C) 2025 Siemens
+#
+# SPDX-License-Identifier: MIT
+
+# sbom type to generate, accepted are "cdx" or "spdx"
+SBOM_TYPES ?= "spdx cdx"
+
+SBOM_DEBSBOM_TYPE_ARGS = "${@"-t " + " -t ".join(d.getVar("SBOM_TYPES").split())}"
+
+# general user variables
+SBOM_DISTRO_SUPPLIER ?= "ISAR"
+SBOM_DISTRO_NAME ?= "ISAR-Debian-GNU-Linux"
+SBOM_DISTRO_VERSION ?= "1"
+SBOM_DISTRO_SUMMARY ?= "Linux distribution built with ISAR"
+SBOM_BASE_DISTRO_VENDOR ??= "debian"
+SBOM_DOCUMENT_UUID ?= ""
+
+# SPDX specific user variables
+SBOM_SPDX_NAMESPACE_PREFIX ?= "https://spdx.org/spdxdocs"
+
+DEPLOY_DIR_SBOM = "${DEPLOY_DIR_IMAGE}"
+
+SBOM_DIR = "${DEPLOY_DIR}/sbom"
+SBOM_CHROOT = "${SBOM_DIR}/sbom-chroot"
+
+# adapted from the isar-cip-core image_uuid.bbclass
+def generate_document_uuid(d, warn_not_repr=True):
+ import uuid
+
+ base_hash = d.getVar("BB_TASKHASH")
+ if base_hash is None:
+ if warn_not_repr:
+ bb.warn("no BB_TASKHASH available, SBOM UUID is not reproducible")
+ return uuid.uuid4()
+ return str(uuid.UUID(base_hash[:32], version=4))
+
+def sbom_doc_uuid(d):
+ if not d.getVar("SBOM_DOCUMENT_UUID"):
+ d.setVar("SBOM_DOCUMENT_UUID", generate_document_uuid(d))
+
+generate_sbom() {
+ sudo mkdir -p ${SBOM_CHROOT}/mnt/rootfs ${SBOM_CHROOT}/mnt/deploy-dir
+
+ TIMESTAMP=$(date --iso-8601=s -d @${SOURCE_DATE_EPOCH})
+ bwrap \
+ --unshare-user \
+ --unshare-pid \
+ --bind ${SBOM_CHROOT} / \
+ --bind ${ROOTFSDIR} /mnt/rootfs \
+ --bind ${DEPLOY_DIR_SBOM} /mnt/deploy-dir \
+ -- debsbom -v generate ${SBOM_DEBSBOM_TYPE_ARGS} -r /mnt/rootfs -o /mnt/deploy-dir/'${PN}-${DISTRO}-${MACHINE}' \
+ --distro-name '${SBOM_DISTRO_NAME}' --distro-supplier '${SBOM_DISTRO_SUPPLIER}' \
+ --distro-version '${SBOM_DISTRO_VERSION}' --distro-arch '${DISTRO_ARCH}' \
+ --base-distro-vendor '${SBOM_BASE_DISTRO_VENDOR}' \
+ --cdx-serialnumber '${SBOM_DOCUMENT_UUID}' \
+ --spdx-namespace '${SBOM_SPDX_NAMESPACE_PREFIX}'-'${SBOM_DOCUMENT_UUID}' \
+ --timestamp $TIMESTAMP
+}
+
+python do_generate_sbom() {
+ sbom_doc_uuid(d)
+ bb.build.exec_func("generate_sbom", d)
+}
diff --git a/meta/classes/sdk.bbclass b/meta/classes/sdk.bbclass
index 00cae0da..d57269e5 100644
--- a/meta/classes/sdk.bbclass
+++ b/meta/classes/sdk.bbclass
@@ -47,7 +47,7 @@ SDK_PREINSTALL += " \
ROOTFS_ARCH:class-sdk = "${HOST_ARCH}"
ROOTFS_DISTRO:class-sdk = "${@get_rootfs_distro(d)}"
ROOTFS_PACKAGES:class-sdk = "sdk-files ${SDK_TOOLCHAIN} ${SDK_PREINSTALL} ${@isar_multiarch_packages('SDK_INSTALL', d)}"
-ROOTFS_FEATURES:append:class-sdk = " clean-package-cache generate-manifest export-dpkg-status"
+ROOTFS_FEATURES:append:class-sdk = " clean-package-cache generate-manifest export-dpkg-status generate-sbom"
ROOTFS_MANIFEST_DEPLOY_DIR:class-sdk = "${DEPLOY_DIR_SDKCHROOT}"
ROOTFS_DPKGSTATUS_DEPLOY_DIR:class-sdk = "${DEPLOY_DIR_SDKCHROOT}"

diff --git a/meta/recipes-devtools/sbom-chroot/sbom-chroot.bb b/meta/recipes-devtools/sbom-chroot/sbom-chroot.bb
new file mode 100644
index 00000000..58200382
--- /dev/null
+++ b/meta/recipes-devtools/sbom-chroot/sbom-chroot.bb
@@ -0,0 +1,30 @@
+# This software is a part of ISAR.
+#
+# Copyright (C) 2025 Siemens
+
+LICENSE = "gpl-2.0"
+LIC_FILES_CHKSUM = "file://${LAYERDIR_core}/licenses/COPYING.GPLv2;md5=751419260aa954499f7abaabaa882bbe"
+
+PV = "1.0"
+
+inherit rootfs
+
+ROOTFS_ARCH = "${HOST_ARCH}"
+ROOTFS_DISTRO = "${@get_rootfs_distro(d)}"
+ROOTFS_BASE_DISTRO = "${HOST_BASE_DISTRO}"
+
+ROOTFS_FEATURES = "no-generate-initrd"
+ROOTFS_INSTALL_COMMAND:remove = "rootfs_restore_initrd_tooling"
+
+# additional packages for the SBOM chroot
+SBOM_IMAGE_INSTALL = "python3-debsbom"
+DEPENDS += "python3-debsbom"
+
+ROOTFSDIR = "${WORKDIR}/rootfs"
+ROOTFS_PACKAGES = "${SBOM_IMAGE_INSTALL}"
+
+do_sbomchroot_deploy[dirs] = "${SBOM_DIR}"
+do_sbomchroot_deploy() {
+ ln -Tfsr "${ROOTFSDIR}" "${SBOM_CHROOT}"
+}
+addtask do_sbomchroot_deploy before do_build after do_rootfs
--
2.51.0

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:48 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com, Felix Moessbauer
When generating an SBOM for Ubuntu, the vendor component of the PURL
needs to be ubuntu (instead of debian). We now set it accordingly.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
meta-isar/conf/distro/ubuntu-common.inc | 2 ++
1 file changed, 2 insertions(+)

diff --git a/meta-isar/conf/distro/ubuntu-common.inc b/meta-isar/conf/distro/ubuntu-common.inc
index f1e8a1d6..ffc41afe 100644
--- a/meta-isar/conf/distro/ubuntu-common.inc
+++ b/meta-isar/conf/distro/ubuntu-common.inc
@@ -45,3 +45,5 @@ SYSTEMD_BOOTLOADER_INSTALL:jammy = "systemd:${DISTRO_ARCH}"

# snapshot mirror for reproducible builds
DISTRO_APT_SNAPSHOT_PREMIRROR ??= "(http|https)://archive.ubuntu.com/(.*) https://snapshot.ubuntu.com/\2/${ISAR_APT_SNAPSHOT_DATE_INTERNAL}\n"
+
+SBOM_BASE_DISTRO_VENDOR ?= "ubuntu"
--
2.51.0

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:49 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com, Felix Moessbauer
Currently the imager dependencies which end up in the image are not
tracked in any BOM (e.g. the manifest file). As these cannot be
automatically derived from the IMAGER_INSTALL packages, we add a new
variable IMAGER_BOM that takes a list of binary packages which are
looked-up using dpkg-query during imaging and added to a local manifest.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
doc/user_manual.md | 1 +
meta/classes/image-tools-extension.bbclass | 7 +++++++
meta/classes/image.bbclass | 6 ++++++
3 files changed, 14 insertions(+)

diff --git a/doc/user_manual.md b/doc/user_manual.md
index 30d60d4c..2dd80101 100644
--- a/doc/user_manual.md
+++ b/doc/user_manual.md
@@ -455,6 +455,7 @@ Some other variables include:
- `FILESEXTRAPATHS` - The default directories BitBake uses when it processes recipes are initially defined by the FILESPATH variable. You can extend FILESPATH variable by using FILESEXTRAPATHS.
- `FILESOVERRIDES` - A subset of OVERRIDES used by the build system for creating FILESPATH. The FILESOVERRIDES variable uses overrides to automatically extend the FILESPATH variable.
- `IMAGER_INSTALL` - The list of package dependencies for an imager like wic.
+ - `IMAGER_BOM` - The list of packages that should be added to the image BOM (e.g. the bootloader). These packages must also be available in the imager rootfs.

---

diff --git a/meta/classes/image-tools-extension.bbclass b/meta/classes/image-tools-extension.bbclass
index 5e248f2e..65258a5a 100644
--- a/meta/classes/image-tools-extension.bbclass
+++ b/meta/classes/image-tools-extension.bbclass
@@ -20,6 +20,7 @@ SCHROOT_MOUNTS += "${REPO_ISAR_DIR}/${DISTRO}:/isar-apt"

imager_run() {
local_install="${@(d.getVar("INSTALL_%s" % d.getVar("BB_CURRENTTASK")) or '').strip()}"
+ local_bom="${@(d.getVar("BOM_%s" % d.getVar("BB_CURRENTTASK")) or '').strip()}"

schroot_create_configs
insert_mounts
@@ -70,6 +71,12 @@ EOAPT

schroot -r -c ${session_id} "$@"

+ if [ -n "${local_bom}" ]; then
+ schroot -r -c ${session_id} -d / -- \
+ dpkg-query -W -f='${source:Package}|${source:Version}|${Package}:${Architecture}|${Version}\n' ${local_bom} > \
+ ${WORKDIR}/imager.manifest
+ fi
+
schroot -e -c ${session_id}

remove_mounts
diff --git a/meta/classes/image.bbclass b/meta/classes/image.bbclass
index 220f5aa3..cbedd787 100644
--- a/meta/classes/image.bbclass
+++ b/meta/classes/image.bbclass
@@ -190,6 +190,7 @@ python() {

imager_install = set()
imager_build_deps = set()
+ imager_bom = set()
conversion_install = set()
for bt in basetypes:
local_imager_install = set()
@@ -220,6 +221,8 @@ python() {
local_imager_install.add(dep)
for dep in (d.getVar('IMAGER_BUILD_DEPS:' + bt_clean) or '').split():
imager_build_deps.add(dep)
+ for dep in (d.getVar('IMAGER_BOM:' + bt_clean) or '').split():
+ imager_bom.add(dep)

# construct image command
image_cmd = localdata.getVar('IMAGE_CMD:' + bt_clean)
@@ -294,11 +297,14 @@ python() {
bb.build.addtask(task, 'do_image', after, d)

# set per type imager dependencies
+ d.setVar('BOM_image_%s' % bt_clean, d.getVar('IMAGER_BOM'))
+ d.appendVar('BOM_image_%s' % bt_clean, ' ' + ' '.join(sorted(imager_bom)))
d.setVar('INSTALL_image_%s' % bt_clean, d.getVar('IMAGER_INSTALL'))
d.appendVar('INSTALL_image_%s' % bt_clean, ' ' + ' '.join(sorted(local_imager_install | local_conversion_install)))
d.appendVarFlag(task, 'vardeps', ' INSTALL_image_%s' % bt_clean)

d.appendVar('IMAGER_INSTALL', ' ' + ' '.join(sorted(imager_install | conversion_install)))
+ d.appendVar('IMAGER_BOM', ' ' + ' '.join(sorted(imager_bom)))
d.appendVar('IMAGER_BUILD_DEPS', ' ' + ' '.join(sorted(imager_build_deps)))
}

--
2.51.0

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:54 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com, Felix Moessbauer
This uses the same interface as the .manifest file, but adds the
packagse to an SBOM.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
meta/classes/image-tools-extension.bbclass | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

diff --git a/meta/classes/image-tools-extension.bbclass b/meta/classes/image-tools-extension.bbclass
index 65258a5a..2576c5ef 100644
--- a/meta/classes/image-tools-extension.bbclass
+++ b/meta/classes/image-tools-extension.bbclass
@@ -75,6 +75,8 @@ EOAPT
schroot -r -c ${session_id} -d / -- \
dpkg-query -W -f='${source:Package}|${source:Version}|${Package}:${Architecture}|${Version}\n' ${local_bom} > \
${WORKDIR}/imager.manifest
+
+ ${@bb.utils.contains('ROOTFS_FEATURES', 'generate-sbom', 'generate_imager_sbom', '', d)}
fi

schroot -e -c ${session_id}
@@ -82,3 +84,23 @@ EOAPT
remove_mounts
schroot_delete_configs
}
+
+generate_imager_sbom() {
+ TIMESTAMP=$(date --iso-8601=s -d @${SOURCE_DATE_EPOCH})
+ sbom_document_uuid="${@d.getVar('SBOM_DOCUMENT_UUID') or generate_document_uuid(d, False)}"
+ bwrap \
+ --unshare-user \
+ --unshare-pid \
+ --bind ${SBOM_CHROOT} / \
+ --bind $schroot_dir /mnt/rootfs \
+ --bind ${WORKDIR} /mnt/deploy-dir \
+ -- debsbom -vv generate ${SBOM_DEBSBOM_TYPE_ARGS} \
+ --from-pkglist -r /mnt/rootfs -o /mnt/deploy-dir/'${PN}-${DISTRO}-${MACHINE}-imager' \
+ --distro-name '${SBOM_DISTRO_NAME}-Imager' --distro-supplier '${SBOM_DISTRO_SUPPLIER}' \
+ --distro-version '${SBOM_DISTRO_VERSION}' --distro-arch '${DISTRO_ARCH}' \
+ --base-distro-vendor '${SBOM_BASE_DISTRO_VENDOR}' \
+ --cdx-serialnumber $sbom_document_uuid \
+ --spdx-namespace '${SBOM_SPDX_NAMESPACE_PREFIX}'-$sbom_document_uuid \
+ --timestamp $TIMESTAMP \
+ < ${WORKDIR}/imager.manifest
+}
--
2.51.0

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:54 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com, Felix Moessbauer
To give an example how to add components to the imager BOM, we set the
corresponding variable for the qemuamd64 target.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
meta-isar/conf/machine/qemuamd64.conf | 1 +
1 file changed, 1 insertion(+)

diff --git a/meta-isar/conf/machine/qemuamd64.conf b/meta-isar/conf/machine/qemuamd64.conf
index 7d5987c6..8d0753a8 100644
--- a/meta-isar/conf/machine/qemuamd64.conf
+++ b/meta-isar/conf/machine/qemuamd64.conf
@@ -7,6 +7,7 @@ KERNEL_NAME ?= "amd64"

IMAGE_FSTYPES ?= "wic"
WKS_FILE ?= "sdimage-efi"
+IMAGER_BOM:wic += "${GRUB_BOOTLOADER_INSTALL}"
IMAGER_INSTALL:wic += "${GRUB_BOOTLOADER_INSTALL}"

QEMU_ARCH ?= "x86_64"
--
2.51.0

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:54 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com, Felix Moessbauer
A wic image consists of potentially many different components. All these
should be covered by a single BOM.

After creating the wic image, we collect the individual manifest files
(rootfs, initrd, imaging), deduplicate it and deploy it into the image
deploy dir (as .wic.manifest).

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
meta/classes/imagetypes_wic.bbclass | 5 +++++
1 file changed, 5 insertions(+)

diff --git a/meta/classes/imagetypes_wic.bbclass b/meta/classes/imagetypes_wic.bbclass
index fb0b81a9..c75d481d 100644
--- a/meta/classes/imagetypes_wic.bbclass
+++ b/meta/classes/imagetypes_wic.bbclass
@@ -196,4 +196,9 @@ EOIMAGER
sudo chown -R $(stat -c "%U" ${LAYERDIR_core}) ${LAYERDIR_core} ${LAYERDIR_isar} ${SCRIPTSDIR} || true
sudo chown -R $(id -u):$(id -g) "${DEPLOY_DIR_IMAGE}/${IMAGE_FULLNAME}.wic"*
rm -rf ${IMAGE_ROOTFS}/../pseudo
+
+ cat ${DEPLOY_DIR_IMAGE}/${IMAGE_FULLNAME}.manifest \
+ ${DEPLOY_DIR_IMAGE}/${INITRD_DEPLOY_FILE}.manifest \
+ ${WORKDIR}/imager.manifest 2>/dev/null \
+ | sort | uniq > "${DEPLOY_DIR_IMAGE}/${IMAGE_FULLNAME}.wic.manifest"
}
--
2.51.0

Felix Moessbauer

unread,
Oct 22, 2025, 11:39:58 AM (9 days ago) Oct 22
to isar-...@googlegroups.com, christop...@siemens.com, cedric.h...@siemens.com, jan.k...@siemens.com, Felix Moessbauer
A wic image consists of potentially many different components. All these
should be covered by a single SBOM.

After creating the wic image, we collect the individual sbom files
(rootfs, initrd, imaging) and semantically merge it with the debsbom
tool. The merge SBOM is then deployed as .wic.(spdx|cdx).json next to
the wic image.

Signed-off-by: Felix Moessbauer <felix.mo...@siemens.com>
---
meta/classes/imagetypes_wic.bbclass | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

diff --git a/meta/classes/imagetypes_wic.bbclass b/meta/classes/imagetypes_wic.bbclass
index c75d481d..fe31e4e6 100644
--- a/meta/classes/imagetypes_wic.bbclass
+++ b/meta/classes/imagetypes_wic.bbclass
@@ -201,4 +201,29 @@ EOIMAGER
${DEPLOY_DIR_IMAGE}/${INITRD_DEPLOY_FILE}.manifest \
${WORKDIR}/imager.manifest 2>/dev/null \
| sort | uniq > "${DEPLOY_DIR_IMAGE}/${IMAGE_FULLNAME}.wic.manifest"
+
+ for bomtype in ${SBOM_TYPES}; do
+ merge_wic_sbom $bomtype
+ done
+}
+
+merge_wic_sbom() {
+ BOMTYPE="$1"
+ TIMESTAMP=$(date --iso-8601=s -d @${SOURCE_DATE_EPOCH})
+ sbom_document_uuid="${@d.getVar('SBOM_DOCUMENT_UUID') or generate_document_uuid(d, False)}"
+
+ cat ${IMAGE_FULLNAME}.${bomtype}.json \
+ ${INITRD_DEPLOY_FILE}.${bomtype}.json \
+ ${WORKDIR}/imager.${bomtype}.json 2>/dev/null | \
+ bwrap \
+ --unshare-user \
+ --unshare-pid \
+ --bind ${SBOM_CHROOT} / \
+ -- debsbom -v merge -t $BOMTYPE \
+ --distro-name '${SBOM_DISTRO_NAME}-Image' --distro-supplier '${SBOM_DISTRO_SUPPLIER}' \
+ --distro-version '${SBOM_DISTRO_VERSION}' --base-distro-vendor '${SBOM_BASE_DISTRO_VENDOR}' \
+ --cdx-serialnumber $sbom_document_uuid \
+ --spdx-namespace '${SBOM_SPDX_NAMESPACE_PREFIX}'-$sbom_document_uuid \
+ --timestamp $TIMESTAMP - -o - \
+ > ${DEPLOY_DIR_IMAGE}/${IMAGE_FULLNAME}.wic.$bomtype.json
}
--
2.51.0

Bouska, Zdenek

unread,
Oct 24, 2025, 4:33:22 AM (7 days ago) Oct 24
to MOESSBAUER, Felix, isar-...@googlegroups.com, Steiger, Christoph, cedric.h...@siemens.com, Kiszka, Jan
Felix Moessbauer wrote on Sent: Wednesday, October 22, 2025 5:39 PM
> This patchset adds proper SBOM generation in the two standard formats
> SPDX and CycloneDX during the rootfs generation process.

I have two warnings when downloading based on generated sbom by
debsbom --progress download --outdir downloads --sources isar-image-base-debian-trixie-qemuamd64.wic.cdx.json

"WARNING:debsbom.download.resolver:no sha256 digest for linux-m...@6.17.2+r0. Lookup will be imprecise"
I guess mainline kernel and other packages built from source are not yet fully supported.
But SHA256 for sources with my patches could be computed in the future if I am not missing something. Right?
I understand, that I can't then look them up in Debian, but at least the SBOM
would represent the sources with the patches in the SHA256 and it would be possible to verify if I have correct sources.

"WARNING:debsbom.download.resolver:no sha256 digest for ope...@3.5.1-1+deb13u1. Lookup will be imprecise"
Not sure why SHA256 is missing in sbom for openssl. I use it without any change from trixie.

Thank you, I like it!
Zdenek Bouska

--
Siemens, s.r.o
Foundational Technologies

MOESSBAUER, Felix

unread,
Oct 24, 2025, 5:00:01 AM (7 days ago) Oct 24
to isar-...@googlegroups.com, Bouska, Zdenek, Steiger, Christoph, cedric.h...@siemens.com, Kiszka, Jan
On Fri, 2025-10-24 at 08:33 +0000, Bouška, Zdeněk (FT D EU CZ PDS1 ICC
1) wrote:
> Felix Moessbauer wrote on Sent: Wednesday, October 22, 2025 5:39 PM
> > This patchset adds proper SBOM generation in the two standard formats
> > SPDX and CycloneDX during the rootfs generation process.
>
> I have two warnings when downloading based on generated sbom by
> debsbom --progress download --outdir downloads --sources isar-image-base-debian-trixie-qemuamd64.wic.cdx.json
>
> "WARNING:debsbom.download.resolver:no sha256 digest for linux-m...@6.17.2+r0. Lookup will be imprecise"
> I guess mainline kernel and other packages built from source are not yet fully supported.

Hi, this means that the package was not found in the apt-cache. Did you
try this series with a fresh build-dir (sstate cache is ok)?

Unfortunately our rootfs_postprocess_commands are not idempotent and by
that a partial rebuild of only the sbom infrastructure runs the debsbom
tool in an rootfs where the apt cache was already dropped.

Anyways, the debsbom tool also supports the custom / rebuilt packages,
but these will simply not be found on the snapshot mirror. The error
above indicates, that precise information in the SBOM is missing -
indicating that the apt cache was missing at time of creating the sbom.

> But SHA256 for sources with my patches could be computed in the future if I am not missing something. Right?
> I understand, that I can't then look them up in Debian, but at least the SBOM
> would represent the sources with the patches in the SHA256 and it would be possible to verify if I have correct sources.
>
> "WARNING:debsbom.download.resolver:no sha256 digest for ope...@3.5.1-1+deb13u1. Lookup will be imprecise"
> Not sure why SHA256 is missing in sbom for openssl. I use it without any change from trixie.

This time, the warning comes from a package created from a built_using
relation which is not in the apt-cache. I observed the same in a
bookworm container (where I ran an apt-update before). Unfortunately,
the built-using relation does not encode any checksums, but we also
have no easy way to annotate that a package was created from a
built_using relation.

This topic is currently also discussed in [1]. Citing:

Source packages referenced in built_using only have name and version
information, but no checksum (and they often cannot be found in the 
apt cache)

Yeah, that feels bad in the abstract. A lot of .buildinfo and
built-using information (and the signature!) is only valid at the
point in time of building/ingestion.

[1] https://lists.debian.org/debian-snapshot/2025/10/msg00004.html

Best regards,
Felix

>
> Thank you, I like it!
> Zdenek Bouska
>
> --
> Siemens, s.r.o
> Foundational Technologies

Bouska, Zdenek

unread,
Oct 24, 2025, 5:37:13 AM (7 days ago) Oct 24
to MOESSBAUER, Felix, isar-...@googlegroups.com, Steiger, Christoph, cedric.h...@siemens.com, Kiszka, Jan
> This patchset adds proper SBOM generation in the two standard formats
> SPDX and CycloneDX during the rootfs generation process.

I compared wic.manifest and wic.cdx and here are the differences:

$ grep arch=source isar-image-base-debian-trixie-qemuamd64.wic.cdx.json \
| tr -d ' ' | sed 's/"[a-z]*":'// | sort -u | cut -d '/' -f 3 | cut -d '?' -f 1 | tr '@' '|' \
| sed 's/%2B/+/' | sort -u > list_from_cdx.txt
$ cat isar-image-base-debian-trixie-qemuamd64.wic.manifest | cut -d '|' -f 1,2 | sort -u > list_from_manifest.txt
$ diff list_from_manifest.txt list_from_cdx.txt
34d33
< grub2|2.12-9
71a71,76
> rust-buffered-reader|1.3.1-2
> rustc|1.85.0+dfsg3-1
> rust-nettle|7.3.0-1
> rust-nettle-sys|2.3.1-1
> rust-sequoia-openpgp|2.0.0-2
> rust-sequoia-policy-config|0.8.0-1
80a86
> unicode-data|15.1.0-1

At least grub should be in wic.cdx.

I am not sure about components which are not in manifest but are in wic.cdx.

MOESSBAUER, Felix

unread,
Oct 24, 2025, 6:03:04 AM (7 days ago) Oct 24
to isar-...@googlegroups.com, Bouska, Zdenek, Steiger, Christoph, cedric.h...@siemens.com, Kiszka, Jan
On Fri, 2025-10-24 at 09:37 +0000, Bouška, Zdeněk (FT D EU CZ PDS1 ICC
1) wrote:
> > This patchset adds proper SBOM generation in the two standard formats
> > SPDX and CycloneDX during the rootfs generation process.
>
> I compared wic.manifest and wic.cdx and here are the differences:
>
> $ grep arch=source isar-image-base-debian-trixie-qemuamd64.wic.cdx.json \
> | tr -d ' ' | sed 's/"[a-z]*":'// | sort -u | cut -d '/' -f 3 | cut -d '?' -f 1 | tr '@' '|' \
> | sed 's/%2B/+/' | sort -u > list_from_cdx.txt
> $ cat isar-image-base-debian-trixie-qemuamd64.wic.manifest | cut -d '|' -f 1,2 | sort -u > list_from_manifest.txt
> $ diff list_from_manifest.txt list_from_cdx.txt
> 34d33
> < grub2|2.12-9
> 71a71,76
> > rust-buffered-reader|1.3.1-2
> > rustc|1.85.0+dfsg3-1
> > rust-nettle|7.3.0-1
> > rust-nettle-sys|2.3.1-1
> > rust-sequoia-openpgp|2.0.0-2
> > rust-sequoia-policy-config|0.8.0-1
> 80a86
> > unicode-data|15.1.0-1
>
> At least grub should be in wic.cdx.

Yes, definitely. At least in my SPDX SBOM this is listed (need to
create a CDX one). Are you sure your bash magic is right? The purl is
likely:
pkg:deb/debian/gr...@2.12-9?arch=source

>
> I am not sure about components which are not in manifest but are in wic.cdx.

These are components created from the built-using relation. They are
not tracked in the manifest.

Felix

>
> Zdenek Bouska
>
> --
> Siemens, s.r.o
> Foundational Technologies

Bouska, Zdenek

unread,
Oct 27, 2025, 3:54:25 AM (4 days ago) Oct 27
to MOESSBAUER, Felix, isar-...@googlegroups.com, Steiger, Christoph, cedric.h...@siemens.com, Kiszka, Jan
> Yes, definitely. At least in my SPDX SBOM this is listed (need to
> create a CDX one). Are you sure your bash magic is right?

Grub is not in SBOMs even if I look for it by text search.

Reproduce:

$ cat trixie_amd64.yml
header:
version: 14

build_system: isar

machine: qemuamd64
distro: debian-trixie

target: mc:qemuamd64-trixie:isar-image-base

repos:
isar:
path: isar
layers:
meta:
meta-isar:

sudo rm -rf tmp/trixie_amd64_wic
mkdir -p tmp/trixie_amd64_wic
cd tmp/trixie_amd64_wic
git clone https://github.com/ilbers/isar.git
cd isar
git checkout next
git am ../../../isar-v3-Add-SBOM-generation-with-debsbom.patch
cd ..
isar/kas/kas-container --runtime-args --net=host build ../../trixie_amd64.yml
grep grub build/tmp/deploy/images/qemuamd64/isar-image-base-debian-trixie-qemuamd64.wic.cdx.json
grep grub build/tmp/deploy/images/qemuamd64/isar-image-base-debian-trixie-qemuamd64.wic.spdx.json
grep grub build/tmp/deploy/images/qemuamd64/isar-image-base-debian-trixie-qemuamd64.wic.manifest


I still got these SHA256 warnings, same as with custom kernel:

$ debsbom download --outdir downloads --sources tmp/trixie_amd64_wic/build/tmp/deploy/images/qemuamd64/isar-image-base-debian-trixie-qemuamd64.wic.cdx.json
WARNING:debsbom.download.resolver:no sha256 digest for li...@6.12.48-1. Lookup will be imprecise
WARNING:debsbom.download.resolver:no sha256 digest for linux-sig...@6.12.48+1. Lookup will be imprecise
WARNING:debsbom.download.resolver:no sha256 digest for ope...@3.5.1-1+deb13u1. Lookup will be imprecise
downloading 232 files, 545 MiB (cached: 0, 0 KiB)

> Hi, this means that the package was not found in the apt-cache. Did you
> try this series with a fresh build-dir (sstate cache is ok)?
>
Warnings with custom patched kernel are these. Now I made sure, that I deleted build dir:
WARNING:debsbom.download.resolver:no sha256 digest for linux-m...@6.17.2+r0. Lookup will be imprecise
WARNING:debsbom.commands.download:not found upstream: linux-m...@6.17.2+r0 <- this is expected
WARNING:debsbom.download.resolver:no sha256 digest for ope...@3.5.1-1+deb13u1. Lookup will be imprecise

MOESSBAUER, Felix

unread,
Oct 27, 2025, 5:24:45 AM (4 days ago) Oct 27
to isar-...@googlegroups.com, Bouska, Zdenek, Steiger, Christoph, cedric.h...@siemens.com, Kiszka, Jan
On Mon, 2025-10-27 at 07:54 +0000, Bouška, Zdeněk (FT D EU CZ PDS1 ICC
1) wrote:
> > Yes, definitely. At least in my SPDX SBOM this is listed (need to
> > create a CDX one). Are you sure your bash magic is right?
>
> Grub is not in SBOMs even if I look for it by text search.
>
> Reproduce:
>
> $ cat trixie_amd64.yml
> header:
> version: 14
>
> build_system: isar
>
> machine: qemuamd64
> distro: debian-trixie
>
> target: mc:qemuamd64-trixie:isar-image-base
>
> repos:
> isar:
> path: isar
> layers:
> meta:
> meta-isar:
>
>

Thanks for insisting on this. I found the bug. It was a simple glitch
in the naming of the imager SBOM which apparently slipped in while
refactoring. The following patch fixes it (will be fixed in the v4 as
well):

diff --git a/meta/classes/image-tools-extension.bbclass
b/meta/classes/image-tools-extension.bbclass
index bfdb8a35..95f003d0 100644
--- a/meta/classes/image-tools-extension.bbclass
+++ b/meta/classes/image-tools-extension.bbclass
@@ -93,7 +93,7 @@ generate_imager_sbom() {
--bind $schroot_dir /mnt/rootfs \
--bind ${WORKDIR} /mnt/deploy-dir \
-- debsbom -vv generate ${SBOM_DEBSBOM_TYPE_ARGS} \
- --from-pkglist -r /mnt/rootfs -o /mnt/deploy-dir/'${PN}-
${DISTRO}-${MACHINE}-imager' \
+ --from-pkglist -r /mnt/rootfs -o /mnt/deploy-dir/imager \
--distro-name '${SBOM_DISTRO_NAME}-Imager' --distro-
supplier '${SBOM_DISTRO_SUPPLIER}' \
--distro-version '${SBOM_DISTRO_VERSION}' --distro-arch
'${DISTRO_ARCH}' \
--base-distro-vendor '${SBOM_BASE_DISTRO_VENDOR}' \


>
>
> I still got these SHA256 warnings, same as with custom kernel:
>
> $ debsbom download --outdir downloads --sources tmp/trixie_amd64_wic/build/tmp/deploy/images/qemuamd64/isar-image-base-debian-trixie-qemuamd64.wic.cdx.json
> WARNING:debsbom.download.resolver:no sha256 digest for li...@6.12.48-1. Lookup will be imprecise
> WARNING:debsbom.download.resolver:no sha256 digest for linux-sig...@6.12.48+1. Lookup will be imprecise
> WARNING:debsbom.download.resolver:no sha256 digest for ope...@3.5.1-1+deb13u1. Lookup will be imprecise
> downloading 232 files, 545 MiB (cached: 0, 0 KiB)
>
> > Hi, this means that the package was not found in the apt-cache. Did you
> > try this series with a fresh build-dir (sstate cache is ok)?
> >
> Warnings with custom patched kernel are these. Now I made sure, that I deleted build dir:
> WARNING:debsbom.download.resolver:no sha256 digest for linux-m...@6.17.2+r0. Lookup will be imprecise
> WARNING:debsbom.commands.download:not found upstream: linux-m...@6.17.2+r0 <- this is expected
> WARNING:debsbom.download.resolver:no sha256 digest for ope...@3.5.1-1+deb13u1. Lookup will be imprecise

All these warnings are from built-using (except maybe for
linux-m...@6.17.2+r0). We discussed the topic upstream with the
reproducible builds people [1] and finally came to the conclusion, that
a name+version tuple is sufficient to identify a package (at least for
the content of the package, the signature still needs clarification).
By that, we will downgrade that warning to a info message (and add some
explantaion to the documentation) [2].

[1] https://github.com/siemens/debsbom/pull/112
[2] https://lists.debian.org/debian-devel/2025/10/msg00236.html

Best regards,
Felix

>
> Zdenek Bouska
>
> --
> Siemens, s.r.o
> Foundational Technologies

Reply all
Reply to author
Forward
0 new messages