Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

better-npm-audit

Package Overview
Dependencies
Maintainers
1
Versions
70
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

better-npm-audit - npm Package Compare versions

Comparing version 3.3.0-rc.1 to 3.3.0

src/handlers/handleFinish.js

54

index.js

@@ -11,6 +11,4 @@ #!/usr/bin/env node

var handleInput_1 = __importDefault(require("./src/handlers/handleInput"));
var handleDisplay_1 = __importDefault(require("./src/handlers/handleDisplay"));
var handleFinish_1 = __importDefault(require("./src/handlers/handleFinish"));
var package_json_1 = __importDefault(require("./package.json"));
var handleAudit_1 = __importDefault(require("./src/handlers/handleAudit"));
var handleScanV7_1 = __importDefault(require("./src/handlers/handleScanV7"));
var MAX_BUFFER_SIZE = 1024 * 1000 * 50; // 50 MB

@@ -20,8 +18,7 @@ var program = new commander_1.Command();

* Run audit
* @param {String} auditCommand The NPM audit command to use (with flags)
* @param {Array} exceptionIds List of vulnerability IDs to exclude
* @param {Object} options Parsed command options
* @param {Boolean} shouldScanModules Flag if we should scan the node_modules
* @param {String} auditCommand The NPM audit command to use (with flags)
* @param {String} auditLevel The level of vulnerabilities we care about
* @param {Array} exceptionIds List of vulnerability IDs to exclude
*/
function callback(auditCommand, exceptionIds, options) {
function callback(auditCommand, auditLevel, exceptionIds) {
// Increase the default max buffer size (1 MB)

@@ -36,40 +33,3 @@ var audit = child_process_1.exec(auditCommand + " --json", { maxBuffer: MAX_BUFFER_SIZE });

if (audit.stderr) {
audit.stderr.on('close', function () {
// Analyze the npm audit response
var _a = handleAudit_1.default(jsonBuffer, exceptionIds, options), unhandledIds = _a.unhandledIds, vulnerabilityIds = _a.vulnerabilityIds, report = _a.report, scanModules = _a.scanModules, npmVersion = _a.npmVersion;
// Grab any un-filtered vulnerabilities at the appropriate level
var unusedExceptionIds = exceptionIds.filter(function (id) { return !vulnerabilityIds.includes(id); });
// Make additional round of scanning the internal modules.
// The reason to do this outside of the above was to keep it cleaner to manage the code
// as npm v7 audit does not provide information of the installed version, we have to
// retrieve that information from the `package.json` file, so we could check the
// dependent modules via `npm ls {module}@{version}` properly.
if (!options.scanModules) {
// Display report
handleDisplay_1.default(report, [], unhandledIds, unusedExceptionIds);
return;
}
// If unable to determine the npm version
if (npmVersion !== 6 && npmVersion !== 7) {
console.error('Unable to determine NPM version from the audit response');
// Exit failed
process.exit(1);
}
if (npmVersion === 6) {
// TODO: Add auto exclusion scanning
handleDisplay_1.default(report, [], unhandledIds, unusedExceptionIds);
}
if (npmVersion === 7) {
// Scanning dependent modules
handleScanV7_1.default(scanModules, report, unhandledIds, options, function (error, response) {
if (error) {
console.error(error);
// Exit failed
process.exit(1);
}
// Display report
handleDisplay_1.default(response.securityReport, response.scanReport, response.unhandledIds, unusedExceptionIds);
});
}
});
audit.stderr.on('close', function () { return handleFinish_1.default(jsonBuffer, auditLevel, exceptionIds); });
// stderr

@@ -88,5 +48,3 @@ audit.stderr.on('data', console.error);

.option('-r, --registry <url>', 'The npm registry url to use.')
.option('-s, --scan-modules [boolean]', 'Scan through reported modules for .nsprc file.', true)
.option('-d, --debug', 'Enable debug mode.')
.action(function (options) { return handleInput_1.default(options, callback); });
program.parse(process.argv);
{
"name": "better-npm-audit",
"version": "3.3.0-rc.1",
"version": "3.3.0",
"author": "Jee Mok <jee.ict@hotmail.com>",
"description": "Reshape a better npm audit for the community and encourage more people to include security audits into their process",
"description": "Reshape into a better npm audit for the community and encourage more people to include security audit into their process.",
"license": "MIT",

@@ -19,4 +19,3 @@ "main": "lib/index.js",

"scripts": {
"audit:only": "node . audit",
"audit": "npm run build && npm run audit:only",
"audit": "npm run build && node . audit",
"test": "mocha -r ts-node/register test/**/*.test.ts",

@@ -30,3 +29,3 @@ "lint": "eslint .",

"publish:live": "npm run build && npm publish ./lib --tag latest",
"publish:next": "npm run build && npm publish ./lib --tag next"
"publish:next": "npm run build && npm publish lib --tag next"
},

@@ -33,0 +32,0 @@ "dependencies": {

@@ -74,10 +74,8 @@ # Better NPM Audit

| Flag | Short | Default | Description |
| ---------------- | ----- | ------- | ------------------------------------------------------------------------------------------------- |
| `--exclude` | `-x` | | Exceptions or the vulnerabilities ID(s) to exclude |
| `--level` | `-l` | | The minimum audit level to validate; Same as the original `--audit-level` flag |
| `--production` | `-p` | | Skip checking the `devDependencies` |
| `--registry` | `-r` | | The npm registry url to use |
| `--scan-modules` | `-s` | `true` | Scan through reported modules for `.nsprc` file. Note: this feature currently only support NPM v7 |
| `--debug` | `-d` | | Debug mode |
| Flag | Short | Description |
| -------------- | ----- | ------------------------------------------------------------------------------ |
| `--exclude` | `-x` | Exceptions or the vulnerabilities ID(s) to exclude |
| `--level` | `-l` | The minimum audit level to validate; Same as the original `--audit-level` flag |
| `--production` | `-p` | Skip checking the `devDependencies` |
| `--registry` | `-r` | The npm registry url to use |

@@ -132,42 +130,2 @@ <br />

## Auto trust security model
If we trust a package author enough to install their package, then we also trust them to create an `.nsprc` file that covers all the (transitive) dependencies of that package, in the context of that package.
So if we are working on a project `A`, and we install a package `B` as a dependency, then we trust the author of `B` to decide whether `B` is affected by a vulnerability in its dependency `C`. I also trust the author of `B` to make decisions about the author of package `C`, so if `C` contains an `.nsprc` file with an exception about a vulnerability in its dependency, `D`, then we trust that exception because the author of `B` trusts it, and we trust him.
More generally, we can imagine a chain like this:
`A` -> `B` -> `C` -> `D` -> `E` -> `F`
where npm audit reports a vulnerability in `F`, but we are trusting the authors of `B`, `C`, `D`, and `E` to say whether that vulnerability is relevant in the context of their packages.
Extending the example above, then, if we have a tree like this:
```
A -> B -> C -> D -> E -> F
|
-> X -> Y -> Z -> F
```
then the author of package `A` (us), still needs to worry about a vulnerability in `F` due to the way it may be used by `X`, `Y`, and `Z`. Again, though, any of the authors of `X`, `Y`, or `Z` can include an `.nsprc` exception for the vulnerability in `F`, and we will trust their judgement (because we are installing `X`'s package, and he trusts `Y`'s code, etc.)
The auto excepted vulnerabilities will be labeled as "auto" in the report table:
<img src="./.README/auto_exclusion.png" alt="Demo of excluding vulnerabilities flagged by the module maintainers" />
You can turn this feature off by using the flag `--scan-modules=false`
Special shout out to [@EdwinTaylor](https://github.com/alertme-edwin) for his effort in making this possible.
> Note: This feature currently only support npm v7
### Debug mode
To inspect the module `.nsprc` file paths and details, use `--debug` flag to turn on debug mode:
<img src="./.README/debug_mode.png" alt="Debug mode showing all scan paths" />
<br />
## Changelog

@@ -174,0 +132,0 @@

"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {

@@ -22,3 +11,3 @@ return (mod && mod.__esModule) ? mod : { "default": mod };

/**
* Process and clean user's input
* Handle user's input
* @param {Object} options User's command options or flags

@@ -40,3 +29,2 @@ * @param {Function} fn The function to handle the inputs

var auditLevel = lodash_get_1.default(options, 'level', envVar) || 'info';
var parsedOptions = __assign(__assign({}, options), { level: auditLevel, scanModules: options.scanModules !== 'false' });
// Get the exceptions

@@ -46,4 +34,4 @@ var nsprc = file_1.readFile('.nsprc');

var exceptionIds = vulnerability_1.getExceptionsIds(nsprc, cmdExceptions);
fn(auditCommand, exceptionIds, parsedOptions);
fn(auditCommand, auditLevel, exceptionIds);
}
exports.default = handleInput;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cleanContent = exports.trimArray = exports.isJsonString = exports.isWholeNumber = void 0;
exports.shortenNodePath = exports.trimArray = exports.isJsonString = exports.isWholeNumber = void 0;
/**

@@ -49,17 +49,10 @@ * @param {String | Number | Null | Boolean} value The input number

exports.trimArray = trimArray;
// TODO: Add unit tests
/**
* Clean text from color formatting
* @param {String} string Input
* @return {String}
* Shorten node path (node_modules/nodemon/node_modules/chokidar/node_modules/fsevents) to (nodemon>chokidar>fsevents)
* @param {String} path Full node path
* @return {String} Shorten Path
*/
function cleanContent(string) {
var content = JSON.stringify(string);
// Remove the color codes
content = content.replace(/\\x1b\[\d{1,2}m/g, '');
content = content.replace(/\\u001b\[\d{1,2}m/g, '');
// Remove additional stringified "
content = content.replace(/"/g, '');
return content;
function shortenNodePath(path) {
return path.replace('node_modules/', '').replace(/\/node_modules\//g, '>');
}
exports.cleanContent = cleanContent;
exports.shortenNodePath = shortenNodePath;

@@ -11,9 +11,7 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.printScanReport = exports.printExceptionReport = exports.printSecurityReport = exports.getColumnWidth = void 0;
exports.printExceptionReport = exports.printSecurityReport = exports.getColumnWidth = void 0;
var lodash_get_1 = __importDefault(require("lodash.get"));
var table_1 = require("table");
var common_1 = require("./common");
var SECURITY_REPORT_HEADER = ['ID', 'Module', 'Title', 'Paths', 'Sev.', 'URL', 'Ex.'];
var EXCEPTION_REPORT_HEADER = ['ID', 'Status', 'Expiry', 'Notes'];
var SCAN_REPORT_HEADER = ['ID', 'Version', 'Node', 'Status', 'Expiry', 'Notes', '.nsprc'];
// TODO: Add unit tests

@@ -33,3 +31,7 @@ /**

var contentLength = tableData.reduce(function (max, cur) {
var content = common_1.cleanContent(lodash_get_1.default(cur, columnIndex, ''));
var content = JSON.stringify(lodash_get_1.default(cur, columnIndex, ''));
// Remove the color codes
content = content.replace(/\\x1b\[\d{1,2}m/g, '');
content = content.replace(/\\u001b\[\d{1,2}m/g, '');
content = content.replace(/"/g, '');
// Keep whichever number that is bigger

@@ -45,19 +47,7 @@ return content.length > max ? content.length : max;

/**
* Print the security report
* @param {Array} data Array of arrays
* @return {undefined} Returns void
* Print the security report in a table format
* @param {Array} data Array of arrays
* @return {undefined} Returns void
*/
function printSecurityReport(data) {
var columns = {
// "Title" column index
2: {
width: getColumnWidth(data, 2),
wrapWord: true,
},
// "Paths" column index
3: {
width: getColumnWidth(data, 3),
wrapWord: true,
},
};
var configs = {

@@ -69,3 +59,14 @@ singleLine: true,

},
columns: columns,
columns: {
// "Title" column index
2: {
width: getColumnWidth(data, 2),
wrapWord: true,
},
// "Paths" column index
3: {
width: getColumnWidth(data, 3),
wrapWord: true,
},
},
};

@@ -76,3 +77,3 @@ console.info(table_1.table(__spreadArray([SECURITY_REPORT_HEADER], data), configs));

/**
* Print the exception report
* Print the exception report in a table format
* @param {Array} data Array of arrays

@@ -92,35 +93,1 @@ * @return {undefined} Returns void

exports.printExceptionReport = printExceptionReport;
/**
* Print the scan report
* @param {Array} data Array of arrays
* @return {undefined} Returns void
*/
function printScanReport(data) {
var columns = {
// "Node" column index
2: {
width: getColumnWidth(data, 2, 40),
wrapWord: true,
},
// "Notes" column index
5: {
width: getColumnWidth(data, 5),
wrapWord: true,
},
// ".nsprc" column index
6: {
width: getColumnWidth(data, 6),
wrapWord: true,
},
};
var configs = {
singleLine: true,
header: {
alignment: 'center',
content: '=== auto exclusion scan report ===\n',
},
columns: columns,
};
console.info(table_1.table(__spreadArray([SCAN_REPORT_HEADER], data), configs));
}
exports.printScanReport = printScanReport;

@@ -11,3 +11,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.processExceptions = exports.getExceptionsIds = exports.processAuditJson = exports.mapLevelToNumber = exports.mapModuleDependencies = void 0;
exports.processExceptions = exports.getExceptionsIds = exports.processAuditJson = exports.mapLevelToNumber = void 0;
var lodash_get_1 = __importDefault(require("lodash.get"));

@@ -20,25 +20,2 @@ var common_1 = require("./common");

/**
* Mao out all dependencies path
* @param {Object} json JSON output of `npm ls --json` command
* @param {Array} excludeModules Modules to be excluded
* @return {Array} List of dependencies paths
*/
function mapModuleDependencies(json, excludeModules) {
if (!json.dependencies) {
return [];
}
return Object.values(json.dependencies).reduce(function (a, c) {
// Replace the full path to relative path, so we get path that starts with 'node_modules/...'
var relativePath = c.path.replace(/(.*)better-npm-audit\//, '');
// Exclude some modules
var shouldExclude = Array.isArray(excludeModules) && excludeModules.some(function (module) { return relativePath.endsWith(module); });
if (!shouldExclude) {
a.push(relativePath);
}
// Recursively mapping the inner dependencies
return a.concat(mapModuleDependencies(c, excludeModules));
}, []);
}
exports.mapModuleDependencies = mapModuleDependencies;
/**
* Converts an audit level to a numeric value

@@ -65,35 +42,13 @@ * @param {String} auditLevel Audit level

exports.mapLevelToNumber = mapLevelToNumber;
var constructV6TableRow = function (vul, isExcepted) {
// Record this vulnerability into the report, and highlight it using yellow color if it's new
return [
color_1.color(vul.id, isExcepted ? '' : 'yellow'),
color_1.color(vul.module_name, isExcepted ? '' : 'yellow'),
color_1.color(vul.title, isExcepted ? '' : 'yellow'),
color_1.color(common_1.trimArray(vul.findings.reduce(function (a, c) { return __spreadArray(__spreadArray([], a), c.paths); }, []), MAX_PATHS_SIZE).join('\n'), isExcepted ? '' : 'yellow'),
color_1.color(vul.severity, isExcepted ? '' : 'yellow', color_1.getSeverityBgColor(vul.severity)),
color_1.color(vul.url, isExcepted ? '' : 'yellow'),
isExcepted ? 'y' : color_1.color('n', 'yellow'),
];
};
var constructV7TableRow = function (vul, isExcepted, nodes) {
var id = lodash_get_1.default(vul, 'source', '');
// Record this vulnerability into the report, and highlight it using yellow color if it's new
return [
color_1.color(String(id), isExcepted ? '' : 'yellow'),
color_1.color(vul.name, isExcepted ? '' : 'yellow'),
color_1.color(vul.title, isExcepted ? '' : 'yellow'),
color_1.color(common_1.trimArray(nodes, MAX_PATHS_SIZE).join('\n'), isExcepted ? '' : 'yellow'),
color_1.color(vul.severity, isExcepted ? '' : 'yellow', color_1.getSeverityBgColor(vul.severity)),
color_1.color(vul.url, isExcepted ? '' : 'yellow'),
isExcepted ? 'y' : color_1.color('n', 'yellow'),
];
};
/**
* Analyze the audit JSON string
* @param {String} jsonBuffer NPM Audit JSON string buffer
* @param {Array} exceptionIds User's exception IDs
* @param {Object} options Parsed command options
* @return {Object} Processed vulnerabilities details
* Analyze the JSON string buffer
* @param {String} jsonBuffer NPM Audit JSON string buffer
* @param {String} auditLevel User's target audit level
* @param {Array} exceptionIds User's exception IDs
* @return {Object} Processed vulnerabilities details
*/
function processAuditJson(jsonBuffer, exceptionIds, options) {
function processAuditJson(jsonBuffer, auditLevel, exceptionIds) {
if (jsonBuffer === void 0) { jsonBuffer = ''; }
if (auditLevel === void 0) { auditLevel = 'info'; }
if (exceptionIds === void 0) { exceptionIds = []; }
if (!common_1.isJsonString(jsonBuffer)) {

@@ -104,9 +59,7 @@ return {

report: [],
scanModules: [],
failed: true,
};
}
// There is a difference in the audit JSON structure for NPM v6 and v7
// - NPM v6 uses `advisories`
// - NPM v7 uses `vulnerabilities`
// NPM v6 uses `advisories`
// NPM v7 uses `vulnerabilities`
// Refer to the `test/__mocks__` folder for some sample mockups

@@ -117,6 +70,14 @@ var _a = JSON.parse(jsonBuffer), advisories = _a.advisories, vulnerabilities = _a.vulnerabilities;

return Object.values(advisories).reduce(function (acc, cur) {
var shouldAudit = mapLevelToNumber(cur.severity) >= mapLevelToNumber(options.level);
var shouldAudit = mapLevelToNumber(cur.severity) >= mapLevelToNumber(auditLevel);
var isExcepted = exceptionIds.includes(Number(cur.id));
// Record this vulnerability into the report, and highlight it using yellow color if it's new
acc.report.push(constructV6TableRow(cur, isExcepted));
acc.report.push([
color_1.color(cur.id, isExcepted ? '' : 'yellow'),
color_1.color(cur.module_name, isExcepted ? '' : 'yellow'),
color_1.color(cur.title, isExcepted ? '' : 'yellow'),
color_1.color(common_1.trimArray(cur.findings.reduce(function (a, c) { return __spreadArray(__spreadArray([], a), c.paths); }, []), MAX_PATHS_SIZE).join('\n'), isExcepted ? '' : 'yellow'),
color_1.color(cur.severity, isExcepted ? '' : 'yellow', color_1.getSeverityBgColor(cur.severity)),
color_1.color(cur.url, isExcepted ? '' : 'yellow'),
isExcepted ? 'y' : color_1.color('n', 'yellow'),
]);
acc.vulnerabilityIds.push(Number(cur.id));

@@ -126,7 +87,2 @@ // Found unhandled vulnerabilities

acc.unhandledIds.push(Number(cur.id));
// TODO:
// Prepare later for scanning usage (only for unhandled vulnerabilities)
// if (options.scanModules) {
// acc.scanModules.push({ id: Number(cur.id) });
// }
}

@@ -138,4 +94,2 @@ return acc;

report: [],
scanModules: [],
npmVersion: 6,
});

@@ -154,6 +108,14 @@ }

}
// Checks if this reported vulnerability is within the target range
var shouldAudit = mapLevelToNumber(vul.severity) >= mapLevelToNumber(options.level);
var shouldAudit = mapLevelToNumber(vul.severity) >= mapLevelToNumber(auditLevel);
var isExcepted = exceptionIds.includes(id);
acc.report.push(constructV7TableRow(vul, isExcepted, lodash_get_1.default(cur, 'nodes', [])));
// Record this vulnerability into the report, and highlight it using yellow color if it's new
acc.report.push([
color_1.color(String(id), isExcepted ? '' : 'yellow'),
color_1.color(vul.name, isExcepted ? '' : 'yellow'),
color_1.color(vul.title, isExcepted ? '' : 'yellow'),
color_1.color(common_1.trimArray(lodash_get_1.default(cur, 'nodes', []).map(common_1.shortenNodePath), MAX_PATHS_SIZE).join('\n'), isExcepted ? '' : 'yellow'),
color_1.color(vul.severity, isExcepted ? '' : 'yellow', color_1.getSeverityBgColor(vul.severity)),
color_1.color(vul.url, isExcepted ? '' : 'yellow'),
isExcepted ? 'y' : color_1.color('n', 'yellow'),
]);
acc.vulnerabilityIds.push(id);

@@ -163,6 +125,2 @@ // Found unhandled vulnerabilities

acc.unhandledIds.push(id);
// Prepare later for scanning usage (only for unhandled vulnerabilities)
if (options.scanModules) {
acc.scanModules.push({ id: id, name: cur.name, nodes: cur.nodes });
}
}

@@ -175,4 +133,2 @@ });

report: [],
scanModules: [],
npmVersion: 7,
});

@@ -184,3 +140,2 @@ }

report: [],
scanModules: [],
failed: true,

@@ -187,0 +142,0 @@ };

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc