diff mbox series

[2/4] spdx30_tasks: Add PURL generation for package identification

Message ID 20260107180951.140895-2-stondo@gmail.com
State Changes Requested
Headers show
Series None | expand

Commit Message

Stefano Tondo Jan. 7, 2026, 6:09 p.m. UTC
From: Stefano Tondo <stefano.tondo.ext@siemens.com>

Add automatic Package URL (PURL) generation according to the Yocto PURL
specification to enable package identification in vulnerability databases
and improve NTIA SBOM compliance.

Field added:
- software_packageUrl: Auto-generates Package URLs per Yocto PURL spec
  Format: pkg:yocto/<LAYERNAME>/<BPN>@<PV>
  See: https://github.com/package-url/purl-spec/pull/372

PURL Implementation:
- Type: yocto (official PURL type for Yocto recipes, per PR #372)
- Namespace: Layer name from FILE_LAYERNAME variable
- Name: BPN (base package name with prefixes/suffixes removed)
- Version: PV (package version from recipe)
- Normalization: Lowercase per PURL spec

New BitBake variable:
- SPDX_PACKAGE_URL: Override auto-generated PURL

The Yocto PURL type specification (purl-spec PR #372) has been approved
by the PURL maintainers and is ready for implementation. This follows
the agreed format from JPEWdev (Joshua Watt) and petermarko.

Signed-off-by: Stefano Tondo <stefano.tondo.ext@siemens.com>
---
 meta/lib/oe/spdx30_tasks.py | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

Comments

Joshua Watt Jan. 7, 2026, 8:14 p.m. UTC | #1
I do plan on implementing this once the PURL spec PR is merged. I
already have a proof of concept:
https://git.yoctoproject.org/poky-contrib/commit/?h=jpew/purls&id=538c31488e7300a4ad3320e861324a4858a6f148

The "extra" repository URLs that it generates are done terribly ATM
and it needs a few other things to be cleaned up. However, it will
also correctly allow end users to add multiple purls (so they can have
their own PURLs in addition to the Yocto ones).

On Wed, Jan 7, 2026 at 11:10 AM Stefano Tondo via
lists.openembedded.org <stondo=gmail.com@lists.openembedded.org>
wrote:
>
> From: Stefano Tondo <stefano.tondo.ext@siemens.com>
>
> Add automatic Package URL (PURL) generation according to the Yocto PURL
> specification to enable package identification in vulnerability databases
> and improve NTIA SBOM compliance.
>
> Field added:
> - software_packageUrl: Auto-generates Package URLs per Yocto PURL spec
>   Format: pkg:yocto/<LAYERNAME>/<BPN>@<PV>
>   See: https://github.com/package-url/purl-spec/pull/372
>
> PURL Implementation:
> - Type: yocto (official PURL type for Yocto recipes, per PR #372)
> - Namespace: Layer name from FILE_LAYERNAME variable
> - Name: BPN (base package name with prefixes/suffixes removed)
> - Version: PV (package version from recipe)
> - Normalization: Lowercase per PURL spec
>
> New BitBake variable:
> - SPDX_PACKAGE_URL: Override auto-generated PURL
>
> The Yocto PURL type specification (purl-spec PR #372) has been approved
> by the PURL maintainers and is ready for implementation. This follows
> the agreed format from JPEWdev (Joshua Watt) and petermarko.
>
> Signed-off-by: Stefano Tondo <stefano.tondo.ext@siemens.com>
> ---
>  meta/lib/oe/spdx30_tasks.py | 34 ++++++++++++++++++++++++++++++++++
>  1 file changed, 34 insertions(+)
>
> diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
> index f731a709e3..86430c7008 100644
> --- a/meta/lib/oe/spdx30_tasks.py
> +++ b/meta/lib/oe/spdx30_tasks.py
> @@ -474,6 +474,36 @@ def create_spdx(d):
>          if val:
>              setattr(obj, name, val)
>
> +    def generate_purl(d, package=None):
> +        """
> +        Generate Package URL (purl) for a package according to Yocto PURL spec.
> +        Format: pkg:yocto/<LAYERNAME>/<BPN>@<PV>
> +
> +        See: https://github.com/package-url/purl-spec/pull/372
> +        """
> +        bpn = d.getVar("BPN")
> +        pv = d.getVar("PV")
> +
> +        # Get layer name using FILE_LAYERNAME
> +        # This is the correct variable that contains the layer name from BBFILE_COLLECTIONS
> +        # (BBFILE_COLLECTIONS itself is not available outside of layer.conf)
> +        layer = d.getVar("FILE_LAYERNAME")
> +
> +        if not layer:
> +            layer = "core"  # Default to core if layer detection fails
> +
> +        # For sub-packages, use BPN (base package name)
> +        # Per spec: BPN has prefixes/suffixes removed
> +        name = bpn
> +
> +        # Normalize name per PURL spec (lowercase only)
> +        # Note: Underscores are not allowed in recipe names
> +        name = name.lower()
> +
> +        purl = f"pkg:yocto/{layer}/{name}@{pv}"
> +
> +        return purl
> +
>      license_data = oe.spdx_common.load_spdx_license_data(d)
>
>      deploydir = Path(d.getVar("SPDXDEPLOY"))
> @@ -646,6 +676,10 @@ def create_spdx(d):
>                      "software_packageUrl",
>                      package=package
>                  )
> +            else:
> +                # Auto-generate PURL if not manually specified
> +                auto_purl = generate_purl(d, package)
> +                spdx_package.software_packageUrl = auto_purl
>
>              pkg_objset.new_scoped_relationship(
>                  [oe.sbom30.get_element_link_id(build)],
> --
> 2.52.0
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#229021): https://lists.openembedded.org/g/openembedded-core/message/229021
> Mute This Topic: https://lists.openembedded.org/mt/117138938/3616693
> Group Owner: openembedded-core+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-core/unsub [JPEWhacker@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
Tondo, Stefano Jan. 8, 2026, 11:14 a.m. UTC | #2
Hi Joshua,
Thank you for the feedback and for sharing your proof of concept.
I have reviewed your jpew/purls branch and agree that your implementation is the better approach. It is more comprehensive and the support for multiple PURLs (both Yocto and custom) is a significant improvement over my proposal.
Since you plan to move forward with that implementation once PURL spec PR #372 is merged, I am withdrawing patches 2, 3, and 4 from this series to avoid duplication.
Regarding the rest of my series:

  *
I will keep patch 1/4 (documentation clarification) as it is independent.
  *
I have already submitted a v2 for the supplier support patch (referencing your earlier feedback).

Please feel free to cherry-pick or adapt any logic from my withdrawn patches if useful. I am happy to help test your implementation once it lands.
Best regards,
Stefano

________________________________
From: Joshua Watt <jpewhacker@gmail.com>
Sent: Wednesday, January 7, 2026 21:14
To: stondo@gmail.com <stondo@gmail.com>
Cc: openembedded-core@lists.openembedded.org <openembedded-core@lists.openembedded.org>; Tondo, Stefano (ext) (SI B PRO AUT PD ZUG SW 2) <stefano.tondo.ext@siemens.com>; Marko, Peter (FT D EU SK BFS1) <Peter.Marko@siemens.com>; Freihofer, Adrian (SI B PRO TI EAC CCP) <adrian.freihofer@siemens.com>
Subject: Re: [OE-core] [PATCH 2/4] spdx30_tasks: Add PURL generation for package identification

I do plan on implementing this once the PURL spec PR is merged. I
already have a proof of concept:
https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.yoctoproject.org%2Fpoky-contrib%2Fcommit%2F%3Fh%3Djpew%2Fpurls%26id%3D538c31488e7300a4ad3320e861324a4858a6f148&data=05%7C02%7Cstefano.tondo.ext%40siemens.com%7Cf7d0c35415234e07f7f808de4e295947%7C38ae3bcd95794fd4addab42e1495d55a%7C1%7C0%7C639034136656442825%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=sZo30nhs%2FY%2BTdbci4I9GQYAr0sCBwrrJqhXD%2FrAUdsE%3D&reserved=0<https://git.yoctoproject.org/poky-contrib/commit/?h=jpew/purls&id=538c31488e7300a4ad3320e861324a4858a6f148>

The "extra" repository URLs that it generates are done terribly ATM
and it needs a few other things to be cleaned up. However, it will
also correctly allow end users to add multiple purls (so they can have
their own PURLs in addition to the Yocto ones).

On Wed, Jan 7, 2026 at 11:10 AM Stefano Tondo via
lists.openembedded.org <stondo=gmail.com@lists.openembedded.org>
wrote:
>
> From: Stefano Tondo <stefano.tondo.ext@siemens.com>
>
> Add automatic Package URL (PURL) generation according to the Yocto PURL
> specification to enable package identification in vulnerability databases
> and improve NTIA SBOM compliance.
>
> Field added:
> - software_packageUrl: Auto-generates Package URLs per Yocto PURL spec
>   Format: pkg:yocto/<LAYERNAME>/<BPN>@<PV>
>   See: https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fpackage-url%2Fpurl-spec%2Fpull%2F372&data=05%7C02%7Cstefano.tondo.ext%40siemens.com%7Cf7d0c35415234e07f7f808de4e295947%7C38ae3bcd95794fd4addab42e1495d55a%7C1%7C0%7C639034136656477188%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=wpryQd4H8mx1f%2B6APIoqrXvboWghjhxDjFDNrXliqxI%3D&reserved=0<https://github.com/package-url/purl-spec/pull/372>
>
> PURL Implementation:
> - Type: yocto (official PURL type for Yocto recipes, per PR #372)
> - Namespace: Layer name from FILE_LAYERNAME variable
> - Name: BPN (base package name with prefixes/suffixes removed)
> - Version: PV (package version from recipe)
> - Normalization: Lowercase per PURL spec
>
> New BitBake variable:
> - SPDX_PACKAGE_URL: Override auto-generated PURL
>
> The Yocto PURL type specification (purl-spec PR #372) has been approved
> by the PURL maintainers and is ready for implementation. This follows
> the agreed format from JPEWdev (Joshua Watt) and petermarko.
>
> Signed-off-by: Stefano Tondo <stefano.tondo.ext@siemens.com>
> ---
>  meta/lib/oe/spdx30_tasks.py | 34 ++++++++++++++++++++++++++++++++++
>  1 file changed, 34 insertions(+)
>
> diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
> index f731a709e3..86430c7008 100644
> --- a/meta/lib/oe/spdx30_tasks.py
> +++ b/meta/lib/oe/spdx30_tasks.py
> @@ -474,6 +474,36 @@ def create_spdx(d):
>          if val:
>              setattr(obj, name, val)
>
> +    def generate_purl(d, package=None):
> +        """
> +        Generate Package URL (purl) for a package according to Yocto PURL spec.
> +        Format: pkg:yocto/<LAYERNAME>/<BPN>@<PV>
> +
> +        See: https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fpackage-url%2Fpurl-spec%2Fpull%2F372&data=05%7C02%7Cstefano.tondo.ext%40siemens.com%7Cf7d0c35415234e07f7f808de4e295947%7C38ae3bcd95794fd4addab42e1495d55a%7C1%7C0%7C639034136656499697%7CUnknown%7CTWFpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=NqHnK0zl6ASj8lKc%2BpV6LaDoozpgpugPMmDWslCA62w%3D&reserved=0<https://github.com/package-url/purl-spec/pull/372>
> +        """
> +        bpn = d.getVar("BPN")
> +        pv = d.getVar("PV")
> +
> +        # Get layer name using FILE_LAYERNAME
> +        # This is the correct variable that contains the layer name from BBFILE_COLLECTIONS
> +        # (BBFILE_COLLECTIONS itself is not available outside of layer.conf)
> +        layer = d.getVar("FILE_LAYERNAME")
> +
> +        if not layer:
> +            layer = "core"  # Default to core if layer detection fails
> +
> +        # For sub-packages, use BPN (base package name)
> +        # Per spec: BPN has prefixes/suffixes removed
> +        name = bpn
> +
> +        # Normalize name per PURL spec (lowercase only)
> +        # Note: Underscores are not allowed in recipe names
> +        name = name.lower()
> +
> +        purl = f"pkg:yocto/{layer}/{name}@{pv}"
> +
> +        return purl
> +
>      license_data = oe.spdx_common.load_spdx_license_data(d)
>
>      deploydir = Path(d.getVar("SPDXDEPLOY"))
> @@ -646,6 +676,10 @@ def create_spdx(d):
>                      "software_packageUrl",
>                      package=package
>                  )
> +            else:
> +                # Auto-generate PURL if not manually specified
> +                auto_purl = generate_purl(d, package)
> +                spdx_package.software_packageUrl = auto_purl
>
>              pkg_objset.new_scoped_relationship(
>                  [oe.sbom30.get_element_link_id(build)],
> --
> 2.52.0
>
>
> 
>
diff mbox series

Patch

diff --git a/meta/lib/oe/spdx30_tasks.py b/meta/lib/oe/spdx30_tasks.py
index f731a709e3..86430c7008 100644
--- a/meta/lib/oe/spdx30_tasks.py
+++ b/meta/lib/oe/spdx30_tasks.py
@@ -474,6 +474,36 @@  def create_spdx(d):
         if val:
             setattr(obj, name, val)
 
+    def generate_purl(d, package=None):
+        """
+        Generate Package URL (purl) for a package according to Yocto PURL spec.
+        Format: pkg:yocto/<LAYERNAME>/<BPN>@<PV>
+
+        See: https://github.com/package-url/purl-spec/pull/372
+        """
+        bpn = d.getVar("BPN")
+        pv = d.getVar("PV")
+
+        # Get layer name using FILE_LAYERNAME
+        # This is the correct variable that contains the layer name from BBFILE_COLLECTIONS
+        # (BBFILE_COLLECTIONS itself is not available outside of layer.conf)
+        layer = d.getVar("FILE_LAYERNAME")
+
+        if not layer:
+            layer = "core"  # Default to core if layer detection fails
+
+        # For sub-packages, use BPN (base package name)
+        # Per spec: BPN has prefixes/suffixes removed
+        name = bpn
+
+        # Normalize name per PURL spec (lowercase only)
+        # Note: Underscores are not allowed in recipe names
+        name = name.lower()
+
+        purl = f"pkg:yocto/{layer}/{name}@{pv}"
+
+        return purl
+
     license_data = oe.spdx_common.load_spdx_license_data(d)
 
     deploydir = Path(d.getVar("SPDXDEPLOY"))
@@ -646,6 +676,10 @@  def create_spdx(d):
                     "software_packageUrl",
                     package=package
                 )
+            else:
+                # Auto-generate PURL if not manually specified
+                auto_purl = generate_purl(d, package)
+                spdx_package.software_packageUrl = auto_purl
 
             pkg_objset.new_scoped_relationship(
                 [oe.sbom30.get_element_link_id(build)],