diff --git a/dist/index.js b/dist/index.js index 4d5d5f9b..09099d7e 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1224,20 +1224,25 @@ module.exports = async function audit(execFn = exec) { if (vulnerabilities != null && typeof vulnerabilities === "object") { const map = /** @type {AuditReport} */ new Map(); + Object.values(vulnerabilities).forEach(({ name, severity, via }) => { if (Array.isArray(via)) { - const [{ title, url }] = via; - if (typeof title === "string" && typeof url === "string") { - map.set(name, { name, severity, title, url }); + const [viaFirst] = via; + if (typeof viaFirst.title === "string" && typeof viaFirst.url === "string") { + map.set(name, { name, severity, title: viaFirst.title, url: viaFirst.url }); + } else if (typeof viaFirst === "string") { + // ignore } else { - throw new Error(`"via" of "${name}" is invalid`); + throw new Error(`"via" of "${name}" is invalid: ${JSON.stringify(via)}`); } } else { throw new Error('"via" is not an array'); } }); + return map; } + throw new Error('"vulnerabilities" is missing'); }; diff --git a/lib/__tests__/audit.test.js b/lib/__tests__/audit.test.js index 92fab889..d51bc1a4 100644 --- a/lib/__tests__/audit.test.js +++ b/lib/__tests__/audit.test.js @@ -1,12 +1,14 @@ const audit = require("../audit"); const auditJson = require("./fixtures/audit.json"); +const auditJson2 = require("./fixtures/audit-2.json"); -test("audit()", async () => { - const exec = jest.fn().mockImplementationOnce((_1, _2, options) => { - options.listeners.stdout(JSON.stringify(auditJson)); +const mockExec = (/** @type {unknown} */ json) => + jest.fn().mockImplementationOnce((_1, _2, options) => { + options.listeners.stdout(JSON.stringify(json)); }); - const packages = await audit(exec); +test("audit()", async () => { + const packages = await audit(mockExec(auditJson)); expect(packages.size).toEqual(1); expect(packages.get("y18n")).toEqual({ name: "y18n", @@ -15,3 +17,14 @@ test("audit()", async () => { url: "https://npmjs.com/advisories/1654", }); }); + +test("audit() - 2", async () => { + const packages = await audit(mockExec(auditJson2)); + expect(packages.size).toEqual(1); + expect(packages.get("underscore.string")).toEqual({ + name: "underscore.string", + severity: "moderate", + title: "Regular Expression Denial of Service", + url: "https://npmjs.com/advisories/745", + }); +}); diff --git a/lib/__tests__/fixtures/audit-2.json b/lib/__tests__/fixtures/audit-2.json new file mode 100644 index 00000000..a4a6de6c --- /dev/null +++ b/lib/__tests__/fixtures/audit-2.json @@ -0,0 +1,72 @@ +{ + "auditReportVersion": 2, + "vulnerabilities": { + "@financial-times/origami-image-set-tools": { + "name": "@financial-times/origami-image-set-tools", + "severity": "moderate", + "via": ["semvish"], + "effects": [], + "range": ">=1.4.2", + "nodes": ["node_modules/@financial-times/origami-image-set-tools"], + "fixAvailable": { + "name": "@financial-times/origami-image-set-tools", + "version": "1.4.1", + "isSemVerMajor": true + } + }, + "semvish": { + "name": "semvish", + "severity": "moderate", + "via": ["underscore.string"], + "effects": ["@financial-times/origami-image-set-tools"], + "range": ">=0.3.0", + "nodes": ["node_modules/semvish"], + "fixAvailable": { + "name": "@financial-times/origami-image-set-tools", + "version": "1.4.1", + "isSemVerMajor": true + } + }, + "underscore.string": { + "name": "underscore.string", + "severity": "moderate", + "via": [ + { + "source": 745, + "name": "underscore.string", + "dependency": "underscore.string", + "title": "Regular Expression Denial of Service", + "url": "https://npmjs.com/advisories/745", + "severity": "moderate", + "range": "<=3.3.4" + } + ], + "effects": ["semvish"], + "range": "<=3.3.4", + "nodes": ["node_modules/underscore.string"], + "fixAvailable": { + "name": "@financial-times/origami-image-set-tools", + "version": "1.4.1", + "isSemVerMajor": true + } + } + }, + "metadata": { + "vulnerabilities": { + "info": 0, + "low": 0, + "moderate": 3, + "high": 0, + "critical": 0, + "total": 3 + }, + "dependencies": { + "prod": 1, + "dev": 617, + "optional": 0, + "peer": 0, + "peerOptional": 0, + "total": 617 + } + } +} diff --git a/lib/audit.js b/lib/audit.js index 00109fcf..efd34ba6 100644 --- a/lib/audit.js +++ b/lib/audit.js @@ -19,19 +19,24 @@ module.exports = async function audit(execFn = exec) { if (vulnerabilities != null && typeof vulnerabilities === "object") { const map = /** @type {AuditReport} */ new Map(); + Object.values(vulnerabilities).forEach(({ name, severity, via }) => { if (Array.isArray(via)) { - const [{ title, url }] = via; - if (typeof title === "string" && typeof url === "string") { - map.set(name, { name, severity, title, url }); + const [viaFirst] = via; + if (typeof viaFirst.title === "string" && typeof viaFirst.url === "string") { + map.set(name, { name, severity, title: viaFirst.title, url: viaFirst.url }); + } else if (typeof viaFirst === "string") { + // ignore } else { - throw new Error(`"via" of "${name}" is invalid`); + throw new Error(`"via" of "${name}" is invalid: ${JSON.stringify(via)}`); } } else { throw new Error('"via" is not an array'); } }); + return map; } + throw new Error('"vulnerabilities" is missing'); };