Files
workinf_Blender_Wasm/tools/web/check-supply-chain-binding.mjs
mes123456 380cbed4ff
Some checks failed
M6 deployable RC / quick (push) Has been cancelled
M6 deployable RC / chromium (push) Has been cancelled
M6 deployable RC / release (push) Has been cancelled
Checkpoint web parity through Chromium input tasks
2026-08-19 10:39:03 -04:00

78 lines
5.6 KiB
JavaScript

import assert from "node:assert/strict";
import crypto from "node:crypto";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { execFileSync } from "node:child_process";
import { fileURLToPath } from "node:url";
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const releaseRoot = path.join(root, "release");
const reportPath = path.join(root, "tests/golden/M13-05E/supply-chain-report.json");
const manifestPath = path.join(root, "tests/golden/M13-05E/manifest.json");
const sha256 = (bytes) => crypto.createHash("sha256").update(bytes).digest("hex");
const fileSha256 = (file) => sha256(fs.readFileSync(file));
const archive = path.join(releaseRoot, "blender-web-offline.tar.gz");
const sourceArchive = path.join(releaseRoot, "blender-web-corresponding-source.tar.gz");
const sumsPath = path.join(releaseRoot, "SHA256SUMS.txt");
const sbomPath = path.join(root, "docs/web/sbom.spdx.json");
const noticesPath = path.join(root, "docs/web/third-party-notices.json");
const lockPath = path.join(root, "web/package-lock.json");
const packagePath = path.join(root, "web/package.json");
const sbom = JSON.parse(fs.readFileSync(sbomPath, "utf8"));
const notices = JSON.parse(fs.readFileSync(noticesPath, "utf8"));
const lockBytes = fs.readFileSync(lockPath);
const noticesBytes = fs.readFileSync(noticesPath);
const lockHash = sha256(lockBytes);
const noticesHash = sha256(noticesBytes);
assert.equal(sbom.spdxVersion, "SPDX-2.3");
assert.equal(sbom.documentNamespace, `https://blender-web.local/spdx/${sha256(Buffer.concat([lockBytes, noticesBytes]))}`);
const rootPackage = sbom.packages.find((item) => item.SPDXID === "SPDXRef-Package-blender-web-editor");
assert.ok(rootPackage);
assert.equal(rootPackage.checksums?.find((item) => item.algorithm === "SHA256")?.checksumValue, lockHash);
assert.ok(notices.packages.length > 0);
assert.ok(fs.statSync(archive).isFile());
assert.ok(fs.statSync(sourceArchive).isFile());
const sums = new Map(fs.readFileSync(sumsPath, "utf8").trim().split(/\r?\n/u).map((line) => {
const match = line.match(/^([a-f0-9]{64}) (.+)$/u);
assert.ok(match, `invalid checksum line ${line}`);
return [match[2], match[1]];
}));
assert.equal(sums.get(path.basename(archive)), fileSha256(archive));
assert.equal(sums.get(path.basename(sourceArchive)), fileSha256(sourceArchive));
function archiveEntries(file) {
return execFileSync("tar", ["-tzf", file], { encoding: "utf8", maxBuffer: 32 * 1024 * 1024 }).split(/\r?\n/u).filter(Boolean);
}
function archiveFile(file, entry) {
return execFileSync("tar", ["-xOf", file, entry], { maxBuffer: 64 * 1024 * 1024 });
}
const binaryEntries = archiveEntries(archive);
const sourceEntries = archiveEntries(sourceArchive);
for (const entry of ["blender-web-offline/sbom.spdx.json", "blender-web-offline/third-party-notices.json", "blender-web-offline/SOURCE_OFFER.txt", "blender-web-offline/manifest.json"]) assert.ok(binaryEntries.includes(entry), `binary archive omits ${entry}`);
for (const entry of ["web/package.json", "web/package-lock.json", "docs/web/sbom.spdx.json", "docs/web/third-party-notices.json", "docs/web/DEPLOYMENT.md", "tools/web/create-offline-release.mjs"]) assert.ok(sourceEntries.includes(entry), `source archive omits ${entry}`);
const sourceOffer = archiveFile(archive, "blender-web-offline/SOURCE_OFFER.txt").toString("utf8");
assert.match(sourceOffer, /blender-web-corresponding-source\.tar\.gz/u);
assert.match(sourceOffer, /SHA256SUMS\.txt/u);
const embeddedPackage = archiveFile(sourceArchive, "web/package.json");
const embeddedLock = archiveFile(sourceArchive, "web/package-lock.json");
assert.equal(sha256(embeddedPackage), fileSha256(packagePath));
assert.equal(sha256(embeddedLock), lockHash);
const embeddedSbom = archiveFile(archive, "blender-web-offline/sbom.spdx.json");
assert.deepEqual(JSON.parse(embeddedSbom), sbom);
const commit = execFileSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" }).trim();
assert.match(commit, /^[a-f0-9]{40}$/u);
const report = {
schemaVersion: 1, task: "M13-05E", operation: "SUPPLY_CHAIN_BINDING", commit,
inputs: { packageSha256: fileSha256(packagePath), lockfileSha256: lockHash, sbomSha256: fileSha256(sbomPath), noticesSha256: noticesHash },
archives: { binary: { path: path.relative(root, archive), sha256: fileSha256(archive), entries: binaryEntries.length }, source: { path: path.relative(root, sourceArchive), sha256: fileSha256(sourceArchive), entries: sourceEntries.length } },
sourceOffer: "BOUND_TO_CORRESPONDING_SOURCE_ARCHIVE_AND_SHA256SUMS",
checks: { spdx23: true, lockfileBound: true, noticesBound: true, sourceOfferBound: true, archiveChecksumsBound: true, sourcePackageBound: true },
execution: "DISABLED", nextTask: "M13-05F",
};
if (process.env.UPDATE_M13_05E_REPORT === "1") { fs.mkdirSync(path.dirname(reportPath), { recursive: true }); fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); }
assert.deepEqual(JSON.parse(fs.readFileSync(reportPath, "utf8")), report);
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
assert.deepEqual({ schemaVersion: manifest.schemaVersion, task: manifest.task, parentTask: manifest.parentTask, nextTask: manifest.nextTask }, { schemaVersion: 1, task: "M13-05E", parentTask: "M13-05D", nextTask: "M13-05F" });
for (const artifact of Object.values(manifest.artifacts)) assert.equal(fileSha256(path.join(root, artifact.path)), artifact.sha256, artifact.path);
process.stdout.write(`supply-chain-binding-ok sbom=SPDX-2.3 lockfile=BOUND notices=BOUND sourceOffer=BOUND binarySha256=${report.archives.binary.sha256} sourceSha256=${report.archives.source.sha256} execution=DISABLED next=M13-05F\n`);