On Wed, Jan 7, 2026 at 11:10 AM Stefano Tondo via
lists.openembedded.org <[email protected]>
wrote:
>
> From: Stefano Tondo <[email protected]>
>
> This commit adds support for including image/product metadata and
> supplier information in SPDX 3.0 SBOMs to meet compliance requirements.
>
> New configuration variables (in spdx-common.bbclass):
>
>   SBOM_COMPONENT_NAME (optional):
>     - Name of the product/image being documented
>     - Creates a software_Package element with metadata
>     - Typically set to IMAGE_BASENAME or product name
>
>   SBOM_COMPONENT_VERSION (optional):
>     - Version of the product/image
>     - Falls back to DISTRO_VERSION if not set
>
>   SBOM_COMPONENT_SUMMARY (optional):
>     - Description of the product/image
>     - Falls back to IMAGE_SUMMARY if not set
>
>   SBOM_SUPPLIER_NAME (optional):
>     - Name of the organization supplying the SBOM
>     - Creates an Organization element
>
>   SBOM_SUPPLIER_URL (optional):
>     - URL of the supplier organization
>     - Added as externalIdentifier
>
> Implementation (in sbom30.py):
>
>   - create_sbom(): Add metadata component and supplier after SBOM
>     creation but before collection expansion
>   - Create relationships:
>     * SBOM --describes--> metadata component
>     * SBOM --availableFrom--> supplier Organization
>
> SPDX 3.0 elements created:
>
>   - software_Package (primaryPurpose: operatingSystem) for product
>   - Organization with optional URL externalIdentifier
>   - Appropriate relationships per SPDX 3.0 spec
>
> Usage example in local.conf:
>
>   SBOM_COMPONENT_NAME = ""
>   SBOM_COMPONENT_VERSION = "1.0.0"
>   SBOM_COMPONENT_SUMMARY = "Production image for Device X"
>   SBOM_SUPPLIER_NAME = "Acme Corporation"
>   SBOM_SUPPLIER_URL = "https://acme.com";
>
> This enables profile-specific SBOM workflows and compliance validation
> tools that require product and supplier metadata.
>
> Signed-off-by: Stefano Tondo <[email protected]>
> ---
>  meta/lib/oe/sbom30.py       | 52 +++++++++++++++++++++++++++++++++++++
>  meta/lib/oe/spdx30_tasks.py | 10 +++----
>  2 files changed, 57 insertions(+), 5 deletions(-)
>
> diff --git a/meta/lib/oe/sbom30.py b/meta/lib/oe/sbom30.py
> index 227ac51877..197361db4f 100644
> --- a/meta/lib/oe/sbom30.py
> +++ b/meta/lib/oe/sbom30.py
> @@ -1045,6 +1045,58 @@ def create_sbom(d, name, root_elements, 
> add_objectsets=[]):
>          )
>      )

create_sbom() is a generic function that is used for more than just
images. As such, using the SBOM_COMPONENT_VERSION, DISTRO_VERSION,
IMAGE_SUMMARY, etc. is not appropriate.

You should be able to do the same thing by using the `objset` and
`sbom` object returned from the function instead.

>
> +    # Add SBOM metadata component (image/product information)
> +    sbom_component_name = d.getVar("SBOM_COMPONENT_NAME")
> +    if sbom_component_name:
> +        sbom_component_version = d.getVar("SBOM_COMPONENT_VERSION") or 
> d.getVar("DISTRO_VERSION") or "unknown"
> +        sbom_component_summary = d.getVar("SBOM_COMPONENT_SUMMARY") or 
> d.getVar("IMAGE_SUMMARY") or f"{name} image"
> +
> +        metadata_component = objset.add(
> +            oe.spdx30.software_Package(
> +                _id=objset.new_spdxid("metadata", "component"),
> +                creationInfo=objset.doc.creationInfo,
> +                name=sbom_component_name,
> +                software_packageVersion=sbom_component_version,
> +                summary=sbom_component_summary,
> +                
> software_primaryPurpose=oe.spdx30.software_SoftwarePurpose.operatingSystem,
> +            )
> +        )

