npm-audit-helper
Version:
Helps you understand your npm audit findings so they're not too overwhelming
667 lines (652 loc) • 15.7 kB
JavaScript
const test = require('tape');
const Help = require('../lib/help-audit');
const help = Help();
const exampleAuditJson = {
vulnerabilities: {
joi: {
name: 'joi',
severity: 'moderate',
via: [
'hoek',
],
effects: [],
range: '0.0.2 - 8.0.5',
nodes: [
'node_modules/joi',
],
fixAvailable: {
name: 'joi',
version: '17.3.0',
isSemVerMajor: true,
},
},
lodash: {
name: 'lodash',
severity: 'high',
via: [
{
source: 1065,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1065',
severity: 'high',
range: '<4.17.12',
},
{
source: 1523,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1523',
severity: 'low',
range: '<4.17.19',
},
{
source: 577,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/577',
severity: 'low',
range: '<4.17.5',
},
{
source: 782,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/782',
severity: 'high',
range: '<4.17.11',
},
],
effects: [],
range: '<=4.17.18',
nodes: [
'node_modules/lodash',
],
fixAvailable: {
name: 'lodash',
version: '4.17.20',
isSemVerMajor: true,
},
},
},
};
const buildAuditResultFixture = (vulnerabilities, metadata = {}) => ({ vulnerabilities, metadata });
test('should filter output by severity', (t) => {
const input = { ...exampleAuditJson };
const { auditResult } = help(input);
t.equal(Object.keys(auditResult.vulnerabilities).length, 1);
t.ok(auditResult.vulnerabilities.lodash);
t.end();
});
test('should not include vulnerabilities that can be fixed with npm audit fix', (t) => {
const input = buildAuditResultFixture({
minimist: {
name: 'minimist',
severity: 'low',
via: [
{
source: 1179,
name: 'minimist',
dependency: 'minimist',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1179',
severity: 'low',
range: '<0.2.1 || >=1.0.0 <1.2.3',
},
],
effects: [
'optimist',
],
range: '<0.2.1 || >=1.0.0 <1.2.3',
nodes: [
'node_modules/minimist',
],
fixAvailable: true,
},
optimist: {
name: 'optimist',
severity: 'low',
via: [
'minimist',
],
effects: [
'handlebars',
],
range: '>=0.6.0',
nodes: [
'node_modules/optimist',
],
fixAvailable: true,
},
joi: {
name: 'joi',
severity: 'low',
via: [
'hoek',
],
effects: [],
range: '0.0.2 - 8.0.5',
nodes: [
'node_modules/joi',
],
fixAvailable: {
name: 'joi',
version: '17.3.0',
isSemVerMajor: true,
},
},
});
const { auditResult } = help(input);
t.equal(Object.keys(auditResult.vulnerabilities).length, 1);
t.notOk(auditResult.vulnerabilities.optimist);
t.notOk(auditResult.vulnerabilities.minimist);
t.ok(auditResult.vulnerabilities.joi);
t.end();
});
test('should move to the next severity if the highest severity has no actions for review', (t) => {
const input = buildAuditResultFixture({
minimist: {
name: 'minimist',
severity: 'high',
via: [
{
source: 1179,
name: 'minimist',
dependency: 'minimist',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1179',
severity: 'high',
range: '<0.2.1 || >=1.0.0 <1.2.3',
},
],
effects: [
'optimist',
],
range: '<0.2.1 || >=1.0.0 <1.2.3',
nodes: [
'node_modules/minimist',
],
fixAvailable: true,
},
optimist: {
name: 'optimist',
severity: 'high',
via: [
'minimist',
],
effects: [
'handlebars',
],
range: '>=0.6.0',
nodes: [
'node_modules/optimist',
],
fixAvailable: true,
},
joi: {
name: 'joi',
severity: 'low',
via: [
'hoek',
],
effects: [],
range: '0.0.2 - 8.0.5',
nodes: [
'node_modules/joi',
],
fixAvailable: {
name: 'joi',
version: '17.3.0',
isSemVerMajor: true,
},
},
});
const { auditResult } = help(input);
t.equal(Object.keys(auditResult.vulnerabilities).length, 1);
t.notOk(auditResult.vulnerabilities.optimist);
t.notOk(auditResult.vulnerabilities.minimist);
t.ok(auditResult.vulnerabilities.joi);
t.end();
});
test('should return a count of auto fixes', (t) => {
const input = buildAuditResultFixture({
minimist: {
name: 'minimist',
severity: 'high',
via: [
{
source: 1179,
name: 'minimist',
dependency: 'minimist',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1179',
severity: 'high',
range: '<0.2.1 || >=1.0.0 <1.2.3',
},
],
effects: [
'optimist',
],
range: '<0.2.1 || >=1.0.0 <1.2.3',
nodes: [
'node_modules/minimist',
],
fixAvailable: true,
},
optimist: {
name: 'optimist',
severity: 'high',
via: [
'minimist',
],
effects: [
'handlebars',
],
range: '>=0.6.0',
nodes: [
'node_modules/optimist',
],
fixAvailable: true,
},
joi: {
name: 'joi',
severity: 'low',
via: [
'hoek',
],
effects: [],
range: '0.0.2 - 8.0.5',
nodes: [
'node_modules/joi',
],
fixAvailable: {
name: 'joi',
version: '17.3.0',
isSemVerMajor: true,
},
},
});
const { autoFixCount } = help(input);
t.equal(autoFixCount, 2);
t.end();
});
test('should return the most problematic dependency as the one with the most vulnerabilities', (t) => {
const input = buildAuditResultFixture({
handlebars: {
name: 'handlebars',
severity: 'high',
via: [
{
source: 61,
name: 'handlebars',
dependency: 'handlebars',
title: 'Cross-Site Scripting',
url: 'https://npmjs.com/advisories/61',
severity: 'high',
range: '<4.0.0',
},
{
source: 755,
name: 'handlebars',
dependency: 'handlebars',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/755',
severity: 'high',
range: '<=4.0.13 || >=4.1.0 <4.1.2',
},
'optimist',
],
effects: [],
range: '<=4.7.3',
nodes: [
'node_modules/handlebars',
],
fixAvailable: {
name: 'handlebars',
version: '4.7.6',
isSemVerMajor: true,
},
},
lodash: {
name: 'lodash',
severity: 'high',
via: [
{
source: 1065,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1065',
severity: 'high',
range: '<4.17.12',
},
{
source: 1523,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1523',
severity: 'low',
range: '<4.17.19',
},
{
source: 577,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/577',
severity: 'low',
range: '<4.17.5',
},
{
source: 782,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/782',
severity: 'high',
range: '<4.17.11',
},
],
effects: [],
range: '<=4.17.18',
nodes: [
'node_modules/lodash',
],
fixAvailable: {
name: 'lodash',
version: '4.17.20',
isSemVerMajor: true,
},
},
minimist: {
name: 'minimist',
severity: 'low',
via: [
{
source: 1179,
name: 'minimist',
dependency: 'minimist',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1179',
severity: 'low',
range: '<0.2.1 || >=1.0.0 <1.2.3',
},
],
effects: [
'optimist',
],
range: '<0.2.1 || >=1.0.0 <1.2.3',
nodes: [
'node_modules/minimist',
],
fixAvailable: {
name: 'handlebars',
version: '4.7.6',
isSemVerMajor: true,
},
},
optimist: {
name: 'optimist',
severity: 'low',
via: [
'minimist',
],
effects: [
'handlebars',
],
range: '>=0.6.0',
nodes: [
'node_modules/handlebars/node_modules/optimist',
],
fixAvailable: {
name: 'handlebars',
version: '4.7.6',
isSemVerMajor: true,
},
},
});
const { mostProblematicDependency } = help(input);
t.equal(mostProblematicDependency.name, 'lodash');
t.equal(mostProblematicDependency.count, 4);
t.end();
});
test('should return the most problematic dependency as the one with the most vulnerabilities, irrespective of severity', (t) => {
const input = buildAuditResultFixture({
handlebars: {
name: 'handlebars',
severity: 'critical',
via: [
{
source: 61,
name: 'handlebars',
dependency: 'handlebars',
title: 'Cross-Site Scripting',
url: 'https://npmjs.com/advisories/61',
severity: 'critical',
range: '<4.0.0',
},
{
source: 755,
name: 'handlebars',
dependency: 'handlebars',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/755',
severity: 'critical',
range: '<=4.0.13 || >=4.1.0 <4.1.2',
},
'optimist',
],
effects: [],
range: '<=4.7.3',
nodes: [
'node_modules/handlebars',
],
fixAvailable: {
name: 'handlebars',
version: '4.7.6',
isSemVerMajor: true,
},
},
lodash: {
name: 'lodash',
severity: 'high',
via: [
{
source: 1065,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1065',
severity: 'high',
range: '<4.17.12',
},
{
source: 1523,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1523',
severity: 'low',
range: '<4.17.19',
},
{
source: 577,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/577',
severity: 'low',
range: '<4.17.5',
},
{
source: 782,
name: 'lodash',
dependency: 'lodash',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/782',
severity: 'high',
range: '<4.17.11',
},
],
effects: [],
range: '<=4.17.18',
nodes: [
'node_modules/lodash',
],
fixAvailable: {
name: 'lodash',
version: '4.17.20',
isSemVerMajor: true,
},
},
minimist: {
name: 'minimist',
severity: 'low',
via: [
{
source: 1179,
name: 'minimist',
dependency: 'minimist',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1179',
severity: 'low',
range: '<0.2.1 || >=1.0.0 <1.2.3',
},
],
effects: [
'optimist',
],
range: '<0.2.1 || >=1.0.0 <1.2.3',
nodes: [
'node_modules/minimist',
],
fixAvailable: {
name: 'handlebars',
version: '4.7.6',
isSemVerMajor: true,
},
},
optimist: {
name: 'optimist',
severity: 'low',
via: [
'minimist',
],
effects: [
'handlebars',
],
range: '>=0.6.0',
nodes: [
'node_modules/handlebars/node_modules/optimist',
],
fixAvailable: {
name: 'handlebars',
version: '4.7.6',
isSemVerMajor: true,
},
},
});
const { mostProblematicDependency } = help(input);
t.equal(mostProblematicDependency.name, 'lodash');
t.equal(mostProblematicDependency.count, 4);
t.end();
});
test('should return a zero exit code if no actions remain', (t) => {
const input = buildAuditResultFixture({});
const { exitCode } = help(input);
t.equal(exitCode, 0);
t.end();
});
test('should return a non-zero exit code if some vulnerabilities remain', (t) => {
const { exitCode } = help(exampleAuditJson);
t.equal(exitCode, 1);
t.end();
});
test('should return a zero exit code if requested', (t) => {
const helpWithZeroExit = Help({ 'exit-zero': true });
const { exitCode } = helpWithZeroExit(exampleAuditJson);
t.equal(exitCode, 0);
t.end();
});
test('should return the highest severity', (t) => {
const input = buildAuditResultFixture({
minimist: {
name: 'minimist',
severity: 'high',
via: [
{
source: 1179,
name: 'minimist',
dependency: 'minimist',
title: 'Prototype Pollution',
url: 'https://npmjs.com/advisories/1179',
severity: 'high',
range: '<0.2.1 || >=1.0.0 <1.2.3',
},
],
effects: [
'optimist',
],
range: '<0.2.1 || >=1.0.0 <1.2.3',
nodes: [
'node_modules/minimist',
],
fixAvailable: true,
},
optimist: {
name: 'optimist',
severity: 'high',
via: [
'minimist',
],
effects: [
'handlebars',
],
range: '>=0.6.0',
nodes: [
'node_modules/optimist',
],
fixAvailable: true,
},
joi: {
name: 'joi',
severity: 'low',
via: [
'hoek',
],
effects: [],
range: '0.0.2 - 8.0.5',
nodes: [
'node_modules/joi',
],
fixAvailable: {
name: 'joi',
version: '17.3.0',
isSemVerMajor: true,
},
},
});
const { highestSeverity } = help(input);
t.equal(highestSeverity, 'low');
t.end();
});
test('should include the metadata', (t) => {
const input = buildAuditResultFixture(exampleAuditJson, {
vulnerabilities: {
info: 0,
low: 7,
moderate: 3,
high: 2,
critical: 0,
total: 12,
},
});
const { auditResult: { metadata } } = help(input);
t.equal(metadata.vulnerabilities.total, 12);
t.end();
});