radroots_sdk_package_license_lib.mjs (2050B)
1 import { createHash } from "node:crypto"; 2 import { readFileSync } from "node:fs"; 3 import { join } from "node:path"; 4 5 const CONTRACT_PATH = "contracts/generated_package_licenses.v1.json"; 6 const EXPECTED_FILES = Object.freeze(["LICENSE-APACHE", "LICENSE-MIT"]); 7 8 function fail(message) { 9 throw new Error(`supply_chain_invalid: ${message}`); 10 } 11 12 function exactKeys(value, expected, context) { 13 if (!value || typeof value !== "object" || Array.isArray(value)) { 14 fail(`${context} must be an object`); 15 } 16 const actual = Object.keys(value).sort(); 17 if (JSON.stringify(actual) !== JSON.stringify([...expected].sort())) { 18 fail(`${context} has invalid keys`); 19 } 20 } 21 22 function sha256(path) { 23 return createHash("sha256").update(readFileSync(path)).digest("hex"); 24 } 25 26 export function validateGeneratedPackageLicenses(root, packageDirectories, expectedRevision) { 27 const contract = JSON.parse(readFileSync(join(root, CONTRACT_PATH), "utf8")); 28 exactKeys( 29 contract, 30 ["schema", "source_repository", "source_revision", "files"], 31 "generated package license contract", 32 ); 33 if ( 34 contract.schema !== "radroots.sdk.generated-package-licenses.v1" || 35 contract.source_repository !== "https://github.com/radrootslabs/lib" || 36 contract.source_revision !== expectedRevision 37 ) { 38 fail("generated package license authority drifted"); 39 } 40 if (!Array.isArray(contract.files) || contract.files.length !== EXPECTED_FILES.length) { 41 fail("generated package license inventory drifted"); 42 } 43 44 for (let index = 0; index < EXPECTED_FILES.length; index += 1) { 45 const file = contract.files[index]; 46 exactKeys(file, ["name", "sha256"], `generated package license ${index}`); 47 if (file.name !== EXPECTED_FILES[index] || !/^[0-9a-f]{64}$/.test(file.sha256)) { 48 fail("generated package license identity drifted"); 49 } 50 for (const packageDirectory of packageDirectories) { 51 if (sha256(join(packageDirectory, file.name)) !== file.sha256) { 52 fail("generated package license bytes drifted"); 53 } 54 } 55 } 56 }