Skip to content

Commit

Permalink
Include dependency path into the security report
Browse files Browse the repository at this point in the history
  • Loading branch information
jeemok committed Aug 7, 2021
1 parent fbb72c5 commit e5d19a5
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 50 deletions.
60 changes: 30 additions & 30 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,15 @@
"name": "better-npm-audit",
"version": "3.1.2",
"author": "Jee Mok <jee.ict@hotmail.com>",
"description": "Reshape npm audit into the way the community would like, by the community itself, to encourage more people to do security audits.",
"description": "Reshape into a better npm audit for the community and encourage more people to include security audit into their process.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/jeemok/better-npm-audit"
},
"keywords": [
"npm",
"audit",
"skip",
"ignore",
"exclude",
"exceptions",
"node",
"security",
"advisory",
"vulnerabilities",
"continuous integration",
"dependencies",
"check",
"build",
"script",
"nsp",
"ci"
],
"main": "lib/index.js",
"bin": {
"better-npm-audit": "index.js"
},
"dependencies": {
"commander": "^8.0.0",
"dayjs": "^1.10.6",
"lodash.get": "^4.4.2",
"table": "^6.7.1"
"repository": {
"type": "git",
"url": "https://github.com/jeemok/better-npm-audit"
},
"engines": {
"node": ">= 8.12"
Expand All @@ -52,6 +27,12 @@
"publish:live": "npm run build && npm publish lib --tag latest",
"publish:next": "npm run build && npm publish lib --tag next"
},
"dependencies": {
"commander": "^8.0.0",
"dayjs": "^1.10.6",
"lodash.get": "^4.4.2",
"table": "^6.7.1"
},
"devDependencies": {
"@types/chai": "^4.2.19",
"@types/lodash.get": "^4.4.6",
Expand All @@ -71,5 +52,24 @@
"sinon": "^9.2.4",
"ts-node": "^10.0.0",
"typescript": "^4.3.5"
}
},
"keywords": [
"npm",
"audit",
"skip",
"ignore",
"exclude",
"exceptions",
"node",
"security",
"advisory",
"vulnerabilities",
"continuous integration",
"dependencies",
"check",
"build",
"script",
"nsp",
"ci"
]
}
5 changes: 5 additions & 0 deletions src/types/general.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export interface v6Advisory {
readonly title: string;
readonly severity: AuditLevel;
readonly url: string;
readonly findings: {
version: string;
paths: string[];
}[];
}

