From ee62c8315c88f07a4dc6c9f2114b9a0988fb7889 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Tue, 24 Feb 2026 11:51:11 -0400 Subject: [PATCH] Generate a SBOM out of the vendored dependencies Signed-off-by: Juan Cruz Viotti --- CMakeLists.txt | 1 + enterprise/Dockerfile | 9 ++- enterprise/scripts/sbom-vendorpull.js | 103 ++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 3 deletions(-) create mode 100755 enterprise/scripts/sbom-vendorpull.js diff --git a/CMakeLists.txt b/CMakeLists.txt index 1680df4a4..be8e6fe93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ else() set(ONE_VERSION "${PROJECT_VERSION}+${PROJECT_GIT_SHA}") endif() message(STATUS "One version: ${ONE_VERSION}") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/VERSION" "${ONE_VERSION}") if(ONE_INDEX OR ONE_SERVER) add_subdirectory(src/shared) diff --git a/enterprise/Dockerfile b/enterprise/Dockerfile index 15b64a7c8..cc441d6eb 100644 --- a/enterprise/Dockerfile +++ b/enterprise/Dockerfile @@ -36,9 +36,6 @@ COPY test/unit /source/test/unit COPY test/js /source/test/js RUN cd /source && npm ci -RUN mkdir -p /usr/share/sourcemeta/one \ - && cd /source && npm sbom --sbom-format spdx --sbom-type library --omit dev \ - > /usr/share/sourcemeta/one/npm-packages.spdx.json ARG SOURCEMETA_ONE_BUILD_TYPE=Release ARG SOURCEMETA_ONE_PARALLEL=2 @@ -74,6 +71,12 @@ RUN cmake --build /build --config ${SOURCEMETA_ONE_BUILD_TYPE} \ RUN ctest --test-dir /build --build-config ${SOURCEMETA_ONE_BUILD_TYPE} \ --output-on-failure --parallel +RUN mkdir -p /usr/share/sourcemeta/one \ + && cd /source && npm sbom --sbom-format spdx --sbom-type library --omit dev \ + > /usr/share/sourcemeta/one/npm-packages.spdx.json \ + && node /source/enterprise/scripts/sbom-vendorpull.js /build/VERSION \ + > /usr/share/sourcemeta/one/vendor-packages.spdx.json + FROM debian:trixie-slim RUN apt-get --yes update && apt-get install --yes --no-install-recommends \ diff --git a/enterprise/scripts/sbom-vendorpull.js b/enterprise/scripts/sbom-vendorpull.js new file mode 100755 index 000000000..bfd096512 --- /dev/null +++ b/enterprise/scripts/sbom-vendorpull.js @@ -0,0 +1,103 @@ +#!/usr/bin/env node + +// Generates an SPDX 2.3 JSON Software Bill of Materials (SBOM) for the +// vendored C++ and frontend dependencies managed through DEPENDENCIES files + +import { readFileSync, readdirSync, existsSync } from "node:fs"; +import { join, resolve, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +const LICENSES = { + "core": "AGPL-3.0-or-later OR LicenseRef-Commercial", + "blaze": "AGPL-3.0-or-later OR LicenseRef-Commercial", + "hydra": "AGPL-3.0-or-later OR LicenseRef-Commercial", + "codegen": "AGPL-3.0-or-later OR LicenseRef-Commercial", + "jsonbinpack": "AGPL-3.0-or-later OR LicenseRef-Commercial", + "jsonschema": "AGPL-3.0-only", + "uwebsockets": "Apache-2.0", + "bootstrap": "MIT", + "bootstrap-icons": "MIT", + "pcre2": "BSD-3-Clause", + "zlib": "Zlib", + "curl": "curl", + "nghttp2": "MIT", + "cpr": "MIT", + "c-ares": "MIT", + "libpsl": "MIT", + "openssl": "Apache-2.0" +}; + +const versionFile = process.argv[2]; +if (!versionFile) { + process.stderr.write(`Usage: ${process.argv[1]} \n`); + process.exit(1); +} + +const version = readFileSync(versionFile, "utf-8").trim(); +const root = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); + +const vendorDirectory = join(root, "vendor"); +const files = [ + join(root, "DEPENDENCIES"), + ...existsSync(vendorDirectory) + ? readdirSync(vendorDirectory, { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => join(vendorDirectory, entry.name, "DEPENDENCIES")) + : [] +].filter(existsSync).sort(); + +const seenUrls = new Set(); +const packages = [{ + name: "sourcemeta-one-enterprise", + SPDXID: "SPDXRef-RootPackage", + versionInfo: version, + downloadLocation: "https://github.com/sourcemeta/one", + filesAnalyzed: false, + licenseConcluded: "NOASSERTION", + licenseDeclared: "NOASSERTION" +}]; +const relationships = [{ + spdxElementId: "SPDXRef-DOCUMENT", + relationshipType: "DESCRIBES", + relatedSpdxElement: "SPDXRef-RootPackage" +}]; + +let index = 0; +for (const file of files) { + for (const line of readFileSync(file, "utf-8").split("\n")) { + if (!line.trim()) continue; + const [name, url, entryVersion] = line.split(/\s+/); + const license = LICENSES[name]; + if (!license || seenUrls.has(url)) continue; + seenUrls.add(url); + index += 1; + const spdxid = `SPDXRef-Vendor-${index}`; + packages.push({ + name, SPDXID: spdxid, + versionInfo: entryVersion, + downloadLocation: url, + filesAnalyzed: false, + licenseConcluded: license, + licenseDeclared: license + }); + relationships.push({ + spdxElementId: "SPDXRef-RootPackage", + relationshipType: "DEPENDS_ON", + relatedSpdxElement: spdxid + }); + } +} + +process.stdout.write(JSON.stringify({ + spdxVersion: "SPDX-2.3", + dataLicense: "CC0-1.0", + SPDXID: "SPDXRef-DOCUMENT", + name: "sourcemeta-one-enterprise", + documentNamespace: `https://one.sourcemeta.com/sbom/${version}`, + creationInfo: { + created: new Date().toISOString(), + creators: [ "Tool: enterprise/scripts/sbom-vendorpull.js" ] + }, + packages, + relationships +}, null, 2) + "\n");