export const VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-(?:alpha|beta|rc)\.\d+)?$/; export const APPROVED_LICENSE_IDS = new Set([ "0BSD", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "BlueOak-1.0.0", "CC0-1.0", "ISC", "MIT", "Python-2.0", "Unicode-3.0", "Unlicense", "Zlib" ]); // Dual-licensed dependencies require one explicit, version-pinned review. // DOMPurify publishes an Apache-2.0 alternative. This exception does not // approve MPL-2.0 as a general license for other production dependencies. const REVIEWED_PACKAGE_LICENSES = new Map([ ["dompurify@3.4.17", "(MPL-2.0 OR Apache-2.0)"], // khroma 2.1.0 npm omits SPDX, but upstream /license contains the MIT grant. ["khroma@2.1.0", "Unknown"] ]); export function normalizeVersion(value) { const version = String(value ?? "").trim().replace(/^v/, ""); if (!VERSION_PATTERN.test(version)) { throw new Error( `Unsupported version: ${version || ""}; use X.Y.Z or X.Y.Z-alpha.N / beta.N / rc.N` ); } return version; } function extractSection(markdown, label) { const lines = markdown.split(/\r?\n/); const prefix = `## [${label}]`; const start = lines.findIndex( (line) => line === prefix || line.startsWith(`${prefix} `) ); if (start < 0) return undefined; let end = lines.length; for (let index = start + 1; index < lines.length; index += 1) { if (/^##\s+/.test(lines[index])) { end = index; break; } } return lines.slice(start + 1, end).join("\n").trim(); } export function extractReleaseSection(markdown, version) { return extractSection(markdown, normalizeVersion(version)); } function isStandaloneHtmlComment(line) { if (!line.startsWith("")) return false; const body = line.slice(4, -3); return !body.includes(""); } export function isUnreleasedEmpty(markdown) { const body = extractSection(markdown, "Unreleased"); if (body === undefined) return false; return body .split(/\r?\n/) .map((line) => line.trim()) .every((line) => line.length === 0 || isStandaloneHtmlComment(line)); } function isApprovedLicenseExpression(expression) { const value = String(expression ?? "").trim(); if ( !value || /unknown|unlicensed|see license in|proprietary|custom/i.test(value) || !/^[A-Za-z0-9.+()\-\s]+$/.test(value) ) { return false; } const tokens = value.match(/[A-Za-z0-9][A-Za-z0-9.+-]*/g) ?? []; let licenseCount = 0; for (const token of tokens) { if (/^(?:AND|OR)$/i.test(token)) continue; if (/^WITH$/i.test(token)) return false; licenseCount += 1; if (!APPROVED_LICENSE_IDS.has(token)) return false; } return licenseCount > 0; } export function inspectLicenseReport(report) { const packages = []; if (Array.isArray(report)) { for (const item of report) { if (item && typeof item === "object") { packages.push({ name: String(item.name ?? ""), versions: Array.isArray(item.versions) ? item.versions.map(String) : [String(item.version ?? "")], license: String(item.license ?? "Unknown") }); } } } else if (report && typeof report === "object") { for (const [groupLicense, entries] of Object.entries(report)) { if (!Array.isArray(entries)) continue; for (const item of entries) { if (!item || typeof item !== "object") continue; packages.push({ name: String(item.name ?? ""), versions: Array.isArray(item.versions) ? item.versions.map(String) : [String(item.version ?? "")], license: String(item.license ?? groupLicense ?? "Unknown") }); } } } if (packages.length === 0) { throw new Error("pnpm returned no production dependency license records"); } const failures = []; for (const pkg of packages) { const specificallyReviewed = pkg.versions.length === 1 && REVIEWED_PACKAGE_LICENSES.get(`${pkg.name}@${pkg.versions[0]}`) === pkg.license; if (!isApprovedLicenseExpression(pkg.license) && !specificallyReviewed) { failures.push( `${pkg.name}@${pkg.versions.join(",")} = ${pkg.license}` ); } } if (failures.length > 0) { throw new Error( "Production dependency licenses require explicit review:\n - " + failures.join("\n - ") ); } return { packageCount: packages.length, licenses: [...new Set(packages.map((pkg) => pkg.license))].sort() }; }