|
| 1 | +#!/usr/bin/env node |
| 2 | + |
| 3 | +// Generates an SPDX 2.3 JSON Software Bill of Materials (SBOM) for the |
| 4 | +// vendored C++ and frontend dependencies managed through DEPENDENCIES files |
| 5 | + |
| 6 | +import { readFileSync, readdirSync, existsSync } from "node:fs"; |
| 7 | +import { join, resolve, dirname } from "node:path"; |
| 8 | +import { fileURLToPath } from "node:url"; |
| 9 | + |
| 10 | +const LICENSES = { |
| 11 | + "core": "AGPL-3.0-or-later OR LicenseRef-Commercial", |
| 12 | + "blaze": "AGPL-3.0-or-later OR LicenseRef-Commercial", |
| 13 | + "hydra": "AGPL-3.0-or-later OR LicenseRef-Commercial", |
| 14 | + "codegen": "AGPL-3.0-or-later OR LicenseRef-Commercial", |
| 15 | + "jsonbinpack": "AGPL-3.0-or-later OR LicenseRef-Commercial", |
| 16 | + "jsonschema": "AGPL-3.0-only", |
| 17 | + "uwebsockets": "Apache-2.0", |
| 18 | + "bootstrap": "MIT", |
| 19 | + "bootstrap-icons": "MIT", |
| 20 | + "pcre2": "BSD-3-Clause", |
| 21 | + "zlib": "Zlib", |
| 22 | + "curl": "curl", |
| 23 | + "nghttp2": "MIT", |
| 24 | + "cpr": "MIT", |
| 25 | + "c-ares": "MIT", |
| 26 | + "libpsl": "MIT", |
| 27 | + "openssl": "Apache-2.0" |
| 28 | +}; |
| 29 | + |
| 30 | +const versionFile = process.argv[2]; |
| 31 | +if (!versionFile) { |
| 32 | + process.stderr.write(`Usage: ${process.argv[1]} <version-file>\n`); |
| 33 | + process.exit(1); |
| 34 | +} |
| 35 | + |
| 36 | +const version = readFileSync(versionFile, "utf-8").trim(); |
| 37 | +const root = resolve(dirname(fileURLToPath(import.meta.url)), "../.."); |
| 38 | + |
| 39 | +const vendorDirectory = join(root, "vendor"); |
| 40 | +const files = [ |
| 41 | + join(root, "DEPENDENCIES"), |
| 42 | + ...existsSync(vendorDirectory) |
| 43 | + ? readdirSync(vendorDirectory, { withFileTypes: true }) |
| 44 | + .filter((entry) => entry.isDirectory()) |
| 45 | + .map((entry) => join(vendorDirectory, entry.name, "DEPENDENCIES")) |
| 46 | + : [] |
| 47 | +].filter(existsSync).sort(); |
| 48 | + |
| 49 | +const seenUrls = new Set(); |
| 50 | +const packages = [{ |
| 51 | + name: "sourcemeta-one-enterprise", |
| 52 | + SPDXID: "SPDXRef-RootPackage", |
| 53 | + versionInfo: version, |
| 54 | + downloadLocation: "https://github.com/sourcemeta/one", |
| 55 | + filesAnalyzed: false, |
| 56 | + licenseConcluded: "NOASSERTION", |
| 57 | + licenseDeclared: "NOASSERTION" |
| 58 | +}]; |
| 59 | +const relationships = [{ |
| 60 | + spdxElementId: "SPDXRef-DOCUMENT", |
| 61 | + relationshipType: "DESCRIBES", |
| 62 | + relatedSpdxElement: "SPDXRef-RootPackage" |
| 63 | +}]; |
| 64 | + |
| 65 | +let index = 0; |
| 66 | +for (const file of files) { |
| 67 | + for (const line of readFileSync(file, "utf-8").split("\n")) { |
| 68 | + if (!line.trim()) continue; |
| 69 | + const [name, url, entryVersion] = line.split(/\s+/); |
| 70 | + const license = LICENSES[name]; |
| 71 | + if (!license || seenUrls.has(url)) continue; |
| 72 | + seenUrls.add(url); |
| 73 | + index += 1; |
| 74 | + const spdxid = `SPDXRef-Vendor-${index}`; |
| 75 | + packages.push({ |
| 76 | + name, SPDXID: spdxid, |
| 77 | + versionInfo: entryVersion, |
| 78 | + downloadLocation: url, |
| 79 | + filesAnalyzed: false, |
| 80 | + licenseConcluded: license, |
| 81 | + licenseDeclared: license |
| 82 | + }); |
| 83 | + relationships.push({ |
| 84 | + spdxElementId: "SPDXRef-RootPackage", |
| 85 | + relationshipType: "DEPENDS_ON", |
| 86 | + relatedSpdxElement: spdxid |
| 87 | + }); |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +process.stdout.write(JSON.stringify({ |
| 92 | + spdxVersion: "SPDX-2.3", |
| 93 | + dataLicense: "CC0-1.0", |
| 94 | + SPDXID: "SPDXRef-DOCUMENT", |
| 95 | + name: "sourcemeta-one-enterprise", |
| 96 | + documentNamespace: `https://one.sourcemeta.com/sbom/${version}`, |
| 97 | + creationInfo: { |
| 98 | + created: new Date().toISOString(), |
| 99 | + creators: [ "Tool: enterprise/scripts/sbom-vendorpull.js" ] |
| 100 | + }, |
| 101 | + packages, |
| 102 | + relationships |
| 103 | +}, null, 2) + "\n"); |
0 commit comments