Skip to content

ENH: Generate SPDX 2.3 Software Bill of Materials at configure time#5817

Open
Copilot wants to merge 8 commits intomainfrom
copilot/generate-sbom-at-build-time
Open

ENH: Generate SPDX 2.3 Software Bill of Materials at configure time#5817
Copilot wants to merge 8 commits intomainfrom
copilot/generate-sbom-at-build-time

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 20, 2026

Add SPDX 2.3 SBOM generation at CMake configure time, producing sbom.spdx.json in the build directory with all enabled ThirdParty modules and their metadata. Per-module SPDX metadata is co-located in each module's itk-module.cmake file. Fixes #4302.

Commit breakdown (7 independent commits)
  1. Core SBOM generationITKSBOMGeneration.cmake with itk_generate_sbom(), itk_sbom_register_package() for remote modules, FFTW GPL tracking, install rule
  2. Distribute metadata to modules — Extend itk_module() with SPDX_LICENSE, SPDX_DOWNLOAD_LOCATION, SPDX_COPYRIGHT, SPDX_CUSTOM_LICENSE_TEXT/NAME; populate all 23 ThirdParty modules
  3. Fix JSON escaping bug — Custom license names/texts in hasExtractedLicensingInfo were not escaped
  4. Add SPDX_VERSION — New itk_module() parameter; versions for 11 modules (Eigen3 3.4.90, Expat 2.7.4, GoogleTest 1.17.0, HDF5 1.14.5, JPEG/libjpeg-turbo 3.0.4, MINC 2.4.06, NIFTI 3.0.0, OpenJPEG 2.5.4, PNG 1.6.54, TIFF 4.7.0, ZLIB 2.2.5); fix Eigen3 license to MPL-2.0 OR Apache-2.0
  5. SBOM JSON validation testITKSBOMValidation CTest checks JSON syntax, SPDX 2.3 fields, package uniqueness
  6. Version consistency testVerifySPDXVersions.py cross-checks SPDX_VERSION against UpdateFromUpstream.sh tags (8 of 16 modules with parseable version tags)
  7. Warn on missing SPDX metadataAUTHOR_WARNING for enabled ThirdParty modules lacking SPDX_LICENSE
Remote module extensibility
itk_sbom_register_package(
  NAME "MyRemoteModule"
  VERSION "1.0.0"
  SPDX_LICENSE "Apache-2.0"
  DOWNLOAD_LOCATION "https://github.com/example/MyRemoteModule"
  SUPPLIER "Organization: Example"
  COPYRIGHT "Copyright Example Inc."
)
Known limitations
  • 12 ThirdParty modules without SPDX_VERSION (track master, commit SHAs, or custom tags in UpdateFromUpstream.sh)
  • DCMTK and GDCM license expressions may need refinement (both listed as BSD-3-Clause but contain sub-components with different licenses)
  • Phase 2 of Generate a Software Bill of Materials at Build Time #4302 (SPDX source file headers) is a separate effort
  • NOTICE file is not yet deprecated/replaced

Copilot AI changed the title [WIP] Generate Software Bill of Materials at build time ENH: Generate SPDX 2.3 Software Bill of Materials at configure time Feb 20, 2026
Copilot AI requested a review from dzenanz February 20, 2026 22:20
@blowekamp
Copy link
Copy Markdown
Member

The VTK modular system uses arguments to the main module macro for the license information: https://docs.vtk.org/en/latest/api/cmake/vtkModule.html#vtk-module-file-contents

I think it is worth a human conversation to decide if this information should have a public interface with the main module macro or if it should be separate. The man macro certainly could call a private function such as these.

I was just looking into moving FFTW into a third party library structure. With the varied backed-ends supported it is a dynamic case to support, and very relevant to license requirements.

Having all the module information defined, along with the license information maybe beneficial to keep some information consistent, and apply certain logic or enforcement such as defaulting internal module to ITK license while requiring remote modules to have these attributes.

@dzenanz
Copy link
Copy Markdown
Member

dzenanz commented Feb 23, 2026

Copilot's first PR seems better to me (easier to maintain), with license information distributed in Modules/ThirdParty/ModuleName/itk-module.cmake‎ files instead of being concentrated in CMake/ITKSBOMGeneration.cmake.

@dzenanz
Copy link
Copy Markdown
Member

dzenanz commented Feb 23, 2026

@copilot Make license and other per-module information distributed in Modules/ThirdParty/ModuleName/itk-module.cmake‎ files instead of being concentrated in CMake/ITKSBOMGeneration.cmake. This will put third party license information into a more intuitive location for easier maintenance.

@dzenanz
Copy link
Copy Markdown
Member

dzenanz commented Feb 23, 2026

More importantly, I am unfamiliar with SPDX and therefore will not be able to tell whether Copilot does a great or terrible job (or somewhere in between).

@dzenanz dzenanz force-pushed the copilot/generate-sbom-at-build-time branch from b6e40e9 to bc0d0a2 Compare February 24, 2026 13:49
@github-actions github-actions bot added type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots type:Enhancement Improvement of existing methods or implementation labels Feb 24, 2026
@dzenanz
Copy link
Copy Markdown
Member

dzenanz commented Feb 24, 2026

@copilot Make license and other per-module information distributed in Modules/ThirdParty/ModuleName/itk-module.cmake‎ files instead of being concentrated in CMake/ITKSBOMGeneration.cmake. This will put third party license information into a more intuitive location for easier maintenance.

@dzenanz
Copy link
Copy Markdown
Member

dzenanz commented Feb 26, 2026

@copilot Make license and other per-module information distributed in Modules/ThirdParty/ModuleName/itk-module.cmake‎ files instead of being concentrated in CMake/ITKSBOMGeneration.cmake. This will put third party license information into a more intuitive location for easier maintenance.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Feb 26, 2026