It's not clear why this is needed over the root elements? You can
effectively do the same thing by setting the "suppliedBy" property on
all the root elements of the SBoM. If some of them are missing
supplier information, we can add properties to set those the same as
the SPDX_PACKAGE_SUPPLIER. (e.g. SPDX_IMAGE_SUPPLIER,
SPDX_SDK_SUPPLIER, etc.)

This seems like it's adding things in the wrong place as-is.

> +
> +        # Link SBOM to metadata component
> +        objset.new_relationship(
> +            [sbom],
> +            oe.spdx30.RelationshipType.describes,
> +            [metadata_component],
> +        )
> +
> +    # Add supplier information if provided
> +    sbom_supplier_name = d.getVar("SBOM_SUPPLIER_NAME")
> +    if sbom_supplier_name:
> +        sbom_supplier_url = d.getVar("SBOM_SUPPLIER_URL")
> +
> +        supplier = objset.add(
> +            oe.spdx30.Organization(
> +                _id=objset.new_spdxid("supplier", 
> sbom_supplier_name.replace(" ", "-").lower()),
> +                creationInfo=objset.doc.creationInfo,
> +                name=sbom_supplier_name,
> +            )
> +        )
> +
> +        if sbom_supplier_url:
> +            supplier.externalIdentifier = [
> +                oe.spdx30.ExternalIdentifier(
> +                    
> externalIdentifierType=oe.spdx30.ExternalIdentifierType.urlScheme,
> +                    identifier=sbom_supplier_url,
> +                )
> +            ]

Use objset.new_agent() so that this follows all the other rules for
agents; this will allow user to de-duplicate and add all the other
metadata using variables

> +
> +        # Link supplier to SBOM (SBOM is available from supplier)
> +        objset.new_relationship(
> +            [sbom],
> +            oe.spdx30.RelationshipType.availableFrom,
> +            [supplier],

FYI, there is a _much_ better way to do this planned in SPDX 3.1
(which is why I've not added it yet):
https://spdx.github.io/spdx-spec/v3.1-dev/model/Core/Classes/SupportRelationship/

In the meantime, isn't the "suppliedBy" property sufficient?

> +        )
> +
>      missing_spdxids = objset.expand_collection(add_objectsets=add_objectsets)
>      if missing_spdxids:
>          bb.warn(
> diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
> index c86b088b61..757503cd6b 100644
> --- a/meta/lib/oe/spdx30_tasks.py
> +++ b/meta/lib/oe/spdx30_tasks.py
> @@ -179,17 +179,17 @@ def add_package_files(
>                  continue
>
>              filename = str(filepath.relative_to(topdir))
> -
> +
>              # Apply file filtering if enabled
>              if spdx_file_filter == "essential":
>                  file_upper = file.upper()
>                  filename_lower = filename.lower()
> -
> +
>                  # Skip if matches exclude patterns
>                  skip_file = any(pattern in filename_lower for pattern in 
> exclude_patterns)
>                  if skip_file:
>                      continue
> -
> +
>                  # Keep only essential files (license/readme/etc)
>                  is_essential = any(pattern in file_upper for pattern in 
> essential_patterns)
>                  if not is_essential:
> @@ -198,7 +198,7 @@ def add_package_files(
>                  # Skip all files
>                  continue
>              # else: spdx_file_filter == "all" or any other value - include 
> all files
> -
> +
>              file_purposes = get_purposes(filepath)
>
>              # Check if file is compiled
> @@ -245,7 +245,7 @@ def get_package_sources_from_debug(
>      d, package, package_files, sources, source_hash_cache
>  ):
>      spdx_file_filter = (d.getVar("SPDX_FILE_FILTER") or "all").lower()
> -
> +
>      def file_path_match(file_path, pkg_file):
>          if file_path.lstrip("/") == pkg_file.name.lstrip("/"):
>              return True
> --
> 2.52.0
>
>
> 
>
-=-=-=-=-=-=-=-=-=-=-=-
Links: You receive all messages sent to this group.
View/Reply Online (#229030): 
https://lists.openembedded.org/g/openembedded-core/message/229030
Mute This Topic: https://lists.openembedded.org/mt/117138943/21656
Group Owner: [email protected]
Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub 
[[email protected]]
-=-=-=-=-=-=-=-=-=-=-=-

Reply via email to