yarn-audit-html
Version:
Generate a HTML report for Yarn Audit
117 lines (116 loc) • 4.22 kB
JavaScript
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';
async function run(argv, input) {
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 = {
vulnerabilities: { info: 0, low: 0, moderate: 0, high: 0, critical: 0 },
dependencies: 0,
devDependencies: 0,
optionalDependencies: 0,
totalDependencies: 0,
};
const options = program.opts();
const vulnerabilities = new Map();
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 () {
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);
Object.values(auditAdvisoryData.advisories).forEach((rawAdvisory) => {
advisoryToVulnerabilities(rawAdvisory);
});
summary = auditAdvisoryData.metadata;
}
else {
const auditAdvisoryData = JSON.parse(line);
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, 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, options.fatalExitCode);
}
});
function advisoryToVulnerabilities(advisory) {
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 };