@copilot Make license and other per-module information distributed in Modules/ThirdParty/ModuleName/itk-module.cmake‎ files instead of being concentrated in `CMake/ITKSBOMGeneratio...

Done in 8692d2d and 50daeeb. SPDX metadata is now declared directly in each Modules/ThirdParty/*/itk-module.cmake file via new itk_module() parameters: SPDX_LICENSE, SPDX_DOWNLOAD_LOCATION, SPDX_COPYRIGHT, SPDX_CUSTOM_LICENSE_TEXT, and SPDX_CUSTOM_LICENSE_NAME. The centralized _itk_sbom_get_thirdparty_metadata() lookup table has been removed from ITKSBOMGeneration.cmake — it now reads metadata from module declarations. Remote modules can also use the same itk_module() parameters or the itk_sbom_register_package() function.

@github-actions github-actions bot added the area:ThirdParty Issues affecting the ThirdParty module label Feb 26, 2026
@hjmjohnson hjmjohnson force-pushed the copilot/generate-sbom-at-build-time branch from 50daeeb to 7efe75e Compare April 15, 2026 00:59
@github-actions github-actions bot added the area:Python wrapping Python bindings for a class label Apr 15, 2026
Implement build-time generation of a Software Bill of Materials
(SBOM) in SPDX 2.3 JSON format. The SBOM documents ITK and all
enabled third-party dependencies with their names, licenses, and
dependency relationships.

- Add CMake/ITKSBOMGeneration.cmake with SBOM generation logic
- Add ITK_GENERATE_SBOM option (default ON) to CMakeLists.txt
- Generate sbom.spdx.json in the build directory at configure time
- Install sbom.spdx.json alongside LICENSE, NOTICE, and README.md
- Support FFTW as optional GPL dependency in SBOM
- Provide itk_sbom_register_package() for remote modules to extend

Co-authored-by: dzenanz <1792121+dzenanz@users.noreply.github.com>
@hjmjohnson hjmjohnson force-pushed the copilot/generate-sbom-at-build-time branch from 3e1311b to 3902e8a Compare April 15, 2026 01:37
Copilot AI and others added 2 commits April 14, 2026 20:40
Move SPDX license and per-module metadata from the centralized
lookup table in ITKSBOMGeneration.cmake into each ThirdParty
module's itk-module.cmake file for co-location and easier
maintenance.

- Extend itk_module() macro to accept SPDX_LICENSE,
  SPDX_DOWNLOAD_LOCATION, SPDX_COPYRIGHT,
  SPDX_CUSTOM_LICENSE_TEXT, and SPDX_CUSTOM_LICENSE_NAME
- Add SPDX metadata to all 23 ThirdParty itk-module.cmake files
- Simplify ITKSBOMGeneration.cmake to read from module declarations

Co-authored-by: dzenanz <1792121+dzenanz@users.noreply.github.com>
Custom license names and extracted texts were written directly
into the JSON output without escaping. Any text containing
quotes, backslashes, or newlines would produce invalid JSON.

Apply _itk_sbom_json_escape() to both the name and
extractedText fields before embedding.
Add SPDX_VERSION to the itk_module() macro for declaring the
version of each vendored third-party dependency. Populate
versions for 11 modules extractable from source headers or
UpdateFromUpstream.sh tags:

  Eigen3 (3.4.90), Expat (2.7.4), GoogleTest (1.17.0),
  HDF5 (1.14.5), JPEG/libjpeg-turbo (3.0.4), MINC (2.4.06),
  NIFTI (3.0.0), OpenJPEG (2.5.4), PNG (1.6.54),
  TIFF (4.7.0), ZLIB (2.2.5)

Also fix Eigen3 license from "MPL-2.0" to "MPL-2.0 OR Apache-2.0"
(dual-licensed) and update NIFTI download URL to the current
GitHub repository.
Add ITKSBOMValidation test that validates the generated
sbom.spdx.json at test time: checks JSON syntax, required
SPDX 2.3 fields, at least one package, DESCRIBES relationship,
unique SPDX IDs, and extracted license entries.

Runs only when ITK_GENERATE_SBOM=ON and BUILD_TESTING=ON.
Add VerifySPDXVersions.py that cross-checks SPDX_VERSION declared
in each itk-module.cmake against the tag in UpdateFromUpstream.sh.
For modules with parseable version tags (v1.2.3, R_2_7_4,
hdf5_1.14.5, bare semver), the test verifies consistency. Modules
tracking master, commit SHAs, or custom ITK tags are skipped.

This catches the case where a vendored dependency is updated via
UpdateFromUpstream.sh but SPDX_VERSION is not bumped. Currently
validates 8 of 16 modules with UpdateFromUpstream.sh scripts.
@hjmjohnson hjmjohnson force-pushed the copilot/generate-sbom-at-build-time branch from 3902e8a to bc8d90b Compare April 15, 2026 01:40
Add AUTHOR_WARNING during SBOM generation for enabled ThirdParty
modules that have no SPDX_LICENSE declared in their itk-module.cmake.
This encourages remote module and ThirdParty maintainers to add
SPDX metadata for supply chain compliance.
@hjmjohnson hjmjohnson marked this pull request as ready for review April 15, 2026 06:56
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 15, 2026

Greptile Summary

This PR adds SPDX 2.3 SBOM generation at CMake configure time (ITKSBOMGeneration.cmake), extends itk_module() with SPDX metadata fields, populates all 23 ThirdParty modules, and adds CTest validation plus a version-consistency script. Two fixable defects in the core JSON-building logic should be addressed before merging:

  • Unescaped JSON fields: _pkg_copyright, _pkg_download, _pkg_version, and _pkg_license are inserted into the JSON document without calling _itk_sbom_json_escape() (only _pkg_description is escaped). A copyright string containing \" or \\ would silently produce an invalid sbom.spdx.json. The same gap exists in itk_sbom_register_package.
  • Python3 guard missing: ITKSBOMValidation.cmake registers two CTest tests using ${Python3_EXECUTABLE} without checking if Python3 was found, creating broken tests in non-Python configurations.

Confidence Score: 4/5

Safe to merge after fixing the JSON-escaping omission and adding a Python3 guard in the validation cmake.

Two P1 defects exist: unescaped copyright/download/license fields that would corrupt the generated SBOM if any value contains a double-quote or backslash, and a missing Python3 availability check that produces broken CTest entries in non-Python configurations. Both are straightforward to fix. The remaining findings are P2 style/maintenance items.

CMake/ITKSBOMGeneration.cmake (JSON escaping, foreach pattern), CMake/ITKSBOMValidation.cmake (Python3 guard), Modules/ThirdParty/PNG/itk-module.cmake (SPDX license ID)

Important Files Changed

Filename Overview
CMake/ITKSBOMGeneration.cmake New file: core SBOM generation. P1 — _pkg_copyright, _pkg_download, _pkg_license, and _pkg_version are inserted into JSON without escaping (unlike _pkg_description). P2 — foreach over unquoted list variable; hardcoded licenseListVersion.
CMake/ITKSBOMValidation.cmake New file: CTest validation and version-consistency tests. P1 — ${Python3_EXECUTABLE} is used without a guard; missing Python3 silently registers a broken test.
CMake/ITKModuleMacros.cmake Adds six new SPDX keyword arguments to itk_module(); regex and elseif chain updated consistently; no issues found.
CMakeLists.txt Adds ITK_GENERATE_SBOM option (default ON), wires up itk_generate_sbom() and ITKSBOMValidation includes after module enablement, and adds install rule for sbom.spdx.json; straightforward.
Modules/ThirdParty/PNG/itk-module.cmake P2 — SPDX_LICENSE "Libpng-2.0" is not a recognised SPDX identifier for libpng 1.6.x; the correct identifier is libpng.
Utilities/Maintenance/VerifySPDXVersions.py New helper: cross-checks SPDX_VERSION in itk-module.cmake against UpdateFromUpstream.sh tags for 8 modules; version normalisation patterns look correct; modules with SHA/master/custom tags are skipped.
Modules/ThirdParty/TIFF/itk-module.cmake Adds SPDX metadata; "libtiff" is a valid SPDX identifier for the libtiff licence; no issues.
Modules/ThirdParty/Eigen3/itk-module.cmake Adds SPDX metadata; MPL-2.0 OR Apache-2.0 licence expression is correct for Eigen 3.4.x; no issues.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[CMake configure] --> B{ITK_GENERATE_SBOM?}
    B -- OFF --> Z[skip]
    B -- ON --> C[include ITKSBOMGeneration]
    C --> D[itk_generate_sbom]
    D --> E[Loop over ITK_MODULES_ENABLED]
    E --> F{SPDX_LICENSE set?}
    F -- NO, ThirdParty --> G[AUTHOR_WARNING]
    F -- NO, non-ThirdParty --> H[skip]
    F -- YES --> I[Append package JSON
⚠️ no escape on copyright/download/license]
    D --> J{ITK_USE_FFTW?}
    J -- YES --> K[Append FFTW package]
    D --> L[get_property ITK_SBOM_EXTRA_PACKAGES
foreach unquoted ⚠️]
    L --> M[Append remote module packages]
    D --> N[Build relationships array]
    D --> O[Build hasExtractedLicensingInfo]
    D --> P[file WRITE sbom.spdx.json]
    P --> Q[include ITKSBOMValidation]
    Q --> R{BUILD_TESTING?}
    R -- NO --> Z
    R -- YES --> S{Python3_EXECUTABLE set?
⚠️ not checked}
    S --> T[add_test ITKSBOMValidation
Python JSON validation]
    S --> U[add_test ITKSBOMVersionConsistency
VerifySPDXVersions.py]
Loading

Reviews (1): Last reviewed commit: "ENH: Warn when ThirdParty modules lack S..." | Re-trigger Greptile

Comment thread CMake/ITKSBOMGeneration.cmake
Comment thread CMake/ITKSBOMValidation.cmake
Comment thread CMake/ITKSBOMGeneration.cmake Outdated
Comment thread Modules/ThirdParty/PNG/itk-module.cmake Outdated
Comment thread CMake/ITKSBOMGeneration.cmake Outdated
@hjmjohnson
Copy link
Copy Markdown
Member

Addressing the discussion points from @blowekamp and @dzenanz:

Distributed metadata (dzenanz's request): Done — commit 8692d2d moved all SPDX metadata into each Modules/ThirdParty/*/itk-module.cmake file via new itk_module() parameters (SPDX_LICENSE, SPDX_DOWNLOAD_LOCATION, SPDX_COPYRIGHT, SPDX_VERSION, SPDX_CUSTOM_LICENSE_TEXT/NAME). The centralized lookup table was removed from ITKSBOMGeneration.cmake.

VTK-style integration with module macro (blowekamp's suggestion): This is exactly what was implemented — the SPDX parameters are arguments to the existing itk_module() macro, consistent with VTK's approach. Remote modules can use the same parameters or the standalone itk_sbom_register_package() function.

FFTW licensing (blowekamp): FFTW is handled as a special case in ITKSBOMGeneration.cmake since it's not an ITK module — it's detected via ITK_USE_FFTWD/ITK_USE_FFTWF and tagged as GPL-2.0-or-later.

Greptile findings: All 5 inline findings (2 P1, 3 P2) have been reviewed and acknowledged — JSON escaping gaps, missing Python3 guard, IN LISTS foreach, PNG license ID, and hardcoded licenseListVersion. Will address in follow-up commits.

Address all findings from greptile code review:

P1: Escape all user-supplied fields (version, download location,
license, copyright) through _itk_sbom_json_escape() before JSON
interpolation — both in itk_generate_sbom() and
itk_sbom_register_package(). Previously only description was escaped.

P1: Add Python3_EXECUTABLE guard in ITKSBOMValidation.cmake to skip
SBOM validation tests when Python3 is not available.

P2: Use foreach(... IN LISTS ...) instead of unquoted variable
expansion to prevent re-splitting on embedded semicolons.

P2: Fix PNG SPDX license identifier from "Libpng-2.0" to "Libpng"
(the valid SPDX license list entry for libpng 1.6.x).

P2: Expose licenseListVersion as ITK_SBOM_SPDX_LICENSE_LIST_VERSION
cache variable (default "3.25") instead of hardcoding "3.22".
@hjmjohnson
Copy link
Copy Markdown
Member

All 5 greptile findings fixed in 6d31421.

Fix summary
Finding Fix
P1 Unescaped JSON fields Added _itk_sbom_json_escape() calls for version, download, license, copyright in both itk_generate_sbom() and itk_sbom_register_package()
P1 Missing Python3 guard Added if(NOT Python3_EXECUTABLE) early-return in ITKSBOMValidation.cmake
P2 foreach without IN LISTS Switched both loops (extra_packages, extra_spdx_ids) to IN LISTS
P2 PNG license Libpng-2.0 Changed to Libpng (valid SPDX identifier)
P2 Hardcoded licenseListVersion Exposed as ITK_SBOM_SPDX_LICENSE_LIST_VERSION cache variable (default 3.25)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Python wrapping Python bindings for a class area:ThirdParty Issues affecting the ThirdParty module type:Enhancement Improvement of existing methods or implementation type:Infrastructure Infrastructure/ecosystem related changes, such as CMake or buildbots

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generate a Software Bill of Materials at Build Time

4 participants