Files
Mebbling/scripts/generate-sbom.mjs
T

17 lines
1.9 KiB
JavaScript

#!/usr/bin/env node
import { readFile, mkdir, writeFile } from "node:fs/promises";
import { dirname, resolve } from "node:path";
const output = resolve(process.argv[2] || "artifacts/mebbling.spdx.json");
const lock = JSON.parse(await readFile("package-lock.json", "utf8"));
const packages = Object.entries(lock.packages)
.filter(([path, value]) => path.startsWith("node_modules/") && value.version)
.map(([path, value]) => {
const name = path.slice("node_modules/".length);
return { SPDXID: `SPDXRef-Package-${name.replace(/[^A-Za-z0-9.-]/g, "-")}`, name, versionInfo: value.version, downloadLocation: value.resolved || "NOASSERTION", licenseConcluded: "NOASSERTION", licenseDeclared: value.license || "NOASSERTION", checksums: value.integrity ? [{ algorithm: "SHA512", checksumValue: value.integrity.replace(/^sha512-/, "") }] : [], externalRefs: [{ referenceCategory: "PACKAGE-MANAGER", referenceType: "purl", referenceLocator: `pkg:npm/${encodeURIComponent(name).replace("%40", "@")}@${value.version}` }] };
});
const sbom = { spdxVersion: "SPDX-2.3", dataLicense: "CC0-1.0", SPDXID: "SPDXRef-DOCUMENT", name: "mebbling", documentNamespace: `https://gitea.fishking.studio/tangsongdayo/Mebbling/sbom/${lock.version}`, creationInfo: { created: new Date().toISOString(), creators: ["Tool: Mebbling package-lock SBOM generator"] }, packages: [{ SPDXID: "SPDXRef-Mebbling", name: lock.name, versionInfo: lock.version, downloadLocation: "NOASSERTION", licenseConcluded: "NOASSERTION", licenseDeclared: lock.packages[""].license || "NOASSERTION" }, ...packages], relationships: packages.map((item) => ({ spdxElementId: "SPDXRef-Mebbling", relationshipType: "DEPENDS_ON", relatedSpdxElement: item.SPDXID })) };
await mkdir(dirname(output), { recursive: true });
await writeFile(output, `${JSON.stringify(sbom, null, 2)}\n`);
console.log(`Generated SPDX SBOM with ${packages.length} packages: ${output}`);