UNPKG

4.38 kBJavaScriptView Raw
1/**
2 * Copyright 2020 F5 Networks, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 *
17 * Note: This is a light wrapper around "npm audit" to support features such as:
18 * - Whitelisting specific vulnerabilities (typically until fix is released in downstream package)
19 * - Providing options in package.json inside "auditProcessor" property
20 *
21 * Usage: node auditProcessor.js --help
22 */
23
24'use strict';
25
26const fs = require('fs');
27const path = require('path');
28const yargs = require('yargs'); // eslint-disable-line import/no-extraneous-dependencies
29
30const PACKAGE_JSON = path.join(process.cwd(), 'package.json');
31const AUDIT_REPORT = path.join(process.cwd(), '.auditReport.json');
32const DEFAULT_EXIT_CODE = 0;
33
34
35class AuditProcessor {
36 constructor() {
37 this.report = {};
38 this.vulnerabilities = [];
39 this.exitCode = DEFAULT_EXIT_CODE;
40 }
41
42 log(msg) { // eslint-disable-line class-methods-use-this
43 console.log(msg); // eslint-disable-line no-console
44 }
45
46 /**
47 * Load report - Loads "npm audit --json" output
48 *
49 * @returns {Void}
50 */
51 loadReport() {
52 if (!fs.existsSync(AUDIT_REPORT)) {
53 throw new Error('Please run "npm audit" first.');
54 }
55 this.report = JSON.parse(fs.readFileSync(AUDIT_REPORT, 'utf-8'));
56 }
57
58 /**
59 * Process report
60 *
61 * @param {Object} options - function options
62 * @param {Array} [options.whitelist] - array containing zero or more ID's to ignore
63 *
64 * @returns {Void}
65 */
66 processReport(options) {
67 options = options || {}; // eslint-disable-line no-param-reassign
68 const whitelist = options.whitelist || [];
69
70 // parse out vulnerabilities
71 this.report.actions.forEach((action) => {
72 action.resolves.forEach((item) => {
73 this.vulnerabilities.push({
74 module: action.module,
75 path: item.path,
76 vulnerability: {
77 id: item.id,
78 url: this.report.advisories[item.id].url,
79 recommendation: this.report.advisories[item.id].recommendation
80 }
81 });
82 });
83 });
84 // determine if any vulnerabilities should be ignored
85 if (whitelist.length) {
86 this.vulnerabilities = this.vulnerabilities.filter(vuln =>
87 !whitelist.includes(vuln.vulnerability.id)); // eslint-disable-line arrow-body-style
88 }
89 }
90
91 /**
92 * Notify - Determine exit code, what should be logged
93 *
94 * @returns {Void}
95 */
96 notify() {
97 // check for vulnerabilities and act accordingly
98 if (this.vulnerabilities.length) {
99 this.log(this.vulnerabilities);
100 this.log(`IMPORTANT: ${this.vulnerabilities.length} vulnerabilities exist, please resolve them!`);
101 process.exit(1);
102 }
103 // good to go
104 this.log('No dependency vulnerabilities exist!');
105 process.exit(this.exitCode);
106 }
107}
108
109function main() {
110 const argv = yargs
111 .version('1.0.0')
112 .command('whitelist', 'Whitelist specific vulnerabilities by ID')
113 .example('$0 --whitelist 1234,1235', 'Whitelist vulnerabilities 1234 and 1235')
114 .help('help')
115 .argv;
116
117 const optionsFromConfig = JSON.parse(fs.readFileSync(PACKAGE_JSON, 'utf-8')).auditProcessor;
118 const parsedArgs = {
119 whitelist: argv.whitelist || optionsFromConfig.whitelist || ''
120 };
121
122 const auditProcessor = new AuditProcessor();
123 auditProcessor.loadReport();
124 auditProcessor.processReport({
125 whitelist: parsedArgs.whitelist.toString()
126 .split(',')
127 .map(item => parseInt(item, 10)) // eslint-disable-line arrow-body-style
128 });
129 auditProcessor.notify();
130}
131
132main();