All files index.ts

100% Statements 103/103
100% Branches 17/17
88.88% Functions 8/9
100% Lines 103/103

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 1044x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 62x 62x 62x 76x 76x 76x 76x 75x 75x 75x 75x 75x 75x 76x 1x 1x 62x 62x 62x 75x 62x 62x 62x 62x 4x 4x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 1x 1x 3x 3x 1x 1x 2x 2x 2x 1x 1x 1x 1x 1x  
import { readFile, writeFile } from 'node:fs/promises';
import process from 'node:process';
 
import ejs from 'ejs';
import { marked } from 'marked';
 
import { AuditAdvisor, AuditMetadata, Options, RawAuditAdvisor, Severity, Vulnerabilities } from './types.js';
 
const bootstrapClassSeverityMap: Record<Severity, string> = {
    critical: 'danger',
    high: 'warning',
    moderate: 'info',
    low: 'primary',
    info: 'secondary',
};
const severitySortPriority = Object.keys(bootstrapClassSeverityMap);
 
export function parseAdvisory(advisory: RawAuditAdvisor) {
    const vulnerabilities: Vulnerabilities = {};
 
    advisory.findings.forEach((finding) => {
        const { version } = finding;
        const key = `${advisory.module_name}@${version}-${advisory.vulnerable_versions}-${advisory.created}.${advisory.cwe}`;
 
        if (!(key in vulnerabilities)) {
            vulnerabilities[key] = {
                ...advisory,
                key,
                version,
                paths: finding.paths,
            };
        } else {
            vulnerabilities[key].paths = [...vulnerabilities[key].paths, ...finding.paths];
        }
    });
 
    Object.entries(vulnerabilities).forEach(([key, vulnerability]) => {
        vulnerabilities[key].paths = Array.from(new Set(vulnerability.paths));
    });
 
    return Object.values(vulnerabilities);
}
 
export async function generateReport(vulnerabilities: AuditAdvisor[], summary: AuditMetadata, options: Options) {
    vulnerabilities.sort(
        (left, right) => severitySortPriority.indexOf(left.severity) - severitySortPriority.indexOf(right.severity)
    );
 
    const report = await renderReport(
        {
            reportDate: new Date(),
            vulnerabilities,
            theme: options.theme,
            summary: {
                vulnerabilities: Object.values(summary.vulnerabilities).reduce((sum, value) => sum + value, 0),
                totalDependencies: summary.totalDependencies,
            },
        },
        options.template
    );
 
    await writeReport(options.output, report);
 
    if (vulnerabilities.length > 0) {
        console.info(`Found ${vulnerabilities.length} vulnerabilities. Report is saved in "${options.output}"`);
 
        if (options.fatalExitCode) {
            return process.exit(1);
        }
    } else {
        console.info('Congrats!!! No vulnerabilities found.');
    }
 
    return process.exit(0);
}
 
export async function renderReport(data: ejs.Data, template: string) {
    const htmlTemplate = await readFile(template, 'utf8');
    const locale = Intl.DateTimeFormat().resolvedOptions().locale;
 
    return ejs.render(htmlTemplate, {
        data,
        formatNumber: (number: number) => new Intl.NumberFormat(locale).format(number),
        formatDate: (dateStr: string) =>
            new Intl.DateTimeFormat(locale, { dateStyle: 'long', timeStyle: 'long' }).format(new Date(dateStr)),
        severityClass: (severity: Severity) => bootstrapClassSeverityMap[severity],
        markdown: (code: string) => marked(code, { mangle: false, headerIds: false }),
    });
}
 
export async function writeReport(outputPath: string, report: string) {
    await writeFile(outputPath, report, { encoding: 'utf8' });
}
 
export function bailWithError(message: string, error: Error, isFatalExitCode: boolean) {
    console.error(`${message}\n`, error);
 
    if (isFatalExitCode) {
        return process.exit(1);
    }
 
    return process.exit(0);
}