33 lines
1.6 KiB
TypeScript
33 lines
1.6 KiB
TypeScript
const exceptions = await Bun.file(new URL("../security/audit-exceptions.json", import.meta.url)).json() as Array<{
|
|
package: string; advisory: string; owner: string; expires: string; reason: string;
|
|
}>;
|
|
const processAudit = Bun.spawn([process.execPath, "audit", "--json"], { stdout: "pipe", stderr: "pipe" });
|
|
const [output, errors, exitCode] = await Promise.all([
|
|
new Response(processAudit.stdout).text(), new Response(processAudit.stderr).text(), processAudit.exited,
|
|
]);
|
|
let audit: Record<string, Array<{ url: string; severity: string; title: string }>>;
|
|
try {
|
|
audit = JSON.parse(output);
|
|
} catch {
|
|
console.error("Dependency audit did not return JSON; refusing to pass.", errors.replace(/https?:\/\/\S+/gu, "[registry]"));
|
|
process.exit(1);
|
|
}
|
|
if (exitCode > 1 || (exitCode !== 0 && Object.keys(audit).length === 0)) process.exit(1);
|
|
let failed = false;
|
|
for (const [name, advisories] of Object.entries(audit)) {
|
|
for (const advisory of advisories) {
|
|
const id = advisory.url.split("/").at(-1);
|
|
const exception = exceptions.find((item) => item.package === name && item.advisory === id);
|
|
if (exception && exception.owner && exception.reason && new Date(`${exception.expires}T00:00:00Z`).getTime() > Date.now()) {
|
|
console.warn(`Accepted development finding ${name}: ${id}; owner=${exception.owner}; expires=${exception.expires}`);
|
|
} else {
|
|
failed = true;
|
|
console.error(`${advisory.severity}: ${name}: ${advisory.title} (${advisory.url})`);
|
|
}
|
|
}
|
|
}
|
|
if (failed) process.exit(1);
|
|
console.info("Dependency audit passed with only documented, unexpired exceptions.");
|
|
|
|
export {};
|