export interface v7Vulnerabilities {
Expand All @@ -32,6 +36,7 @@ export interface v7Vulnerabilities {
export interface v7Vulnerability {
readonly name: string;
readonly via: v7VulnerabilityVia[] | string[];
readonly nodes: string[];
}

export interface v7VulnerabilityVia {
Expand Down
2 changes: 1 addition & 1 deletion src/types/table.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export type SecurityReportHeader = 'ID' | 'Module' | 'Title' | 'Sev.' | 'URL' | 'Ex.';
export type SecurityReportHeader = 'ID' | 'Module' | 'Title' | 'Paths' | 'Sev.' | 'URL' | 'Ex.';
export type ExceptionReportHeader = 'ID' | 'Status' | 'Expiry' | 'Notes';
17 changes: 17 additions & 0 deletions src/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,20 @@ export function isJsonString(string: string): boolean {
}
return true;
}

// TODO: Add unit tests
/**
* Trim array size to a maximum number
* @param {Array} array Array to trim
* @param {Number} maxLength Desired length
* @return {Array} Trimmed array with additional message
*/
export function trimArray(array: string[], maxLength: number): string[] {
const originalLength = array.length;
const removedLength = Math.max(0, originalLength - maxLength);
if (removedLength === 0) {
return array;
}
array.length = maxLength;
return array.concat(`...and ${removedLength} more`);
}
43 changes: 42 additions & 1 deletion src/utils/print.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
import get from 'lodash.get';
import { table, TableUserConfig } from 'table';
import { SecurityReportHeader, ExceptionReportHeader } from 'src/types';

const SECURITY_REPORT_HEADER: SecurityReportHeader[] = ['ID', 'Module', 'Title', 'Sev.', 'URL', 'Ex.'];
const SECURITY_REPORT_HEADER: SecurityReportHeader[] = ['ID', 'Module', 'Title', 'Paths', 'Sev.', 'URL', 'Ex.'];
const EXCEPTION_REPORT_HEADER: ExceptionReportHeader[] = ['ID', 'Status', 'Expiry', 'Notes'];

// TODO: Add unit tests
/**
* Get the column width size for the table
* @param {Array} tableData Table data (Array of array)
* @param {Number} columnIndex Target column index
* @param {Number} maxWidth Maximum width
* @param {Number} minWidth Minimum width
* @return {Number} width
*/
export function getColumnWidth(tableData: string[][], columnIndex: number, maxWidth = 50, minWidth = 15): number {
// Find the maximum length in the column
const contentLength = tableData.reduce(
(max, cur) => {
let content = JSON.stringify(get(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
return content.length > max ? content.length : max;
},
// Start with minimum width (also auto handling empty column case)
minWidth,
);
// Return the content length up to a maximum point
return Math.min(contentLength, maxWidth);
}

/**
* Print the security report in a table format
* @param {Array} data Array of arrays
Expand All @@ -16,6 +45,18 @@ export function printSecurityReport(data: string[][]): void {
alignment: 'center',
content: '=== npm audit security report ===\n',
},
columns: {
// "Title" column index
2: {
width: getColumnWidth(data, 2),
wrapWord: true,
},
// "Paths" column index
3: {
width: getColumnWidth(data, 3),
wrapWord: true,
},
},
};

console.info(table([SECURITY_REPORT_HEADER, ...data], configs));
Expand Down
14 changes: 12 additions & 2 deletions src/utils/vulnerability.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import get from 'lodash.get';

import { isJsonString } from './common';
import { isJsonString, trimArray } from './common';
import { color, getSeverityBgColor } from './color';
import { printExceptionReport } from './print';
import { analyzeExpiry } from './date';
Expand All @@ -18,6 +18,8 @@ import {
AuditNumber,
} from 'src/types';

const MAX_PATHS_SIZE = 5;

/**
* Converts an audit level to a numeric value
* @param {String} auditLevel Audit level
Expand Down Expand Up @@ -74,6 +76,13 @@ export function processAuditJson(jsonBuffer = '', auditLevel: AuditLevel = 'info
color(cur.id, isExcepted ? '' : 'yellow'),
color(cur.module_name, isExcepted ? '' : 'yellow'),
color(cur.title, isExcepted ? '' : 'yellow'),
color(
trimArray(
cur.findings.reduce((a, c) => [...a, ...c.paths] as [], []),
MAX_PATHS_SIZE,
).join('\n'),
isExcepted ? '' : 'yellow',
),
color(cur.severity, isExcepted ? '' : 'yellow', getSeverityBgColor(cur.severity)),
color(cur.url, isExcepted ? '' : 'yellow'),
isExcepted ? 'y' : color('n', 'yellow'),
Expand Down Expand Up @@ -118,9 +127,10 @@ export function processAuditJson(jsonBuffer = '', auditLevel: AuditLevel = 'info
color(String(id), isExcepted ? '' : 'yellow'),
color(vul.name, isExcepted ? '' : 'yellow'),
color(vul.title, isExcepted ? '' : 'yellow'),
color(trimArray(get(cur, 'nodes', []), MAX_PATHS_SIZE).join('\n'), isExcepted ? '' : 'yellow'),
color(vul.severity, isExcepted ? '' : 'yellow', getSeverityBgColor(vul.severity)),
color(vul.url, isExcepted ? '' : 'yellow'),
isExcepted ? 'y' : color('n', 'red'),
isExcepted ? 'y' : color('n', 'yellow'),
]);

acc.vulnerabilityIds.push(id);
Expand Down
13 changes: 12 additions & 1 deletion test/__mocks__/v6-security-report-table-data.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"\u001b[33m975\u001b[0m",
"\u001b[33mswagger-ui\u001b[0m",
"\u001b[33mReverse Tabnapping\u001b[0m",
"\u001b[33mloopback-component-explorer>swagger-ui\u001b[0m",
"\u001b[33mmoderate\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/975\u001b[0m",
"\u001b[33mn\u001b[0m"
Expand All @@ -11,6 +12,7 @@
"\u001b[33m976\u001b[0m",
"\u001b[33mswagger-ui\u001b[0m",
"\u001b[33mCross-Site Scripting\u001b[0m",
"\u001b[33mloopback-component-explorer>swagger-ui\u001b[0m",
"\u001b[33mmoderate\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/976\u001b[0m",
"\u001b[33mn\u001b[0m"
Expand All @@ -19,6 +21,7 @@
"\u001b[33m985\u001b[0m",
"\u001b[33mswagger-ui\u001b[0m",
"\u001b[33mCross-Site Scripting\u001b[0m",
"\u001b[33mloopback-component-explorer>swagger-ui\u001b[0m",
"\u001b[33mmoderate\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/985\u001b[0m",
"\u001b[33mn\u001b[0m"
Expand All @@ -27,6 +30,7 @@
"\u001b[33m1084\u001b[0m",
"\u001b[33mmem\u001b[0m",
"\u001b[33mDenial of Service\u001b[0m",
"\u001b[33mloopback-connector-rest>strong-globalize>os-locale>mem\u001b[0m",
"\u001b[33mlow\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/1084\u001b[0m",
"\u001b[33mn\u001b[0m"
Expand All @@ -35,6 +39,7 @@
"\u001b[33m1179\u001b[0m",
"\u001b[33mminimist\u001b[0m",
"\u001b[33mPrototype Pollution\u001b[0m",
"\u001b[33mmocha>mkdirp>minimist\u001b[0m",
"\u001b[33mlow\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/1179\u001b[0m",
"\u001b[33mn\u001b[0m"
Expand All @@ -43,6 +48,7 @@
"\u001b[33m1213\u001b[0m",
"\u001b[33mdot-prop\u001b[0m",
"\u001b[33mPrototype Pollution\u001b[0m",
"\u001b[33mnodemon>update-notifier>configstore>dot-prop\u001b[0m",
"\u001b[33m\u001b[41mhigh\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/1213\u001b[0m",
"\u001b[33mn\u001b[0m"
Expand All @@ -51,6 +57,7 @@
"\u001b[33m1500\u001b[0m",
"\u001b[33myargs-parser\u001b[0m",
"\u001b[33mPrototype Pollution\u001b[0m",
"\u001b[33mmocha>yargs-parser\nmocha>yargs-unparser>yargs>yargs-parser\u001b[0m",
"\u001b[33mlow\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/1500\u001b[0m",
"\u001b[33mn\u001b[0m"
Expand All @@ -59,6 +66,7 @@
"\u001b[33m1523\u001b[0m",
"\u001b[33mlodash\u001b[0m",
"\u001b[33mPrototype Pollution\u001b[0m",
"\u001b[33mlodash\nloopback>async>lodash\nloopback>loopback-connector-remote>loopback-datasource-juggler>async>lodash\nloopback>loopback-datasource-juggler>async>lodash\nloopback>loopback-connector-remote>strong-remoting>loopback-phase>async>lodash\n...and 40 more\u001b[0m",
"\u001b[33mlow\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/1523\u001b[0m",
"\u001b[33mn\u001b[0m"
Expand All @@ -67,6 +75,7 @@
"\u001b[33m1555\u001b[0m",
"\u001b[33mbl\u001b[0m",
"\u001b[33mRemote Memory Exposure\u001b[0m",
"\u001b[33mloopback>loopback-connector-remote>loopback-datasource-juggler>loopback-connector>msgpack5>bl\nloopback>loopback-datasource-juggler>loopback-connector>msgpack5>bl\nloopback-connector-mongodb>loopback-connector>msgpack5>bl\u001b[0m",
"\u001b[33m\u001b[41mcritical\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/1555\u001b[0m",
"\u001b[33mn\u001b[0m"
Expand All @@ -75,6 +84,7 @@
"\u001b[33m1556\u001b[0m",
"\u001b[33mnode-fetch\u001b[0m",
"\u001b[33mDenial of Service\u001b[0m",
"\u001b[33mloopback-connector-rest>strong-globalize>g11n-pipeline>swagger-client>cross-fetch>node-fetch\u001b[0m",
"\u001b[33mlow\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/1556\u001b[0m",
"\u001b[33mn\u001b[0m"
Expand All @@ -83,8 +93,9 @@
"\u001b[33m1589\u001b[0m",
"\u001b[33mini\u001b[0m",
"\u001b[33mPrototype Pollution\u001b[0m",
"\u001b[33mnodemon>chokidar>fsevents>node-pre-gyp>rc>ini\nnodemon>update-notifier>is-installed-globally>global-dirs>ini\nnodemon>update-notifier>latest-version>package-json>registry-auth-token>rc>ini\nnodemon>update-notifier>latest-version>package-json>registry-url>rc>ini\u001b[0m",
"\u001b[33mlow\u001b[0m",
"\u001b[33mhttps://npmjs.com/advisories/1589\u001b[0m",
"\u001b[33mn\u001b[0m"
]
]
]

0 comments on commit e5d19a5

Please sign in to comment.