better-npm-audit
Advanced tools
Comparing version 1.8.1 to 1.9.0
58
index.js
@@ -11,3 +11,4 @@ #!/usr/bin/env node | ||
const { isWholeNumber, mapLevelToNumber, getVulnerabilities, filterValidException } = require('./utils/common'); | ||
const { readFile } = require('./utils/file') | ||
const { readFile } = require('./utils/file'); | ||
const consoleUtil = require('./utils/console'); | ||
@@ -19,4 +20,6 @@ const EXCEPTION_FILE_PATH = '.nsprc'; | ||
const MAX_BUFFER_SIZE = 1024 * 1000 * 50; // 50 MB | ||
const SUCCESS_MESSAGE = '🤝 All good!'; | ||
const LOGS_EXCEEDED_MESSAGE = '[MAXIMUM EXCEEDED] Logs exceeded the maximum length limit. Add the flag `-f` to see the full audit logs.'; | ||
const RESPONSE_MESSAGE = { | ||
SUCCESS: '🤝 All good!', | ||
LOGS_EXCEEDED: '[MAXIMUM EXCEEDED] Logs exceeded the maximum length limit. Add the flag `-f` to see the full audit logs.', | ||
}; | ||
@@ -30,7 +33,8 @@ /** | ||
if (vulnerabilities.length > 0) { | ||
const message = `${vulnerabilities.length} vulnerabilities found. Node security advisories: ${vulnerabilities}`; | ||
throw new Error(message); | ||
consoleUtil.error(`${vulnerabilities.length} vulnerabilities found. Node security advisories: ${vulnerabilities}`); | ||
// Exit failed | ||
process.exit(1); | ||
} else { | ||
// Happy happy, joy joy | ||
console.info(SUCCESS_MESSAGE); | ||
consoleUtil.info(RESPONSE_MESSAGE.SUCCESS); | ||
} | ||
@@ -47,14 +51,14 @@ } | ||
if (fullLog) { | ||
console.info(data); | ||
consoleUtil.info(data); | ||
} else { | ||
const toDisplay = data.substring(0, maxLength); | ||
console.info(toDisplay); | ||
consoleUtil.info(toDisplay); | ||
// Display additional info if it is not the full message | ||
if (toDisplay.length < data.length) { | ||
console.info(''); | ||
console.info('...'); | ||
console.info(''); | ||
console.info(LOGS_EXCEEDED_MESSAGE); | ||
console.info(''); | ||
consoleUtil.info(''); | ||
consoleUtil.info('...'); | ||
consoleUtil.info(''); | ||
consoleUtil.info(RESPONSE_MESSAGE.LOGS_EXCEEDED); | ||
consoleUtil.info(''); | ||
} | ||
@@ -88,3 +92,4 @@ } | ||
* @param {Boolean} fullLog True if the full log should be displayed in the case of no vulerabilities | ||
*/ | ||
* @param {Array} exceptionIds List of vulernability IDs to ignore | ||
*/ | ||
function audit(auditCommand, auditLevel, fullLog, exceptionIds) { | ||
@@ -130,3 +135,2 @@ // Execute `npm audit` command to get the security report, taking into account | ||
} | ||
if (options && options.ignore) { | ||
@@ -136,15 +140,11 @@ const cmdExceptions = options.ignore.split(SEPARATOR).filter(isWholeNumber).map(Number); | ||
} | ||
if (Array.isArray(exceptionIds) && exceptionIds.length) { | ||
console.info('Exception vulnerabilities ID(s): ', exceptionIds); | ||
consoleUtil.info('Exception vulnerabilities ID(s): ', exceptionIds); | ||
} | ||
if (options && options.level) { | ||
auditLevel = mapLevelToNumber(options.level); | ||
} | ||
if (options && options.production) { | ||
auditCommand += ' --production'; | ||
} | ||
if (options && options.full) { | ||
@@ -160,9 +160,9 @@ fullLog = true; | ||
program | ||
.command('audit') | ||
.description('execute npm audit') | ||
.option('-i, --ignore <ids>', 'Vulnerabilities ID(s) to ignore') | ||
.option('-f, --full', `Display the full audit logs. Default to ${DEFAULT_MESSSAGE_LIMIT} characters.`) | ||
.option('-l, --level <auditLevel>', 'The minimum audit level to include') | ||
.option('-p, --production', 'Skip checking devDependencies') | ||
.action(userOptions => handleUserInput(userOptions, audit)); | ||
.command('audit') | ||
.description('execute npm audit') | ||
.option('-i, --ignore <ids>', 'Vulnerabilities ID(s) to ignore') | ||
.option('-f, --full', `Display the full audit logs. Default to ${DEFAULT_MESSSAGE_LIMIT} characters.`) | ||
.option('-l, --level <auditLevel>', 'The minimum audit level to include') | ||
.option('-p, --production', 'Skip checking devDependencies') | ||
.action(userOptions => handleUserInput(userOptions, audit)); | ||
@@ -176,4 +176,4 @@ program.parse(process.argv); | ||
BASE_COMMAND, | ||
SUCCESS_MESSAGE, | ||
LOGS_EXCEEDED_MESSAGE, | ||
SUCCESS_MESSAGE: RESPONSE_MESSAGE.SUCCESS, | ||
LOGS_EXCEEDED_MESSAGE: RESPONSE_MESSAGE.LOGS_EXCEEDED, | ||
}; |
{ | ||
"name": "better-npm-audit", | ||
"version": "1.8.1", | ||
"version": "1.9.0", | ||
"author": "Jee Mok <jee.ict@hotmail.com>", | ||
@@ -40,6 +40,9 @@ "description": "Made to allow skipping certain vulnerabilities, and any extra handling that are not supported by the default npm audit in the future.", | ||
"scripts": { | ||
"test": "mocha test/index.js" | ||
"test": "mocha test/index.js", | ||
"lint": "eslint ." | ||
}, | ||
"devDependencies": { | ||
"chai": "^4.3.0", | ||
"eslint": "^7.25.0", | ||
"eslint-config-google": "^0.14.0", | ||
"mocha": "^8.3.0", | ||
@@ -46,0 +49,0 @@ "sinon": "^9.2.4" |
@@ -8,5 +8,32 @@ const sinon = require('sinon'); | ||
const V7_JSON_BUFFER_EMPTY = require('./__mocks__/v7-json-buffer-empty.json'); | ||
const consoleUtil = require('../utils/console'); | ||
const { isWholeNumber, mapLevelToNumber, getVulnerabilities, isJsonString, filterValidException } = require('../utils/common'); | ||
const { handleLogDisplay, handleFinish, handleUserInput, BASE_COMMAND, SUCCESS_MESSAGE, LOGS_EXCEEDED_MESSAGE } = require('../index'); | ||
const { FG_WHITE, BG_BLACK, RESET_COLOR } = consoleUtil; | ||
describe('console utils', () => { | ||
it('should wrap error console message with styling format correctly', () => { | ||
const stub = sinon.stub(console, 'error'); | ||
const message = 'console message'; | ||
expect(stub.called).to.equal(false); | ||
consoleUtil.error(message); | ||
expect(stub.called).to.equal(true); | ||
expect(stub.calledWith(`${FG_WHITE}${BG_BLACK}${message}${RESET_COLOR}`)).to.equal(true); | ||
}); | ||
it('should wrap error info message with styling format correctly', () => { | ||
const stub = sinon.stub(console, 'info'); | ||
const message = 'console message'; | ||
expect(stub.called).to.equal(false); | ||
consoleUtil.info(message); | ||
expect(stub.called).to.equal(true); | ||
expect(stub.calledWith(`${FG_WHITE}${message}${RESET_COLOR}`)).to.equal(true); | ||
}); | ||
}); | ||
describe('common utils', () => { | ||
@@ -32,3 +59,3 @@ it('should return true for valid JSON object', () => { | ||
expect(isWholeNumber(2920)).to.equal(true); | ||
expect(isWholeNumber(0934)).to.equal(true); | ||
expect(isWholeNumber(934)).to.equal(true); | ||
expect(isWholeNumber('0920')).to.equal(true); | ||
@@ -55,10 +82,10 @@ | ||
const exceptions = { | ||
137: { | ||
'137': { | ||
ignore: true, | ||
reason: 'Ignored since we dont use xxx method', | ||
}, | ||
581: { | ||
'581': { | ||
reason: 'Ignored since we dont use xxx method', | ||
}, | ||
980: 'Ignored since we dont use xxx method', | ||
'980': 'Ignored since we dont use xxx method', | ||
'invalid': 'Ignored since we dont use xxx method', | ||
@@ -72,11 +99,11 @@ }; | ||
const exceptions = { | ||
137: { | ||
'137': { | ||
ignore: true, | ||
expiry: 1615462130000, | ||
}, | ||
581: { | ||
'581': { | ||
ignore: true, | ||
expiry: 1615462140000, | ||
}, | ||
980: { | ||
'980': { | ||
ignore: true, | ||
@@ -88,3 +115,2 @@ expiry: 1615462150000, | ||
expect(filterValidException(exceptions)).to.deep.equal([]); | ||
let clock = sinon.stub(Date, 'now').returns(1615462140000); | ||
@@ -227,3 +253,3 @@ | ||
it('should be able to handle the success result properly', () => { | ||
const stub = sinon.stub(console, 'info'); | ||
const stub = sinon.stub(consoleUtil, 'info'); | ||
const vulnerabilities = []; | ||
@@ -239,8 +265,23 @@ | ||
it('should be able to handle the found vulnerabilities properly', () => { | ||
const stubProcess = sinon.stub(process, 'exit'); | ||
const stubConsole = sinon.stub(consoleUtil, 'error'); | ||
const vulnerabilities = [1165, 1890]; | ||
expect(() => handleFinish(vulnerabilities)).to.throw('2 vulnerabilities found. Node security advisories: 1165,1890'); | ||
expect(stubProcess.called).to.equal(false); | ||
expect(stubConsole.called).to.equal(false); | ||
handleFinish(vulnerabilities); | ||
expect(stubProcess.called).to.equal(true); | ||
expect(stubConsole.called).to.equal(true); | ||
expect(stubProcess.calledWith(1)).to.equal(true); | ||
expect(stubConsole.calledWith('2 vulnerabilities found. Node security advisories: 1165,1890')).to.equal(true); | ||
stubProcess.restore(); | ||
stubConsole.restore(); | ||
}); | ||
it('should be able to handle normal log display correctly', () => { | ||
const stub = sinon.stub(console, 'info'); | ||
const stub = sinon.stub(consoleUtil, 'info'); | ||
const data = '123456789'; | ||
@@ -258,3 +299,3 @@ const fullLog = true; | ||
it('should be able to handle overlength log display properly', () => { | ||
const stub = sinon.stub(console, 'info'); | ||
const stub = sinon.stub(consoleUtil, 'info'); | ||
const data = '123456789'; | ||
@@ -275,3 +316,3 @@ const fullLog = false; | ||
it('should be able to handle log display properly', () => { | ||
const stub = sinon.stub(console, 'info'); | ||
const stub = sinon.stub(consoleUtil, 'info'); | ||
const data = '123456789'; | ||
@@ -278,0 +319,0 @@ const fullLog = false; |
@@ -6,3 +6,3 @@ const get = require('lodash.get'); | ||
* @param {String} auditLevel The npm audit level | ||
* @return {Number} The numeric value, higher is more severe | ||
* @return {Number} Returns the numeric value, higher is more severe | ||
*/ | ||
@@ -31,2 +31,3 @@ function mapLevelToNumber(auditLevel) { | ||
* @param {Array} exceptionIds List of exception vulnerabilities | ||
* @return {Array} Returns the list of found vulnerabilities | ||
*/ | ||
@@ -42,5 +43,5 @@ function getVulnerabilities(jsonBuffer = '', auditLevel = 0, exceptionIds = []) { | ||
return Object.values(advisories) | ||
.filter(advisory => mapLevelToNumber(advisory.severity) >= auditLevel) // Filter out if there is requested audit level | ||
.map(advisory => advisory.id) // Map out the vulnerabilities IDs | ||
.filter(id => !exceptionIds.includes(id)); // Filter out exceptions provided by user | ||
.filter(advisory => mapLevelToNumber(advisory.severity) >= auditLevel) // Filter out if there is requested audit level | ||
.map(advisory => advisory.id) // Map out the vulnerabilities IDs | ||
.filter(id => !exceptionIds.includes(id)); // Filter out exceptions provided by user | ||
} | ||
@@ -51,11 +52,11 @@ | ||
return Object.values(vulnerabilities) | ||
.filter(vulnerability => mapLevelToNumber(vulnerability.severity) >= auditLevel) // Filter out if there is requested audit level | ||
// Map out the vulnerabilities IDs | ||
.reduce((acc, vulnerability) => { | ||
// Its stored inside `via` array, but sometimes it might be a String | ||
const cleanedArray = get(vulnerability, 'via', []).map(each => get(each, 'source')).filter(Boolean); | ||
// Compile into a single array | ||
return acc.concat(cleanedArray); | ||
}, []) | ||
.filter(id => !exceptionIds.includes(id)); // Filter out exceptions provided by user | ||
.filter(vulnerability => mapLevelToNumber(vulnerability.severity) >= auditLevel) // Filter out if there is requested audit level | ||
// Map out the vulnerabilities IDs | ||
.reduce((acc, vulnerability) => { | ||
// Its stored inside `via` array, but sometimes it might be a String | ||
const cleanedArray = get(vulnerability, 'via', []).map(each => get(each, 'source')).filter(Boolean); | ||
// Compile into a single array | ||
return acc.concat(cleanedArray); | ||
}, []) | ||
.filter(id => !exceptionIds.includes(id)); // Filter out exceptions provided by user | ||
} | ||
@@ -69,3 +70,3 @@ | ||
* @param {Object} fileException The exception object | ||
* @return {Array} The list of found | ||
* @return {Array} Returns the list of found vulnerabilities | ||
*/ | ||
@@ -86,3 +87,3 @@ function filterValidException(fileException) { | ||
} | ||
// `ignore` flag has to be true | ||
// `ignore` flag has to be true | ||
if (!details.ignore) { | ||
@@ -105,2 +106,6 @@ return acc; | ||
/** | ||
* @param {Any} value The input number | ||
* @return {Boolean} Returns true if the input is a whole number | ||
*/ | ||
function isWholeNumber(value) { | ||
@@ -113,2 +118,6 @@ if (!Number(value)) { | ||
/** | ||
* @param {String} string The JSON stringified object | ||
* @return {Boolean} Returns true if the input string is parse-able | ||
*/ | ||
function isJsonString(string) { | ||
@@ -121,3 +130,3 @@ try { | ||
return true; | ||
}; | ||
} | ||
@@ -124,0 +133,0 @@ module.exports = { |
const fs = require('fs'); | ||
const { isJsonString } = require('./common'); | ||
/** | ||
* @param {String} path The file path | ||
* @return {(Object|Boolean)} Returns the parsed data if found, or else returns `false` | ||
*/ | ||
function readFile(path) { | ||
@@ -5,0 +9,0 @@ try { |
Sorry, the diff of this file is not supported yet
95278
16
2175
5