@whittjs/better-npm-audit 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # Better NPM Audit
2
+
3
+ The goal of this project is to provide additional features on top of the existing npm audit options. We hope to encourage more people to do security audits for their projects.
4
+
5
+ [![NPM](https://nodei.co/npm/better-npm-audit.png)](https://npmjs.org/package/better-npm-audit)
6
+
7
+ ![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square) ![npm downloads](https://img.shields.io/npm/d18m/better-npm-audit) ![node current](https://img.shields.io/node/v/better-npm-audit) ![node support](https://img.shields.io/badge/node-up_to_v22-brightgreen) ![npm vulnerability](https://snyk.io/test/github/jeemok/better-npm-audit/badge.svg?targetFile=package.json) ![GitHub issues](https://img.shields.io/github/issues/jeemok/better-npm-audit?style=flat-square) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/better-npm-audit?style=flat-square) ![Languages](https://img.shields.io/github/languages/top/jeemok/better-npm-audit?style=flat-square)
8
+
9
+ ## Looking for Collaborators
10
+
11
+ Hi there! đź‘‹
12
+
13
+ I’m currently looking for collaborators to help maintain and develop this project. Due to time constraints, I haven’t been able to give it the attention it deserves, but I believe it has great potential to grow with the help of passionate contributors.
14
+
15
+ Become a Collaborator
16
+ If you’re interested in contributing on a regular basis, I’d love to have you on board as a collaborator. Whether you’re interested in fixing bugs, adding new features, or improving documentation, your contributions will be highly valued.
17
+
18
+ As a collaborator, you’ll have push access to the repository and play a key role in shaping the future of the project. If this sounds like something you’d be interested in, please reach out! You can open an issue titled “Interested in Collaborating” or contact me directly via email.
19
+
20
+ Let’s work together to make this project even better!
21
+
22
+ ## NPM version 6 and 7, and 8
23
+
24
+ NPM has upgraded to version 7 in late 2020 and has breaking changes on the `npm audit`. The output of npm audit has significantly changed both in the human-readable and `--json` output styles. Even more unfortunately, when NPM changed the JSON output in npm v7, they removed many of the other useful identifiers (`cves`, `cwe`, `github_advisory_id`) and the only thing left is the URL. We are trying our best to handle each version and provide consistent functionality to all of them. Related docs on v6 and v7 changes:
25
+
26
+ | Docs | Link |
27
+ | -------------------------- | ------------------------------------------------------------------------------------------ |
28
+ | NPM v6 & v7 changes | https://github.blog/2020-10-13-presenting-v7-0-0-of-the-npm-cli/ |
29
+ | NPM v7 blog post | https://blog.npmjs.org/post/626173315965468672/npm-v7-series-beta-release-and-semver-major |
30
+ | Official NPM v6 audit docs | https://docs.npmjs.com/cli/v6/commands/npm-audit |
31
+ | Official NPM v7 audit docs | https://docs.npmjs.com/cli/v7/commands/npm-audit |
32
+ | Dealing with new npm audit | https://uko.codes/dealing-with-npm-v7-audit-changes |
33
+
34
+ You may find the sample JSON outputs for each NPM versions in our codebase: [v6](https://github.com/jeemok/better-npm-audit/blob/master/test/__mocks__/v6-json-buffer.json), [v7](https://github.com/jeemok/better-npm-audit/blob/master/test/__mocks__/v7-json-buffer.json) & [v8](https://github.com/jeemok/better-npm-audit/blob/master/test/__mocks__/v8-json-buffer.json).
35
+
36
+ <br />
37
+
38
+ ## Installation
39
+
40
+ $ npm install --save better-npm-audit
41
+
42
+ or
43
+
44
+ $ npm install -g better-npm-audit
45
+
46
+ <br />
47
+
48
+ ## Usage
49
+
50
+ ### Run global
51
+
52
+ ```bash
53
+ better-npm-audit audit
54
+ ```
55
+
56
+ ### Run with exceptions
57
+
58
+ <img src="./.README/all_good.png" alt="Demo of table displaying the security report" />
59
+
60
+ Unhandled or newly reported vulnerabilities will be highlighted:
61
+
62
+ <img src="./.README/highlighted_exceptions.png" alt="Demo of table displaying the security report" />
63
+
64
+ Unused exceptions will be notified:
65
+
66
+ <img src="./.README/unused_exception.png" alt="Demo of displaying the unused exception" />
67
+
68
+ ### Add into package scripts
69
+
70
+ ```JSON
71
+ {
72
+ "scripts": {
73
+ "prepush": "npm run test && npm run audit",
74
+ "audit": "better-npm-audit audit"
75
+ }
76
+ }
77
+ ```
78
+
79
+ Now you can run locally or in your CI pipeline:
80
+
81
+ ```bash
82
+ npm run audit
83
+ ```
84
+
85
+ ### Filter vulnerability table
86
+
87
+ You can filter the vulnerability table to show only vulnerabilities at or above a specified severity level using the `--filter-table` flag. This is useful for reducing noise in the output while maintaining the original audit behavior for exit codes.
88
+
89
+ ```bash
90
+ # Filter table to show only high and critical vulnerabilities
91
+ better-npm-audit audit --filter-table high
92
+
93
+ # Filter table to match the audit level
94
+ better-npm-audit audit --level moderate --filter-table
95
+
96
+ # Set different levels for exit behavior vs table display
97
+ better-npm-audit audit --level high --filter-table moderate
98
+ ```
99
+
100
+ **Note:** The `--filter-table` flag only affects what vulnerabilities are displayed in the table. The audit level (`--level`) still controls the exit behavior and vulnerability counting.
101
+
102
+ <br />
103
+
104
+ ## Options
105
+
106
+ | Flag | Short | Description |
107
+ | ------------------- | ----- | ----------------------------------------------------------------------------------------------------- |
108
+ | `--exclude` | `-x` | Exceptions or the vulnerabilities ID(s) to exclude; the ID can be the numeric ID, CVE, CWE or GHSA ID |
109
+ | `--module-ignore` | `-m` | Names of modules to exclude |
110
+ | `--level` | `-l` | The minimum audit level to validate; Same as the original `--audit-level` flag |
111
+ | `--filter-table` | `-f` | Filter the vulnerability table to show only vulnerabilities at or above the specified level. Accepts a level (`info`, `low`, `moderate`, `high`, `critical`) or can be used as a boolean flag to filter by the audit level |
112
+ | `--production` | `-p` | Skip the `devDependencies` |
113
+ | `--registry` | `-r` | The npm registry url to use |
114
+ | `--include-columns` | `-i` | Columns to include in report. Available columns: `ID`, `Module`, `Title`, `Paths`, `Severity`, `URL`, `Ex.`, `Fix` (e.g. `-i Module,Title,Severity`) |
115
+
116
+ <br />
117
+
118
+ ## Environment Variables
119
+
120
+ | Variable | Description |
121
+ | ------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
122
+ | `NO_COLOR` | Support the [no-color standard](https://no-color.org/) to allow users use the tool without colored output. |
123
+ | `NPM_CONFIG_AUDIT_LEVEL` | Used in setting the audit level. <br /> _Note: this will be disregard if the audit level flag is passed onto the command._ |
124
+
125
+ <br />
126
+
127
+ ## Using `.nsprc` file to manage exceptions
128
+
129
+ You may add a file `.nsprc` to your project root directory to manage the exceptions. For example:
130
+
131
+ ```json
132
+ {
133
+ "1337": {
134
+ "active": true,
135
+ "notes": "Ignored since we don't use xxx method",
136
+ "expiry": 1615462134681
137
+ },
138
+ "4501": {
139
+ "active": false,
140
+ "notes": "Ignored since we don't use xxx method"
141
+ },
142
+ "CWE-471": "CWE ID is acceptable",
143
+ "GHSA-ww39-953v-wcq6": "GHSA ID is acceptable",
144
+ "https://npmjs.com/advisories/1213": "Full or partial URL is acceptable too"
145
+ }
146
+ ```
147
+
148
+ ### Fields
149
+
150
+ | Attribute | Type | Description | Default | Examples |
151
+ | --------- | ---------------- | --------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
152
+ | `active` | Boolean | If the tool should use it for exception | `true` | `true` |
153
+ | `expiry` | String \| Number | Human-readable date, or milliseconds since the UNIX Epoch | | - `'2020-01-31'` <br> - `'2020/01/31'` <br> - `'01/31/2021, 11:03:58'` <br> - `'1 March 2016 15:00'` <br> - `'1 March 2016 3:00 pm'` <br> - `'2012-01-26T13:51:50.417-07:00'` <br> - `'Sun, 11 Jul 2021 03:03:13 GMT'` <br> - `'Thu Jan 26 2017 11:00:00 GMT+1100 (Australian Eastern Daylight Time)'` <br> - `327611110417` |
154
+ | `notes` | String | Notes related to the vulnerability. | |
155
+
156
+ <br />
157
+
158
+ When using a `.nsprc` file, a report will be displayed when it starts running:
159
+
160
+ <img src="./.README/exceptions_table.png" alt="Demo of table displaying a list of exceptions" />
161
+
162
+ > Note: the expiry date will be styled in yellow and red color if it is detected more than one or five years ago.
163
+
164
+ <br />
165
+
166
+ ## Changelog
167
+
168
+ You can find the changelog [here](https://github.com/jeemok/better-npm-audit/blob/master/CHANGELOG.md).
169
+
170
+ <br />
171
+
172
+ ## Contributors
173
+
174
+ [Ian Wright](https://github.com/IPWright83), [Edwin Taylor](https://github.com/alertme-edwin), [Maarten Hus](https://github.com/MrHus), [Alex Burkowsky](https://github.com/alexburkowskypolysign), [David M. Lee](https://github.com/leedm777), [Kyle Clark](https://github.com/kyle-clark1824), [Guillermo Pincay](https://github.com/guillermaster), [Grzegorz Pawłowski](https://github.com/GrzesiekP), [CSLTech](https://github.com/CSLTech), [Paul Clarkin](https://github.com/paulclarkin), [mgdodge](https://github.com/mgdodge), [Ricky Sullivan](https://github.com/rickysullivan), [Sam Gregory](https://github.com/samgregory88), [Tristan WAGNER](https://github.com/tristanwagner), [Zak](https://github.com/ZedLove), [Eric Cornelissen](https://github.com/ericcornelissen), [Gaurav Chinavle](https://github.com/GauravChinavle)
175
+
176
+ <br />
177
+
178
+ ---
179
+
180
+ If you like this project,
181
+
182
+ <a href="https://www.buymeacoffee.com/jeemok" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
package/index.js ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.callback = void 0;
8
+ var commander_1 = require("commander");
9
+ var child_process_1 = require("child_process");
10
+ var handleInput_1 = __importDefault(require("./src/handlers/handleInput"));
11
+ var handleFinish_1 = __importDefault(require("./src/handlers/handleFinish"));
12
+ var package_json_1 = __importDefault(require("./package.json"));
13
+ var MAX_BUFFER_SIZE = 1024 * 1000 * 50; // 50 MB
14
+ var program = new commander_1.Command();
15
+ /**
16
+ * Run audit
17
+ * @param {String} auditCommand The NPM audit command to use (with flags)
18
+ * @param {String} auditLevel The level of vulnerabilities we care about
19
+ * @param {Array} exceptionIds List of vulnerability IDs to exclude
20
+ * @param {Array} modulesToIgnore List of vulnerable modules to ignore in audit results
21
+ * @param {Array} columnsToInclude List of columns to include in audit results
22
+ * @param {String} filterLevel Optional level to filter table display
23
+ */
24
+ function callback(auditCommand, auditLevel, exceptionIds, modulesToIgnore, columnsToInclude, filterLevel) {
25
+ // Increase the default max buffer size (1 MB)
26
+ var audit = (0, child_process_1.exec)("".concat(auditCommand, " --json"), { maxBuffer: MAX_BUFFER_SIZE });
27
+ // Grab the data in chunks and buffer it as we're unable to parse JSON straight from stdout
28
+ var jsonBuffer = '';
29
+ if (audit.stdout) {
30
+ audit.stdout.on('data', function (data) { return (jsonBuffer += data); });
31
+ }
32
+ // Once the stdout has completed, process the output
33
+ if (audit.stderr) {
34
+ audit.stderr.on('close', function () { return (0, handleFinish_1.default)(jsonBuffer, auditLevel, exceptionIds, modulesToIgnore, columnsToInclude, filterLevel); });
35
+ // stderr
36
+ audit.stderr.on('data', console.error);
37
+ }
38
+ }
39
+ exports.callback = callback;
40
+ program.name(package_json_1.default.name).version(package_json_1.default.version);
41
+ program
42
+ .command('audit')
43
+ .description('execute npm audit')
44
+ .option('-x, --exclude <ids>', 'Exceptions or the vulnerabilities ID(s) to exclude.')
45
+ .option('-m, --module-ignore <moduleNames>', 'Names of modules to ignore.')
46
+ .option('-l, --level <auditLevel>', 'The minimum audit level to validate.')
47
+ .option('-f, --filter-table [level]', 'Filter table to show only vulnerabilities at or above specified level (defaults to audit level if no value provided).')
48
+ .option('-p, --production', 'Skip checking the devDependencies.')
49
+ .option('-r, --registry <url>', 'The npm registry url to use.')
50
+ .option('-i, --include-columns <columnName1>,<columnName2>,..,<columnNameN>', 'Columns to include in report.')
51
+ .action(function (options) { return (0, handleInput_1.default)(options, callback); });
52
+ program.parse(process.argv);
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "@whittjs/better-npm-audit",
3
+ "version": "3.12.0",
4
+ "author": "Jee Mok <jee.ict@hotmail.com>",
5
+ "description": "Reshape into a better npm audit for the community and encourage more people to include security audit into their process.",
6
+ "license": "MIT",
7
+ "main": "index.js",
8
+ "bin": {
9
+ "better-npm-audit": "index.js"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/WhittJS/better-npm-audit"
14
+ },
15
+ "engines": {
16
+ "node": ">= 8.12"
17
+ },
18
+ "scripts": {
19
+ "preaudit": "npm run build",
20
+ "audit": "node lib audit -x 1064843,1067245",
21
+ "test": "mocha -r ts-node/register test/**/*.test.ts",
22
+ "lint": "eslint .",
23
+ "qc": "npm run test && npm run lint",
24
+ "clean": "rimraf lib",
25
+ "prebuild": "npm run qc && npm run clean",
26
+ "build": "tsc",
27
+ "postbuild": "cp README.md lib && chmod +x ./lib/index.js",
28
+ "publish:live": "npm run build && npm publish ./lib --tag latest",
29
+ "publish:next": "npm run build && npm publish ./lib --tag next"
30
+ },
31
+ "dependencies": {
32
+ "commander": "^8.0.0",
33
+ "dayjs": "^1.10.6",
34
+ "lodash.get": "^4.4.2",
35
+ "semver": "^7.6.3",
36
+ "table": "^6.7.1"
37
+ },
38
+ "devDependencies": {
39
+ "@types/chai": "^4.2.19",
40
+ "@types/lodash.get": "^4.4.6",
41
+ "@types/mocha": "^8.2.3",
42
+ "@types/node": "^16.0.0",
43
+ "@types/semver": "^7.5.8",
44
+ "@types/sinon": "^10.0.2",
45
+ "@typescript-eslint/eslint-plugin": "^4.28.2",
46
+ "@typescript-eslint/parser": "^4.28.2",
47
+ "chai": "^4.3.0",
48
+ "eslint": "^7.25.0",
49
+ "eslint-config-google": "^0.14.0",
50
+ "eslint-config-prettier": "^8.3.0",
51
+ "eslint-plugin-prettier": "^3.4.0",
52
+ "mocha": "^10.7.3",
53
+ "prettier": "2.3.2",
54
+ "rimraf": "^3.0.2",
55
+ "sinon": "^9.2.4",
56
+ "ts-node": "^10.0.0",
57
+ "typescript": "^4.3.5"
58
+ },
59
+ "keywords": [
60
+ "npm",
61
+ "audit",
62
+ "skip",
63
+ "ignore",
64
+ "exclude",
65
+ "exceptions",
66
+ "node",
67
+ "security",
68
+ "advisory",
69
+ "vulnerabilities",
70
+ "continuous integration",
71
+ "dependencies",
72
+ "check",
73
+ "build",
74
+ "script",
75
+ "nsp",
76
+ "ci"
77
+ ]
78
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ var print_1 = require("../utils/print");
4
+ var vulnerability_1 = require("../utils/vulnerability");
5
+ /**
6
+ * Process and analyze the NPM audit JSON
7
+ * @param {String} jsonBuffer NPM audit stringified JSON payload
8
+ * @param {Number} auditLevel The level of vulnerabilities we care about
9
+ * @param {Array} exceptionIds List of vulnerability IDs to exclude
10
+ * @param {Array} exceptionModules List of vulnerable modules to ignore in audit results
11
+ * @param {Array} columnsToInclude List of columns to include in audit results
12
+ * @param {String} filterLevel Optional level to filter table display
13
+ */
14
+ function handleFinish(jsonBuffer, auditLevel, exceptionIds, exceptionModules, columnsToInclude, filterLevel) {
15
+ var _a = (0, vulnerability_1.processAuditJson)(jsonBuffer, auditLevel, exceptionIds, exceptionModules, columnsToInclude, filterLevel), unhandledIds = _a.unhandledIds, report = _a.report, failed = _a.failed, unusedExceptionIds = _a.unusedExceptionIds, unusedExceptionModules = _a.unusedExceptionModules;
16
+ // If unable to process the audit JSON
17
+ if (failed) {
18
+ console.error('Unable to process the JSON buffer string.');
19
+ // Exit failed
20
+ process.exit(1);
21
+ return;
22
+ }
23
+ // Print the security report
24
+ if (report.length) {
25
+ (0, print_1.printSecurityReport)(report, columnsToInclude);
26
+ }
27
+ // Handle unused exceptions
28
+ (0, vulnerability_1.handleUnusedExceptions)(unusedExceptionIds, unusedExceptionModules);
29
+ // Display the found unhandled vulnerabilities
30
+ if (unhandledIds.length) {
31
+ console.error("".concat(unhandledIds.length, " vulnerabilities found. Node security advisories: ").concat(unhandledIds.join(', ')));
32
+ // Exit failed
33
+ process.exit(1);
34
+ }
35
+ else {
36
+ // Happy happy, joy joy
37
+ console.info('🤝 All good!');
38
+ process.exit(0);
39
+ }
40
+ }
41
+ exports.default = handleFinish;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ var lodash_get_1 = __importDefault(require("lodash.get"));
7
+ var semver_1 = __importDefault(require("semver"));
8
+ var npm_1 = require("../utils/npm");
9
+ var file_1 = require("../utils/file");
10
+ var vulnerability_1 = require("../utils/vulnerability");
11
+ /**
12
+ * Get the `npm audit` flag to audit only production dependencies.
13
+ * @return {String} The flag.
14
+ */
15
+ function getProductionOnlyOption() {
16
+ var npmVersion = (0, npm_1.getNpmVersion)();
17
+ if (semver_1.default.satisfies(npmVersion, '<=8.13.2')) {
18
+ return '--production';
19
+ }
20
+ else {
21
+ return '--omit=dev';
22
+ }
23
+ }
24
+ /**
25
+ * Handle user's input
26
+ * @param {Object} options User's command options or flags
27
+ * @param {Function} fn The function to handle the inputs
28
+ */
29
+ function handleInput(options, fn) {
30
+ // Generate NPM Audit command
31
+ var auditCommand = [
32
+ 'npm audit',
33
+ // flags
34
+ (0, lodash_get_1.default)(options, 'production') ? getProductionOnlyOption() : '',
35
+ (0, lodash_get_1.default)(options, 'registry') ? "--registry=".concat(options.registry) : '',
36
+ ]
37
+ .filter(Boolean)
38
+ .join(' ');
39
+ // Taking the audit level from the command or environment variable
40
+ var envVar = process.env.NPM_CONFIG_AUDIT_LEVEL;
41
+ var auditLevel = (0, lodash_get_1.default)(options, 'level', envVar) || 'info';
42
+ // Process filter table option
43
+ var filterLevel;
44
+ var filterTableOption = (0, lodash_get_1.default)(options, 'filterTable');
45
+ if (filterTableOption) {
46
+ if (typeof filterTableOption === 'string') {
47
+ // User provided a specific level for filtering
48
+ filterLevel = filterTableOption;
49
+ }
50
+ else {
51
+ // User provided true flag, use the audit level
52
+ filterLevel = auditLevel;
53
+ }
54
+ }
55
+ // Get the exceptions
56
+ var nsprc = (0, file_1.readFile)('.nsprc');
57
+ var cmdExceptions = (0, lodash_get_1.default)(options, 'exclude', '')
58
+ .split(',')
59
+ .map(function (each) { return each.trim(); })
60
+ .filter(function (each) { return each !== ''; });
61
+ var exceptionIds = (0, vulnerability_1.getExceptionsIds)(nsprc, cmdExceptions);
62
+ var cmdModuleIgnore = (0, lodash_get_1.default)(options, 'moduleIgnore', '').split(',');
63
+ var cmdIncludeColumns = (0, lodash_get_1.default)(options, 'includeColumns', '')
64
+ .split(',')
65
+ .map(function (each) { return each.trim(); })
66
+ .filter(function (each) { return !!each; });
67
+ fn(auditCommand, auditLevel, exceptionIds, cmdModuleIgnore, cmdIncludeColumns, filterLevel);
68
+ }
69
+ exports.default = handleInput;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getSeverityBgColor = exports.color = void 0;
7
+ var lodash_get_1 = __importDefault(require("lodash.get"));
8
+ var RESET = '\x1b[0m';
9
+ var COLORS = {
10
+ reset: {
11
+ fg: '\x1b[0m',
12
+ bg: '\x1b[0m',
13
+ },
14
+ black: {
15
+ fg: '\x1b[30m',
16
+ bg: '\x1b[40m',
17
+ },
18
+ red: {
19
+ fg: '\x1b[31m',
20
+ bg: '\x1b[41m',
21
+ },
22
+ green: {
23
+ fg: '\x1b[32m',
24
+ bg: '\x1b[42m',
25
+ },
26
+ yellow: {
27
+ fg: '\x1b[33m',
28
+ bg: '\x1b[43m',
29
+ },
30
+ blue: {
31
+ fg: '\x1b[34m',
32
+ bg: '\x1b[44m',
33
+ },
34
+ magenta: {
35
+ fg: '\x1b[35m',
36
+ bg: '\x1b[45m',
37
+ },
38
+ cyan: {
39
+ fg: '\x1b[36m',
40
+ bg: '\x1b[46m',
41
+ },
42
+ white: {
43
+ fg: '\x1b[37m',
44
+ bg: '\x1b[47m',
45
+ },
46
+ };
47
+ /**
48
+ * Color a console message's foreground and background
49
+ * @param {String} message Message
50
+ * @param {String} fgColor Foreground color
51
+ * @param {String} bgColor Background color
52
+ * @return {String} Message
53
+ */
54
+ function color(message, fgColor, bgColor) {
55
+ if ('NO_COLOR' in process.env) {
56
+ return message;
57
+ }
58
+ return [
59
+ (0, lodash_get_1.default)(COLORS, "".concat(fgColor, ".fg"), ''),
60
+ (0, lodash_get_1.default)(COLORS, "".concat(bgColor, ".bg"), ''),
61
+ message,
62
+ RESET,
63
+ ].join('');
64
+ }
65
+ exports.color = color;
66
+ /**
67
+ * Get background color based on severity
68
+ * @param {String} severity Vulnerability's severity
69
+ * @return {(String | undefined)} Background color or undefined
70
+ */
71
+ function getSeverityBgColor(severity) {
72
+ switch (severity) {
73
+ case 'info':
74
+ return undefined;
75
+ case 'low':
76
+ return undefined;
77
+ case 'moderate':
78
+ return undefined;
79
+ case 'high':
80
+ return 'red';
81
+ case 'critical':
82
+ return 'red';
83
+ default: {
84
+ var exhaustiveCheck = severity;
85
+ return exhaustiveCheck;
86
+ }
87
+ }
88
+ }
89
+ exports.getSeverityBgColor = getSeverityBgColor;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shortenNodePath = exports.trimArray = exports.isJsonString = exports.isWholeNumber = void 0;
4
+ // TODO: This might be unused
5
+ /**
6
+ * @param {String | Number | Null | Boolean} value The input number
7
+ * @return {Boolean} Returns true if the input is a whole number
8
+ */
9
+ function isWholeNumber(value) {
10
+ if (value === null || value === undefined) {
11
+ return false;
12
+ }
13
+ if (!Number(value)) {
14
+ return false;
15
+ }
16
+ return Number(value) % 1 === 0;
17
+ }
18
+ exports.isWholeNumber = isWholeNumber;
19
+ /**
20
+ * @param {String} string The JSON stringified object
21
+ * @return {Boolean} Returns true if the input string is parse-able
22
+ */
23
+ function isJsonString(string) {
24
+ try {
25
+ JSON.parse(string);
26
+ }
27
+ catch (e) {
28
+ console.log('Failed parsing .nsprc file: ' + e);
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ exports.isJsonString = isJsonString;
34
+ // TODO: Add unit tests
35
+ /**
36
+ * Trim array size to a maximum number
37
+ * @param {Array} array Array to trim
38
+ * @param {Number} maxLength Desired length
39
+ * @return {Array} Trimmed array with additional message
40
+ */
41
+ function trimArray(array, maxLength) {
42
+ var originalLength = array.length;
43
+ var removedLength = Math.max(0, originalLength - maxLength);
44
+ if (removedLength === 0) {
45
+ return array;
46
+ }
47
+ array.length = maxLength;
48
+ return array.concat("...and ".concat(removedLength, " more"));
49
+ }
50
+ exports.trimArray = trimArray;
51
+ /**
52
+ * Shorten node path (node_modules/nodemon/node_modules/chokidar/node_modules/fsevents) to (nodemon>chokidar>fsevents)
53
+ * @param {String} path Full node path
54
+ * @return {String} Shorten Path
55
+ */
56
+ function shortenNodePath(path) {
57
+ return path.replace('node_modules/', '').replace(/\/node_modules\//g, '>');
58
+ }
59
+ exports.shortenNodePath = shortenNodePath;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.analyzeExpiry = exports.isValidDate = void 0;
7
+ var dayjs_1 = __importDefault(require("dayjs"));
8
+ /**
9
+ * Validate if the given timestamp is a valid UNIX timestamp
10
+ * @param {Any} timestamp The given timestamp
11
+ * @return {Boolean} Returns true if it is a valid UNIX timestamp
12
+ */
13
+ function isValidDate(timestamp) {
14
+ return new Date(timestamp).getTime() > 0;
15
+ }
16
+ exports.isValidDate = isValidDate;
17
+ /**
18
+ * Analyze the given date time if it has expired (in the past)
19
+ * @param {String | Number} expiry Expiry timestamp
20
+ * @param {String | Number} now The date to compare with
21
+ * @return {Object} Return the analysis
22
+ */
23
+ function analyzeExpiry(expiry, now) {
24
+ if (now === void 0) { now = new Date().valueOf(); }
25
+ if (!expiry) {
26
+ return { valid: true };
27
+ }
28
+ if (!isValidDate(expiry) || !isValidDate(now)) {
29
+ return { valid: false };
30
+ }
31
+ var dayjsNow = (0, dayjs_1.default)(now);
32
+ return {
33
+ valid: true,
34
+ expired: dayjsNow.isAfter(expiry),
35
+ years: dayjsNow.diff(expiry, 'years'),
36
+ };
37
+ }
38
+ exports.analyzeExpiry = analyzeExpiry;
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.readFile = void 0;
7
+ var fs_1 = __importDefault(require("fs"));
8
+ var common_1 = require("./common");
9
+ /**
10
+ * Read file from path
11
+ * @param {String} path File path
12
+ * @return {(Object | Boolean)} Returns the parsed data if found, or else returns `false`
13
+ */
14
+ function readFile(path) {
15
+ try {
16
+ var data = fs_1.default.readFileSync(path, 'utf8');
17
+ if (!(0, common_1.isJsonString)(data)) {
18
+ return false;
19
+ }
20
+ return JSON.parse(data);
21
+ }
22
+ catch (err) {
23
+ return false;
24
+ }
25
+ }
26
+ exports.readFile = readFile;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getNpmVersion = void 0;
4
+ var child_process_1 = require("child_process");
5
+ /**
6
+ * Get the current npm version
7
+ * @return {String} The npm version
8
+ */
9
+ function getNpmVersion() {
10
+ var version = (0, child_process_1.execSync)('npm --version');
11
+ return version.toString();
12
+ }
13
+ exports.getNpmVersion = getNpmVersion;
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
3
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
4
+ if (ar || !(i in from)) {
5
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
6
+ ar[i] = from[i];
7
+ }
8
+ }
9
+ return to.concat(ar || Array.prototype.slice.call(from));
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.printExceptionReport = exports.printSecurityReport = exports.getColumnWidth = void 0;
16
+ var lodash_get_1 = __importDefault(require("lodash.get"));
17
+ var table_1 = require("table");
18
+ var SECURITY_REPORT_HEADER = ['ID', 'Module', 'Title', 'Paths', 'Severity', 'URL', 'Ex.', 'Fix'];
19
+ var EXCEPTION_REPORT_HEADER = ['ID', 'Status', 'Expiry', 'Notes'];
20
+ // TODO: Add unit tests
21
+ /**
22
+ * Get the column width size for the table
23
+ * @param {Array} tableData Table data (Array of array)
24
+ * @param {Number} columnIndex Target column index
25
+ * @param {Number} maxWidth Maximum width
26
+ * @param {Number} minWidth Minimum width
27
+ * @return {Number} width
28
+ */
29
+ function getColumnWidth(tableData, columnIndex, maxWidth, minWidth) {
30
+ if (maxWidth === void 0) { maxWidth = 50; }
31
+ if (minWidth === void 0) { minWidth = 15; }
32
+ // Find the maximum length in the column
33
+ var contentLength = tableData.reduce(function (max, cur) {
34
+ var content = JSON.stringify((0, lodash_get_1.default)(cur, columnIndex, ''));
35
+ // Remove the color codes
36
+ content = content.replace(/\\x1b\[\d{1,2}m/g, '');
37
+ content = content.replace(/\\u001b\[\d{1,2}m/g, '');
38
+ content = content.replace(/"/g, '');
39
+ // Keep whichever number that is bigger
40
+ return content.length > max ? content.length : max;
41
+ },
42
+ // Start with minimum width (also auto handling empty column case)
43
+ minWidth);
44
+ // Return the content length up to a maximum point
45
+ return Math.min(contentLength, maxWidth);
46
+ }
47
+ exports.getColumnWidth = getColumnWidth;
48
+ /**
49
+ * Print the security report in a table format
50
+ * @param {Array} data Array of arrays
51
+ * @return {undefined} Returns void
52
+ * @param {Array} columnsToInclude List of columns to include in audit results
53
+ */
54
+ function printSecurityReport(data, columnsToInclude) {
55
+ var configs = {
56
+ singleLine: true,
57
+ header: {
58
+ alignment: 'center',
59
+ content: '=== npm audit security report ===\n',
60
+ },
61
+ columns: {
62
+ // "Title" column index
63
+ 2: {
64
+ width: getColumnWidth(data, 2),
65
+ wrapWord: true,
66
+ },
67
+ // "Paths" column index
68
+ 3: {
69
+ width: getColumnWidth(data, 3),
70
+ wrapWord: true,
71
+ },
72
+ },
73
+ };
74
+ var headers = columnsToInclude.length ? SECURITY_REPORT_HEADER.filter(function (h) { return columnsToInclude.includes(h); }) : SECURITY_REPORT_HEADER;
75
+ console.info((0, table_1.table)(__spreadArray([headers], data, true), configs));
76
+ }
77
+ exports.printSecurityReport = printSecurityReport;
78
+ /**
79
+ * Print the exception report in a table format
80
+ * @param {Array} data Array of arrays
81
+ * @return {undefined} Returns void
82
+ */
83
+ function printExceptionReport(data) {
84
+ var configs = {
85
+ singleLine: true,
86
+ header: {
87
+ alignment: 'center',
88
+ content: '=== list of exceptions ===\n',
89
+ },
90
+ };
91
+ console.info((0, table_1.table)(__spreadArray([EXCEPTION_REPORT_HEADER], data, true), configs));
92
+ }
93
+ exports.printExceptionReport = printExceptionReport;
@@ -0,0 +1,365 @@
1
+ "use strict";
2
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
3
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
4
+ if (ar || !(i in from)) {
5
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
6
+ ar[i] = from[i];
7
+ }
8
+ }
9
+ return to.concat(ar || Array.prototype.slice.call(from));
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.handleUnusedExceptions = exports.processExceptions = exports.getExceptionsIds = exports.processAuditJson = exports.validateV7Vulnerability = exports.validateV6Vulnerability = exports.mapLevelToNumber = void 0;
16
+ var lodash_get_1 = __importDefault(require("lodash.get"));
17
+ var common_1 = require("./common");
18
+ var color_1 = require("./color");
19
+ var print_1 = require("./print");
20
+ var date_1 = require("./date");
21
+ var MAX_PATHS_SIZE = 5;
22
+ /**
23
+ * Converts an audit level to a numeric value
24
+ * @param {String} auditLevel Audit level
25
+ * @return {Number} Numeric level: the higher the number, the more severe it is
26
+ */
27
+ function mapLevelToNumber(auditLevel) {
28
+ switch (auditLevel) {
29
+ case 'info':
30
+ return 0;
31
+ case 'low':
32
+ return 1;
33
+ case 'moderate':
34
+ return 2;
35
+ case 'high':
36
+ return 3;
37
+ case 'critical':
38
+ return 4;
39
+ default:
40
+ return 0;
41
+ }
42
+ }
43
+ exports.mapLevelToNumber = mapLevelToNumber;
44
+ /**
45
+ * Validate if the vulnerability should be excepted
46
+ * @param {Object} vulnerability NPM v6 audit report's vulnerability
47
+ * @param {Array} exceptionIds Exception IDs
48
+ * @return {Object} Validation result
49
+ */
50
+ function validateV6Vulnerability(vulnerability, exceptionIds) {
51
+ return exceptionIds.reduce(function (acc, id) {
52
+ // check if ID matches
53
+ if (id === String(vulnerability.id)) {
54
+ return { isExcepted: true, usedExceptionKey: id };
55
+ }
56
+ // check if any of the CVEs matches
57
+ if (Array.isArray(vulnerability.cves) && vulnerability.cves.includes(id)) {
58
+ return { isExcepted: true, usedExceptionKey: id };
59
+ }
60
+ // check if the CWE matches
61
+ if (vulnerability.cwe === id) {
62
+ return { isExcepted: true, usedExceptionKey: id };
63
+ }
64
+ // check if the URL matches
65
+ if (vulnerability.url && vulnerability.url.includes(id)) {
66
+ return { isExcepted: true, usedExceptionKey: id };
67
+ }
68
+ return acc;
69
+ }, {
70
+ isExcepted: false,
71
+ usedExceptionKey: '',
72
+ });
73
+ }
74
+ exports.validateV6Vulnerability = validateV6Vulnerability;
75
+ /**
76
+ * Validate if the vulnerability should be excepted
77
+ * @param {Object} vulnerability NPM v7 audit report's vulnerability
78
+ * @param {Array} exceptionIds Exception IDs
79
+ * @return {Object} Validation result
80
+ */
81
+ function validateV7Vulnerability(vulnerability, exceptionIds) {
82
+ return exceptionIds.reduce(function (acc, id) {
83
+ // check if ID matches
84
+ if (id === String(vulnerability.source)) {
85
+ return { isExcepted: true, usedExceptionKey: id };
86
+ }
87
+ // check if the URL matches
88
+ if (vulnerability.url && vulnerability.url.includes(id)) {
89
+ return { isExcepted: true, usedExceptionKey: id };
90
+ }
91
+ return acc;
92
+ }, {
93
+ isExcepted: false,
94
+ usedExceptionKey: '',
95
+ });
96
+ }
97
+ exports.validateV7Vulnerability = validateV7Vulnerability;
98
+ /**
99
+ * Analyze the JSON string buffer
100
+ * @param {String} jsonBuffer NPM Audit JSON string buffer
101
+ * @param {String} auditLevel User's target audit level
102
+ * @param {Array} exceptionIds Exception IDs (ID to be ignored)
103
+ * @param {Array} exceptionModules Exception modules (modules to be ignored)
104
+ * @param {Array} columnsToInclude List of columns to include in audit results
105
+ * @param {String} filterLevel Optional level to filter table display (if not provided, shows all vulnerabilities)
106
+ * @return {Object} Processed vulnerabilities details
107
+ */
108
+ function processAuditJson(jsonBuffer, auditLevel, exceptionIds, exceptionModules, columnsToInclude, filterLevel) {
109
+ if (jsonBuffer === void 0) { jsonBuffer = ''; }
110
+ if (auditLevel === void 0) { auditLevel = 'info'; }
111
+ if (exceptionIds === void 0) { exceptionIds = []; }
112
+ if (exceptionModules === void 0) { exceptionModules = []; }
113
+ if (columnsToInclude === void 0) { columnsToInclude = []; }
114
+ if (!(0, common_1.isJsonString)(jsonBuffer)) {
115
+ return {
116
+ unhandledIds: [],
117
+ vulnerabilityIds: [],
118
+ vulnerabilityModules: [],
119
+ unusedExceptionIds: exceptionIds,
120
+ unusedExceptionModules: exceptionModules,
121
+ report: [],
122
+ failed: true,
123
+ };
124
+ }
125
+ // NPM v6 uses `advisories`
126
+ // NPM v7 uses `vulnerabilities`
127
+ // Refer to the `test/__mocks__` folder for some sample mockups
128
+ var _a = JSON.parse(jsonBuffer), advisories = _a.advisories, vulnerabilities = _a.vulnerabilities;
129
+ // NPM v6 handling
130
+ if (advisories) {
131
+ return Object.values(advisories).reduce(function (acc, cur) {
132
+ var shouldAudit = mapLevelToNumber(cur.severity) >= mapLevelToNumber(auditLevel);
133
+ var _a = validateV6Vulnerability(cur, exceptionIds), isIdExcepted = _a.isExcepted, usedExceptionKey = _a.usedExceptionKey;
134
+ var isModuleExcepted = exceptionModules.includes(cur.module_name);
135
+ var isExcepted = isIdExcepted || isModuleExcepted;
136
+ // Record used exception ID/module
137
+ if (isIdExcepted) {
138
+ acc.unusedExceptionIds = acc.unusedExceptionIds.filter(function (id) { return id !== usedExceptionKey; });
139
+ }
140
+ if (isModuleExcepted) {
141
+ acc.unusedExceptionModules = acc.unusedExceptionModules.filter(function (module) { return module !== cur.module_name; });
142
+ }
143
+ var rowData = [
144
+ { key: 'ID', value: cur.id.toString() },
145
+ { key: 'Module', value: cur.module_name },
146
+ { key: 'Title', value: cur.title },
147
+ {
148
+ key: 'Paths',
149
+ value: (0, common_1.trimArray)(cur.findings.reduce(function (a, c) { return __spreadArray(__spreadArray([], a, true), c.paths, true); }, []), MAX_PATHS_SIZE).join('\n'),
150
+ },
151
+ { key: 'Severity', value: cur.severity },
152
+ { key: 'URL', value: cur.url },
153
+ { key: 'Ex.', value: isExcepted ? 'y' : 'n' },
154
+ { key: 'Fix', value: 'N/A' },
155
+ ]
156
+ .filter(function (_a) {
157
+ var key = _a.key;
158
+ return (columnsToInclude.length ? columnsToInclude.includes(key) : true);
159
+ })
160
+ .map(function (_a) {
161
+ var key = _a.key, value = _a.value;
162
+ return (0, color_1.color)(value, isExcepted ? '' : 'yellow', key === 'Severity' ? (0, color_1.getSeverityBgColor)(cur.severity) : undefined);
163
+ });
164
+ // Record this vulnerability into the report based on filter level
165
+ var shouldIncludeInTable = filterLevel ? mapLevelToNumber(cur.severity) >= mapLevelToNumber(filterLevel) : true;
166
+ if (shouldIncludeInTable) {
167
+ acc.report.push(rowData);
168
+ }
169
+ acc.vulnerabilityIds.push(cur.id.toString());
170
+ if (!acc.vulnerabilityModules.includes(cur.module_name)) {
171
+ acc.vulnerabilityModules.push(cur.module_name);
172
+ }
173
+ // Found unhandled vulnerabilities
174
+ if (shouldAudit && !isExcepted) {
175
+ acc.unhandledIds.push(cur.id.toString());
176
+ }
177
+ return acc;
178
+ }, {
179
+ unhandledIds: [],
180
+ vulnerabilityIds: [],
181
+ vulnerabilityModules: [],
182
+ unusedExceptionIds: exceptionIds,
183
+ unusedExceptionModules: exceptionModules,
184
+ report: [],
185
+ });
186
+ }
187
+ // NPM v7 handling
188
+ if (vulnerabilities) {
189
+ return Object.values(vulnerabilities).reduce(function (acc, cur) {
190
+ // Inside `via` array, its either the related module name or the vulnerability source object.
191
+ (0, lodash_get_1.default)(cur, 'via', []).forEach(function (vul) {
192
+ // The vulnerability ID is labeled as `source`
193
+ var id = (0, lodash_get_1.default)(vul, 'source');
194
+ var moduleName = (0, lodash_get_1.default)(vul, 'name', '');
195
+ // Let's skip if ID is a string (module name), and only focus on the root vulnerabilities
196
+ if (!id || typeof id === 'string' || typeof vul === 'string') {
197
+ return;
198
+ }
199
+ var shouldAudit = mapLevelToNumber(vul.severity) >= mapLevelToNumber(auditLevel);
200
+ var _a = validateV7Vulnerability(vul, exceptionIds), isIdExcepted = _a.isExcepted, usedExceptionKey = _a.usedExceptionKey;
201
+ var isModuleExcepted = exceptionModules.includes(moduleName);
202
+ var isExcepted = isIdExcepted || isModuleExcepted;
203
+ // Record used exception ID/module
204
+ if (isIdExcepted) {
205
+ acc.unusedExceptionIds = acc.unusedExceptionIds.filter(function (id) { return id !== usedExceptionKey; });
206
+ }
207
+ if (isModuleExcepted) {
208
+ acc.unusedExceptionModules = acc.unusedExceptionModules.filter(function (module) { return module !== moduleName; });
209
+ }
210
+ var rowData = [
211
+ { key: 'ID', value: String(id) },
212
+ { key: 'Module', value: vul.name },
213
+ { key: 'Title', value: vul.title },
214
+ { key: 'Paths', value: (0, common_1.trimArray)((0, lodash_get_1.default)(cur, 'nodes', []).map(common_1.shortenNodePath), MAX_PATHS_SIZE).join('\n') },
215
+ { key: 'Severity', value: vul.severity, bgColor: (0, color_1.getSeverityBgColor)(vul.severity) },
216
+ { key: 'URL', value: vul.url },
217
+ { key: 'Ex.', value: isExcepted ? 'y' : 'n' },
218
+ {
219
+ key: 'Fix',
220
+ value: (function () {
221
+ var fixAvailable = (0, lodash_get_1.default)(cur, 'fixAvailable');
222
+ if (typeof fixAvailable === 'object' && fixAvailable !== null && fixAvailable.isSemVerMajor) {
223
+ return 'major';
224
+ }
225
+ return String(Boolean(fixAvailable));
226
+ })(),
227
+ },
228
+ ]
229
+ .filter(function (_a) {
230
+ var key = _a.key;
231
+ return (columnsToInclude.length ? columnsToInclude.includes(key) : true);
232
+ })
233
+ .map(function (_a) {
234
+ var key = _a.key, value = _a.value, bgColor = _a.bgColor;
235
+ return (0, color_1.color)(value, isExcepted ? '' : 'yellow', key === 'Severity' ? bgColor : undefined);
236
+ });
237
+ // Record this vulnerability into the report based on filter level
238
+ var shouldIncludeInTable = filterLevel ? mapLevelToNumber(vul.severity) >= mapLevelToNumber(filterLevel) : true;
239
+ if (shouldIncludeInTable) {
240
+ acc.report.push(rowData);
241
+ }
242
+ acc.vulnerabilityIds.push(String(id));
243
+ if (!acc.vulnerabilityModules.includes(moduleName)) {
244
+ acc.vulnerabilityModules.push(moduleName);
245
+ }
246
+ // Found unhandled vulnerabilities
247
+ if (shouldAudit && !isExcepted) {
248
+ acc.unhandledIds.push(String(id));
249
+ }
250
+ });
251
+ return acc;
252
+ }, {
253
+ unhandledIds: [],
254
+ vulnerabilityIds: [],
255
+ vulnerabilityModules: [],
256
+ unusedExceptionIds: exceptionIds,
257
+ unusedExceptionModules: exceptionModules,
258
+ report: [],
259
+ });
260
+ }
261
+ return {
262
+ unhandledIds: [],
263
+ vulnerabilityIds: [],
264
+ vulnerabilityModules: [],
265
+ unusedExceptionIds: exceptionIds,
266
+ unusedExceptionModules: exceptionModules,
267
+ report: [],
268
+ failed: true,
269
+ };
270
+ }
271
+ exports.processAuditJson = processAuditJson;
272
+ /**
273
+ * Process all exceptions and return a list of exception IDs
274
+ * @param {Object | Boolean} nsprc File content from `.nsprc`
275
+ * @param {Array} cmdExceptions Exceptions passed in via command line
276
+ * @return {Array} List of found vulnerabilities
277
+ */
278
+ function getExceptionsIds(nsprc, cmdExceptions) {
279
+ if (cmdExceptions === void 0) { cmdExceptions = []; }
280
+ // If file does not exists
281
+ if (!nsprc || typeof nsprc !== 'object') {
282
+ // If there are exceptions passed in from command line
283
+ if (cmdExceptions.length) {
284
+ // Display simple info
285
+ console.info("Exception IDs: ".concat(cmdExceptions.join(', ')));
286
+ return cmdExceptions;
287
+ }
288
+ return [];
289
+ }
290
+ // Process the content of the file along with the command line exceptions
291
+ var _a = processExceptions(nsprc, cmdExceptions), exceptionIds = _a.exceptionIds, report = _a.report;
292
+ (0, print_1.printExceptionReport)(report);
293
+ return exceptionIds;
294
+ }
295
+ exports.getExceptionsIds = getExceptionsIds;
296
+ /**
297
+ * Filter the given list in the `.nsprc` file for valid exceptions
298
+ * @param {Object} nsprc The nsprc file content, contains exception info
299
+ * @param {Array} cmdExceptions Exceptions passed in via command line
300
+ * @return {Object} Processed vulnerabilities details
301
+ */
302
+ function processExceptions(nsprc, cmdExceptions) {
303
+ if (cmdExceptions === void 0) { cmdExceptions = []; }
304
+ return Object.entries(nsprc).reduce(function (acc, _a) {
305
+ var id = _a[0], details = _a[1];
306
+ var isActive = Boolean((0, lodash_get_1.default)(details, 'active', true)); // default to true
307
+ var notes = typeof details === 'string' ? details : (0, lodash_get_1.default)(details, 'notes', '');
308
+ var _b = (0, date_1.analyzeExpiry)((0, lodash_get_1.default)(details, 'expiry')), valid = _b.valid, expired = _b.expired, years = _b.years;
309
+ // Color the status accordingly
310
+ var status = (0, color_1.color)('active', 'green');
311
+ if (expired) {
312
+ status = (0, color_1.color)('expired', 'red');
313
+ }
314
+ else if (!valid) {
315
+ status = (0, color_1.color)('invalid', 'red');
316
+ }
317
+ else if (!isActive) {
318
+ status = (0, color_1.color)('inactive', 'yellow');
319
+ }
320
+ // Color the date accordingly
321
+ var expiry = (0, lodash_get_1.default)(details, 'expiry');
322
+ var expiryDate = expiry !== undefined && expiry !== null ? new Date(expiry).toUTCString() : '';
323
+ // If it was expired for more than 5 years ago, warn by coloring the date in red
324
+ if (years && years <= -5) {
325
+ expiryDate = (0, color_1.color)(expiryDate, 'red');
326
+ }
327
+ else if (years && years <= -1) {
328
+ expiryDate = (0, color_1.color)(expiryDate, 'yellow');
329
+ }
330
+ acc.report.push([id, status, expiryDate, notes]);
331
+ if (isActive && !expired) {
332
+ acc.exceptionIds.push(id);
333
+ }
334
+ return acc;
335
+ }, {
336
+ exceptionIds: cmdExceptions,
337
+ report: cmdExceptions.map(function (id) { return [String(id), (0, color_1.color)('active', 'green'), '', '']; }),
338
+ });
339
+ }
340
+ exports.processExceptions = processExceptions;
341
+ /**
342
+ * Handle unused exceptions from user: console log them
343
+ * @param {Array} unusedExceptionIds List of unused exception IDs
344
+ * @param {Array} unusedExceptionModules List of unused exception module names
345
+ */
346
+ function handleUnusedExceptions(unusedExceptionIds, unusedExceptionModules) {
347
+ var cleanedUnusedExceptionIds = unusedExceptionIds.filter(Boolean);
348
+ var cleanedUnusedExceptionModules = unusedExceptionModules.filter(Boolean);
349
+ var message = [
350
+ cleanedUnusedExceptionIds.length &&
351
+ "".concat(cleanedUnusedExceptionIds.length, " of the excluded vulnerabilities did not match any of the found vulnerabilities: ").concat(cleanedUnusedExceptionIds.join(', '), "."),
352
+ cleanedUnusedExceptionIds.length &&
353
+ "".concat(cleanedUnusedExceptionIds.length > 1 ? 'They' : 'It', " can be removed from the .nsprc file or --exclude -x flags."),
354
+ cleanedUnusedExceptionModules.length &&
355
+ "".concat(cleanedUnusedExceptionModules.length, " of the ignored modules did not match any of the found vulnerabilities: ").concat(cleanedUnusedExceptionModules.join(', '), "."),
356
+ cleanedUnusedExceptionModules.length &&
357
+ "".concat(cleanedUnusedExceptionModules.length > 1 ? 'They' : 'It', " can be removed from the --module-ignore -m flags."),
358
+ ]
359
+ .filter(Boolean)
360
+ .join(' ');
361
+ if (message) {
362
+ console.warn(message);
363
+ }
364
+ }
365
+ exports.handleUnusedExceptions = handleUnusedExceptions;