Skip to content
This repository was archived by the owner on Feb 2, 2022. It is now read-only.

Commit 1569867

Browse files
authored
Merge pull request #73 from InfoSec812/Issue_72-_-npm_version_update_breaking_dev_modes
Issue 72: npm version update breaking dev dependency filter
2 parents b092424 + dfeae4a commit 1569867

File tree

5 files changed

+677
-13
lines changed

5 files changed

+677
-13
lines changed

lib/parse_args.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const options = [
7878
*/
7979
async function check_npm_version() {
8080
const { stdout } = await exec('npm --version');
81-
const [major] = stdout.trim().split(".")[0];
81+
const [major, minor, patch] = stdout.trim().split(".");
8282
const majorInt = parseInt(major);
8383
return (majorInt >= 6);
8484
}

lib/parser.js

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ function parse_audit_results(err, data, threshold, ignoreDev, jsonOutput = false
5151
} else {
5252
const advisories = Object.entries(data.advisories);
5353

54-
const flaggedDependencies = filter_advisories(advisories, ignoreDev, threshold, whitelist);
54+
let moduleInfo = {};
55+
if (data.hasOwnProperty('actions')) {
56+
moduleInfo = process_actions(data.actions);
57+
}
58+
59+
const flaggedDependencies = filter_advisories(advisories, ignoreDev, threshold, moduleInfo, whitelist);
5560

5661
// If `-j` or `--json` passed, return the json data with the appropriate filters applied
5762
if (jsonOutput) {
@@ -103,19 +108,21 @@ function parse_audit_results(err, data, threshold, ignoreDev, jsonOutput = false
103108
* @param {Object[]} advisories An array of Advisory objects returned from NPM Audit
104109
* @param {boolean} ignoreDev Should dev dependencies be ignored?
105110
* @param {number} threshold The severity threshold above which a vulnerability will not be ignored
111+
* @param {Object} moduleInfo A Key/Value Map of module name to dev/prod status
106112
* @param {string[]} whitelist A (possibly empty) list of modules/versions which should be ignored
107113
* @returns An array (possibly empty) of advisory objects
108114
*/
109-
function filter_advisories(advisories, ignoreDev, threshold, whitelist = []) {
110-
const filteredByThreshold = advisories.filter((advisory, idx) => {
111-
return (!(advisory[1].findings[0].dev && ignoreDev)); // Filter out Dev dependencies when indicated
115+
function filter_advisories(advisories, ignoreDev, threshold, moduleInfo = {}, whitelist = []) {
116+
const filteredByDev = advisories.filter((advisory, idx) => {
117+
const isDev = advisory[1].findings[0].dev || moduleInfo[advisory[1].module_name];
118+
return (!(isDev && ignoreDev)); // Filter out Dev dependencies when indicated
112119
});
113120

114-
const filteredByDev = filteredByThreshold.filter((advisory, idx) => {
121+
const filteredByThreshold = filteredByDev.filter((advisory, idx) => {
115122
return (validThresholds.indexOf(advisory[1].severity) >= threshold); // Filter out lower severities when indicated
116123
});
117124

118-
return filteredByDev.filter((advisory, idx) => {
125+
return filteredByThreshold.filter((advisory, idx) => {
119126
const moduleName = advisory[1].module_name;
120127
const moduleVersion = advisory[1].findings[0].version;
121128
for (let i = 0; i < whitelist.length; i++) {
@@ -132,7 +139,22 @@ function filter_advisories(advisories, ignoreDev, threshold, whitelist = []) {
132139
});
133140
}
134141

142+
/**
143+
* Parse the "Actions" section of the NPM Audit report and determine which modules are dev dependencies and which are not
144+
* @param {Object[]} actions An array/list of Action objects from NPM Audit
145+
*/
146+
function process_actions(actions) {
147+
let moduleInfo = {};
148+
actions.forEach(action => {
149+
const module_name = action.module;
150+
const is_dev_dependency = action.resolves.filter(path => path.dev).length > 0;
151+
moduleInfo[module_name] = is_dev_dependency;
152+
});
153+
return moduleInfo;
154+
}
155+
135156
module.exports = {
136157
parse_audit_results,
137-
filter_advisories: filter_advisories
158+
filter_advisories: filter_advisories,
159+
process_actions
138160
};

lib/parser.test.js

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
const JSONStream = require('JSONStream');
1818
const es = require('event-stream');
1919
const { readFileSync, createReadStream } = require('fs');
20-
const { parse_audit_results, filter_advisories } = require('./parser');
20+
const { parse_audit_results, filter_advisories, process_actions } = require('./parser');
2121

2222
const LOW_THRESHOLD = 0;
2323
const MOD_THRESHOLD = 1;
@@ -215,7 +215,7 @@ test('Validate whitelisting of https-proxy-agent:1.0.0', () => {
215215
const test_data = readFileSync('test_data/vue_js_app.json', 'utf8');
216216
const data = JSON.parse(test_data);
217217
const results = filter_advisories(Object.entries(data.advisories), true, HIGH_THRESHOLD, ['https-proxy-agent:1.0.0']);
218-
expect(results.length).toBe(0);
218+
expect(results.length).toBe(1);
219219
});
220220

221221
/*
@@ -225,7 +225,7 @@ test('Validate whitelisting of all versions of https-proxy-agent', () => {
225225
const test_data = readFileSync('test_data/vue_js_app.json', 'utf8');
226226
const data = JSON.parse(test_data);
227227
const results = filter_advisories(Object.entries(data.advisories), true, HIGH_THRESHOLD, ['https-proxy-agent']);
228-
expect(results.length).toBe(0);
228+
expect(results.length).toBe(1);
229229
});
230230

231231
/*
@@ -235,7 +235,7 @@ test('Validate whitelisting of all versions of https-proxy-agent using wildcard'
235235
const test_data = readFileSync('test_data/vue_js_app.json', 'utf8');
236236
const data = JSON.parse(test_data);
237237
const results = filter_advisories(Object.entries(data.advisories), true, HIGH_THRESHOLD, ['https-proxy-agent:*']);
238-
expect(results.length).toBe(0);
238+
expect(results.length).toBe(1);
239239
});
240240

241241
/*
@@ -278,6 +278,22 @@ test('Ensure that large JSON responses from NPM audit are handled properly', don
278278
}));
279279
});
280280

281+
test('Ensure that process_actions can extract module dev/prod status', () => {
282+
const test_data = readFileSync('test_data/stoplight-cryptiles.json', 'utf8');
283+
const data = JSON.parse(test_data);
284+
const moduleInfo = process_actions(data.actions);
285+
expect(moduleInfo['hoek']).toBeDefined();
286+
expect(moduleInfo['hoek']).toEqual(true);
287+
});
288+
289+
test('Ensure that dev dependencies do not cause problems when disabled', () => {
290+
const test_data = readFileSync('test_data/stoplight-cryptiles.json', 'utf8');
291+
const data = JSON.parse(test_data);
292+
const moduleInfo = process_actions(data.actions);
293+
const results = filter_advisories(Object.entries(data.advisories), true, HIGH_THRESHOLD, moduleInfo);
294+
expect(results.length).toBe(0);
295+
});
296+
281297
test('Ensure that errors communicating with the registry service are properly handled', done => {
282298
createReadStream('test_data/registry_error.json', 'utf8')
283299
.pipe(JSONStream.parse())

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "npm-audit-ci-wrapper",
3-
"version": "2.6.6",
3+
"version": "3.0.0",
44
"description": "A wrapper for 'npm audit' which can be configurable for use in a CI/CD tool like Jenkins",
55
"keywords": [
66
"npm",

0 commit comments

Comments
 (0)