UNPKG

yarn-audit-html

Version:

Generate a HTML report for Yarn Audit

158 lines (133 loc) 3.89 kB
#!/usr/bin/env node import { fstatSync } from "node:fs"; import { spawnSync } from "node:child_process"; import process from "node:process"; import { program, Option } from "commander"; import { bailWithError, generateReport, parseAdvisory } from "./index.js"; import { AuditMetadata, RawAuditAdvisor, AuditAdvisoryData, Options, AuditMetadataData, AuditAdvisor, } from "./types.js"; async function run(argv: NodeJS.Process["argv"], input: typeof process.stdin) { program .option("-o, --output [output path]", "output file", "yarn-audit.html") .option( "-t, --template [ejs path]", "ejs template path", new URL("../templates/template.ejs", import.meta.url).pathname, ) .addOption( new Option("--theme [theme name]", "Bootswatch theme. see https://bootswatch.com/#:~:text=Cerulean") .default("materia") .choices([ "cerulean", "cosmo", "cyborg", "darkly", "flatly", "journal", "litera", "lumen", "lux", "materia", "minty", "morph", "pulse", "quartz", "sandstone", "simplex", "sketchy", "slate", "solar", "spacelab", "superhero", "united", "vapor", "yeti", "zephyr", ]), ) .option("--fatal-exit-code", "exit with code 1 if vulnerabilities were found", false) .parse(argv); console.info("Checking audit logs..."); let summary: AuditMetadata = { vulnerabilities: { info: 0, low: 0, moderate: 0, high: 0, critical: 0 }, dependencies: 0, devDependencies: 0, optionalDependencies: 0, totalDependencies: 0, }; const options = program.opts<Options>(); const vulnerabilities = new Map<string, AuditAdvisor>(); const { stdout } = spawnSync("yarn", ["--version"]); const yarnMajorVersion = Number.parseInt(stdout.toString()); // Determine if cli is piped *in* const stats = fstatSync(0); if (!stats.isFIFO()) { program.outputHelp(); return process.exit(1); } let text = ""; input.on("readable", function (this: typeof process.stdin) { try { const chunk = this.read(); if (chunk !== null) { text += chunk; const lines = text.split("\n"); if (lines.length > 1) { text = lines.splice(-1, 1)[0]; lines.forEach((line) => { if (yarnMajorVersion >= 2) { const auditAdvisoryData = JSON.parse(line) as { advisories: Record<string, RawAuditAdvisor>; metadata: AuditMetadata; }; Object.values(auditAdvisoryData.advisories).forEach((rawAdvisory) => { advisoryToVulnerabilities(rawAdvisory); }); summary = auditAdvisoryData.metadata; } else { const auditAdvisoryData = JSON.parse(line) as AuditAdvisoryData | AuditMetadataData; if (auditAdvisoryData.type === "auditAdvisory") { advisoryToVulnerabilities(auditAdvisoryData.data.advisory); } else if (auditAdvisoryData.type === "auditSummary") { summary = auditAdvisoryData.data; } } }); } } } catch (error) { bailWithError("Failed to parse YARN Audit JSON!", error as Error, options.fatalExitCode); } }); input.on("end", async function () { try { await generateReport(Array.from(vulnerabilities.values()), summary, options); } catch (error) { bailWithError( "Failed to generate report! Please report this issue to https://github.com/davityavryan/yarn-audit-html/issues", error as Error, options.fatalExitCode, ); } }); function advisoryToVulnerabilities(advisory: RawAuditAdvisor) { const newVulnerabilities = parseAdvisory(advisory); newVulnerabilities.forEach((newVulnerability) => { const { key } = newVulnerability; if (!vulnerabilities.has(key)) { vulnerabilities.set(key, newVulnerability); } }); } } /* istanbul ignore if */ if (process.env.NODE_ENV !== "test") { run(process.argv, process.stdin); } export { run };