Skip to content

tools,build: add CycloneDX SBOM generation - #66761

Draft
BethGriggs wants to merge 1 commit into
nodejs:mainfrom
BethGriggs:poc-sbom-impl
Draft

BethGriggs wants to merge 1 commit into
nodejs:mainfrom
BethGriggs:poc-sbom-impl

Conversation

@BethGriggs

Copy link
Copy Markdown
Member

This is a proof-of-concept for build-time SBOM generation in Node.js core (nodejs/security-wg#1115).

The PR adds tools/generate-sbom.py, a stdlib-only Python 3 script that produces a CycloneDX 1.5 JSON Software Bill of Materials covering all bundled runtime dependencies of the Node.js binary.

The script reads config.gypi (produced by ./configure) to determine which dependencies are active in the current build, then extracts version strings from committed C headers, CMakeLists.txt files, package.json files,
and other metadata sources across all deps/ subdirectories. Dependencies disabled via configure flags or satisfied by system libraries are listed with scope: excluded so the document accurately reflects the actual build.

./configure
make sbom       # → out/Release/node-v27.0.0-sbom.json
make test-sbom  # validates the output

Pre-emptive questions

  • How much ongoing maintenance does this require?
    • Little. Dep version bumps are picked up automatically from committed headers and package.json files. New JS files in lib/ flow through config.gypi automatically. The only manual step is one DEP_CATALOG entry when a new C/C++ dep is added - a CI test enforces this so a missing entry fails rather than producing a silent gap.
  • Why CycloneDX and not SPDX?:
    • npm already ships a CycloneDX 1.5 implementation in deps/npm/lib/utils/sbom-cyclonedx.js. SPDX support could be added later behind a --format flag.
  • Why Python and not a Node.js script?:
    • The script runs before node is built. ./configure just needs to have run. Python is already the language of the entire build toolchain (configure.py, gyp_node.py, install.py).
  • Why not generate this automatically during make build / make release?
    • Intentionally out of scope for this PR, the idea is that we'd review and iterate before committing to ship it as a release asset.

SBOM snippets

{
  "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
  "bomFormat": "CycloneDX",
  "specVersion": "1.5",
  "serialNumber": "urn:uuid:17555426-32bc-454a-b766-1a7de082741c",
  "version": 1,
  "metadata": {
    "timestamp": "2026-10-15T13:21:37.952127+00:00",
    "lifecycles": [
      {
        "phase": "build"
      }
    ],
    "tools": [
      {
        "vendor": "Node.js",
        "name": "generate_sbom.py",
        "version": "27.0.0"
      }
    ],
    "component": {
      "bom-ref": "nodejs@27.0.0",
      "type": "framework",
      "name": "nodejs",
      "version": "27.0.0",
      "description": "Node.js JavaScript runtime",
      "purl": "pkg:github/nodejs/node@27.0.0"
    },

 ...
 "components": [
    {
      "bom-ref": "openssl@3.5.9",
      "type": "library",
      "name": "openssl",
      "version": "3.5.9",
      "description": "TLS/SSL and general-purpose cryptography library",
      "scope": "required",
      "purl": "pkg:github/openssl/openssl@3.5.9"
    },
    {
      "bom-ref": "zlib@1.3.2.1",
      "type": "library",
      "name": "zlib",
      "version": "1.3.2.1",
      "description": "General-purpose lossless data compression library",
      "scope": "required",
      "purl": "pkg:github/madler/zlib@1.3.2.1"
    },
    ...

Add tools/generate_sbom.py, a stdlib-only Python 3 script that writes a
CycloneDX 1.5 SBOM describing the bundled dependencies of the Node.js
binary.

The script reads config.gypi to determine which dependencies are active
in the current build, then extracts version strings from whichever file
each dependency uses to record its version: C headers, CMakeLists.txt,
package.json, Cargo.toml and others. A dependency whose version cannot
be read is reported as "unknown" with a warning rather than failing the
run. Dependencies satisfied by a system library or disabled via a
configure flag are emitted with scope "excluded", so the document
reflects the build that was configured.

Output defaults to out/Release/node-v{version}-sbom.json.

New Makefile targets:

  make sbom       - generate the SBOM (requires ./configure first)
  make test-sbom  - validate the generated SBOM

doc/contributing/maintaining/maintaining-dependencies.md now asks for a
DEP_CATALOG entry when adding a dependency, so new deps do not silently
go missing from the SBOM.

Unit tests in test/tools/test_generate_sbom.py cover catalog structural
correctness, the version extractors, scope resolution and deps/
coverage. They run under the existing `make tooltest` target.

Refs: nodejs/security-wg#1115

Assisted-by: IBM Bob
Signed-off-by: Beth Griggs <bethanyngriggs@gmail.com>
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/tsc

@nodejs-github-bot nodejs-github-bot added build Issues and PRs related to Node.js builds or CI infrastructure. needs-ci PRs that need a full CI run. tools Issues and PRs related to the tools directory. labels Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build Issues and PRs related to Node.js builds or CI infrastructure. needs-ci PRs that need a full CI run. tools Issues and PRs related to the tools directory.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants