All files cli.ts

93.63% Statements 147/157
82.35% Branches 14/17
100% Functions 2/2
93.63% Lines 147/157

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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 1581x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 1x 1x 1x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 48x 2x 2x 2x 2x 2x 2x 16x 2x 2x 2x 48x 46x 46x 46x 45x 46x 1x 1x 46x 3x 3x 3x 3x     3x 3x 3x 3x 3x 3x             3x 3x 3x 61x 61x 61x 73x 73x 73x 28x 28x 61x 61x 3x 1x 1x 1x     1x 1x  
#!/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 };