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

Commit

Permalink
Merge pull request #73 from InfoSec812/Issue_72-_-npm_version_update_…
Browse files Browse the repository at this point in the history
…breaking_dev_modes

Issue 72:  npm version update breaking dev dependency filter
  • Loading branch information
InfoSec812 authored Aug 28, 2020
2 parents b092424 + dfeae4a commit 1569867
Show file tree
Hide file tree
Showing 5 changed files with 677 additions and 13 deletions.
2 changes: 1 addition & 1 deletion lib/parse_args.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const options = [
*/
async function check_npm_version() {
const { stdout } = await exec('npm --version');
const [major] = stdout.trim().split(".")[0];
const [major, minor, patch] = stdout.trim().split(".");
const majorInt = parseInt(major);
return (majorInt >= 6);
}
Expand Down
36 changes: 29 additions & 7 deletions lib/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ function parse_audit_results(err, data, threshold, ignoreDev, jsonOutput = false
} else {
const advisories = Object.entries(data.advisories);

const flaggedDependencies = filter_advisories(advisories, ignoreDev, threshold, whitelist);
let moduleInfo = {};
if (data.hasOwnProperty('actions')) {
moduleInfo = process_actions(data.actions);
}

const flaggedDependencies = filter_advisories(advisories, ignoreDev, threshold, moduleInfo, whitelist);

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

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

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

/**
* Parse the "Actions" section of the NPM Audit report and determine which modules are dev dependencies and which are not
* @param {Object[]} actions An array/list of Action objects from NPM Audit
*/
function process_actions(actions) {
let moduleInfo = {};
actions.forEach(action => {
const module_name = action.module;
const is_dev_dependency = action.resolves.filter(path => path.dev).length > 0;
moduleInfo[module_name] = is_dev_dependency;
});
return moduleInfo;
}

module.exports = {
parse_audit_results,
filter_advisories: filter_advisories
filter_advisories: filter_advisories,
process_actions
};
24 changes: 20 additions & 4 deletions lib/parser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
const JSONStream = require('JSONStream');
const es = require('event-stream');
const { readFileSync, createReadStream } = require('fs');
const { parse_audit_results, filter_advisories } = require('./parser');
const { parse_audit_results, filter_advisories, process_actions } = require('./parser');

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

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

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

/*
Expand Down Expand Up @@ -278,6 +278,22 @@ test('Ensure that large JSON responses from NPM audit are handled properly', don
}));
});

test('Ensure that process_actions can extract module dev/prod status', () => {
const test_data = readFileSync('test_data/stoplight-cryptiles.json', 'utf8');
const data = JSON.parse(test_data);
const moduleInfo = process_actions(data.actions);
expect(moduleInfo['hoek']).toBeDefined();
expect(moduleInfo['hoek']).toEqual(true);
});

test('Ensure that dev dependencies do not cause problems when disabled', () => {
const test_data = readFileSync('test_data/stoplight-cryptiles.json', 'utf8');
const data = JSON.parse(test_data);
const moduleInfo = process_actions(data.actions);
const results = filter_advisories(Object.entries(data.advisories), true, HIGH_THRESHOLD, moduleInfo);
expect(results.length).toBe(0);
});

test('Ensure that errors communicating with the registry service are properly handled', done => {
createReadStream('test_data/registry_error.json', 'utf8')
.pipe(JSONStream.parse())
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "npm-audit-ci-wrapper",
"version": "2.6.6",
"version": "3.0.0",
"description": "A wrapper for 'npm audit' which can be configurable for use in a CI/CD tool like Jenkins",
"keywords": [
"npm",
Expand Down
Loading

0 comments on commit 1569867

Please sign in to comment.