diff --git a/dist/index.cjs b/dist/index.cjs index f3c14cda..062e41ad 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -10367,7 +10367,7 @@ function splitPackageName(packageName) { } // lib/aggregateReport.js -var byNameAndVersion = (a, b) => { +function byNameAndVersion(a, b) { const res = a.name.localeCompare(b.name); if (res > 0) { return 1; @@ -10376,41 +10376,42 @@ var byNameAndVersion = (a, b) => { return -1; } return semverToNumber(a.version) - semverToNumber(b.version); -}; +} +function getAuditInfo(audit2, name) { + const info4 = audit2.get(name); + const severity = info4 == null ? null : capitalize(info4.severity); + const title = info4 == null ? null : info4.title; + const url = info4 == null ? null : info4.url; + return { severity, title, url }; +} async function aggregateReport(audit2, beforePackages, afterPackages) { const added = []; - afterPackages.forEach((version2, name) => { - if (!beforePackages.has(name)) { - const pkg = splitPackageName(name); - added.push({ name: pkg.name, location: pkg.location, version: version2 }); + afterPackages.forEach((version2, pkgName) => { + if (!beforePackages.has(pkgName)) { + const { name, location } = splitPackageName(pkgName); + added.push({ name, location, version: version2 }); } }); added.sort(byNameAndVersion); const removed = []; - beforePackages.forEach((version2, name) => { - if (!afterPackages.has(name)) { - const pkg = splitPackageName(name); - removed.push({ name: pkg.name, location: pkg.location, version: version2 }); + beforePackages.forEach((version2, pkgName) => { + if (!afterPackages.has(pkgName)) { + const { name, location } = splitPackageName(pkgName); + removed.push({ name, location, version: version2, ...getAuditInfo(audit2, name) }); } }); removed.sort(byNameAndVersion); const updated = []; - afterPackages.forEach((version2, name) => { - const previousVersion = beforePackages.get(name); + afterPackages.forEach((version2, pkgName) => { + const previousVersion = beforePackages.get(pkgName); if (version2 !== previousVersion && previousVersion != null) { - const pkg = splitPackageName(name); - const info4 = audit2.get(pkg.name); - const severity = info4 == null ? null : capitalize(info4.severity); - const title = info4 == null ? null : info4.title; - const url = info4 == null ? null : info4.url; + const { name, location } = splitPackageName(pkgName); updated.push({ - name: pkg.name, - location: pkg.location, + name, + location, version: version2, previousVersion, - severity, - title, - url + ...getAuditInfo(audit2, name) }); } }); @@ -10442,15 +10443,13 @@ async function audit(execFn = import_exec2.getExecOutput) { }); const { vulnerabilities } = JSON.parse(stdout); if (vulnerabilities != null && typeof vulnerabilities === "object") { - const map = ( - /** @type {AuditReport} */ - /* @__PURE__ */ new Map() - ); - Object.values(vulnerabilities).forEach(({ name, severity, via }) => { + const map = /* @__PURE__ */ new Map(); + for (const { name, severity, via } of Object.values(vulnerabilities)) { if (Array.isArray(via)) { const [viaFirst] = via; - if (typeof viaFirst.title === "string" && typeof viaFirst.url === "string") { - map.set(name, { name, severity, title: viaFirst.title, url: viaFirst.url }); + const { title, url } = viaFirst; + if (typeof title === "string" && typeof url === "string") { + map.set(name, { name, severity, title, url }); } else if (typeof viaFirst === "string") { } else { throw new Error(`"via" of "${name}" is invalid: ${JSON.stringify(via)}`); @@ -10458,7 +10457,7 @@ async function audit(execFn = import_exec2.getExecOutput) { } else { throw new Error('"via" is not an array'); } - }); + } return map; } throw new Error('"vulnerabilities" is missing'); @@ -10480,21 +10479,24 @@ async function auditFix() { } // lib/buildCommitBody.js -function buildCommitBody(report) { +function buildCommitBody({ updated, added, removed }) { const lines = []; lines.push("Summary:"); - lines.push(`- Updated packages: ${report.updated.length}`); - lines.push(`- Added packages: ${report.added.length}`); - lines.push(`- Removed packages: ${report.removed.length}`); + lines.push(`- Updated packages: ${updated.length}`); + lines.push(`- Added packages: ${added.length}`); + lines.push(`- Removed packages: ${removed.length}`); lines.push(""); - if (report.updated.length > 0) { - lines.push("Fixed vulnerabilities:"); - const vulnerabilities = /* @__PURE__ */ new Set(); - report.updated.forEach(({ name, severity, title, url }) => { + const vulnerabilities = /* @__PURE__ */ new Set(); + for (const entry of [...updated, ...added, ...removed]) { + if ("severity" in entry && "title" in entry && "url" in entry) { + const { name, severity, title, url } = entry; if (severity != null && title != null && url != null) { vulnerabilities.add(`- ${name}: "${title}" (${url})`); } - }); + } + } + if (vulnerabilities.size > 0) { + lines.push("Fixed vulnerabilities:"); lines.push(...Array.from(vulnerabilities)); } else { lines.push("No fixed vulnerabilities."); @@ -10523,6 +10525,12 @@ var repoLink = (report, name) => { return url ? `[${url.type}](${url.url})` : EMPTY; }; var versionLabel = (version2) => `\`${version2}\``; +var detail = ({ severity, title, url }) => { + if (severity != null && title != null && url != null) { + return `**[${severity}]** ${title} ([ref](${url}))`; + } + return EMPTY; +}; function buildPullRequestBody(report, npmVersion) { const header = []; header.push("| Package | Version | Source | Detail |"); @@ -10538,16 +10546,12 @@ function buildPullRequestBody(report, npmVersion) { lines.push(""); lines.push(...header); report.updated.forEach(({ name, version: version2, location, previousVersion, severity, title, url }) => { - let extra = EMPTY; - if (severity != null && title != null && url != null) { - extra = `**[${severity}]** ${title} ([ref](${url}))`; - } lines.push( buildTableRow( npmPackage(name, version2, location), `${versionLabel(previousVersion)} \u2192 ${versionLabel(version2)}`, repoLink(report, name), - extra + detail({ severity, title, url }) ) ); }); @@ -10566,7 +10570,7 @@ function buildPullRequestBody(report, npmVersion) { npmPackage(name, version2, location), versionLabel(version2), repoLink(report, name), - EMPTY + detail({}) ) ); }); @@ -10579,13 +10583,13 @@ function buildPullRequestBody(report, npmVersion) { lines.push(`Removed (${report.removed.length})`); lines.push(""); lines.push(...header); - report.removed.forEach(({ name, version: version2, location }) => { + report.removed.forEach(({ name, version: version2, location, severity, title, url }) => { lines.push( buildTableRow( npmPackage(name, version2, location), versionLabel(version2), repoLink(report, name), - EMPTY + detail({ severity, title, url }) ) ); }); @@ -10766,10 +10770,11 @@ async function run() { await (0, import_exec7.exec)("npm", npmArgs("ci")); }); const afterPackages = await core2.group("List packages after", () => listPackages()); - const report = await core2.group( - "Aggregate report", - () => aggregateReport(auditReport, beforePackages, afterPackages) - ); + const report = await core2.group("Aggregate report", async () => { + const res = await aggregateReport(auditReport, beforePackages, afterPackages); + core2.info(JSON.stringify(res, null, 2)); + return res; + }); if (report.packageCount === 0) { core2.info("No update."); return; diff --git a/lib/__tests__/__snapshots__/aggregateReport.test.js.snap b/lib/__tests__/__snapshots__/aggregateReport.test.js.snap index 223fa31b..f5fa977b 100644 --- a/lib/__tests__/__snapshots__/aggregateReport.test.js.snap +++ b/lib/__tests__/__snapshots__/aggregateReport.test.js.snap @@ -36,11 +36,17 @@ exports[`aggregateReport() 1`] = ` { "location": "svgo/node_modules/css-select", "name": "css-select", + "severity": null, + "title": null, + "url": null, "version": "3.1.2", }, { "location": null, "name": "xmldom", + "severity": "Low", + "title": "Misinterpretation of malicious XML input", + "url": "https://npmjs.com/advisories/1650", "version": "0.5.0", }, ], diff --git a/lib/aggregateReport.js b/lib/aggregateReport.js index d5f9bc54..a09c40bf 100644 --- a/lib/aggregateReport.js +++ b/lib/aggregateReport.js @@ -8,7 +8,7 @@ import splitPackageName from "./utils/splitPackageName.js"; * @param {{ name: string, version: string }} b * @returns {number} */ -const byNameAndVersion = (a, b) => { +function byNameAndVersion(a, b) { const res = a.name.localeCompare(b.name); if (res > 0) { return 1; @@ -17,7 +17,20 @@ const byNameAndVersion = (a, b) => { return -1; } return semverToNumber(a.version) - semverToNumber(b.version); -}; +} + +/** + * @param {AuditReport} audit + * @param {string} name + * @returns {{ severity: string | null, title: string | null, url: string | null }} + */ +function getAuditInfo(audit, name) { + const info = audit.get(name); + const severity = info == null ? null : capitalize(info.severity); + const title = info == null ? null : info.title; + const url = info == null ? null : info.url; + return { severity, title, url }; +} /** * @param {AuditReport} audit @@ -28,42 +41,36 @@ const byNameAndVersion = (a, b) => { export default async function aggregateReport(audit, beforePackages, afterPackages) { /** @type {Report["added"]} */ const added = []; - afterPackages.forEach((version, name) => { - if (!beforePackages.has(name)) { - const pkg = splitPackageName(name); - added.push({ name: pkg.name, location: pkg.location, version }); + afterPackages.forEach((version, pkgName) => { + if (!beforePackages.has(pkgName)) { + const { name, location } = splitPackageName(pkgName); + added.push({ name, location, version }); } }); added.sort(byNameAndVersion); /** @type {Report["removed"]} */ const removed = []; - beforePackages.forEach((version, name) => { - if (!afterPackages.has(name)) { - const pkg = splitPackageName(name); - removed.push({ name: pkg.name, location: pkg.location, version }); + beforePackages.forEach((version, pkgName) => { + if (!afterPackages.has(pkgName)) { + const { name, location } = splitPackageName(pkgName); + removed.push({ name, location, version, ...getAuditInfo(audit, name) }); } }); removed.sort(byNameAndVersion); /** @type {Report["updated"]} */ const updated = []; - afterPackages.forEach((version, name) => { - const previousVersion = beforePackages.get(name); + afterPackages.forEach((version, pkgName) => { + const previousVersion = beforePackages.get(pkgName); if (version !== previousVersion && previousVersion != null) { - const pkg = splitPackageName(name); - const info = audit.get(pkg.name); - const severity = info == null ? null : capitalize(info.severity); - const title = info == null ? null : info.title; - const url = info == null ? null : info.url; + const { name, location } = splitPackageName(pkgName); updated.push({ - name: pkg.name, - location: pkg.location, + name, + location, version, previousVersion, - severity, - title, - url, + ...getAuditInfo(audit, name), }); } }); diff --git a/lib/audit.js b/lib/audit.js index ca0b5019..e3ff22ce 100644 --- a/lib/audit.js +++ b/lib/audit.js @@ -12,13 +12,15 @@ export default async function audit(execFn = getExecOutput) { const { vulnerabilities } = JSON.parse(stdout); if (vulnerabilities != null && typeof vulnerabilities === "object") { - const map = /** @type {AuditReport} */ new Map(); + /** @type {AuditReport} */ + const map = new Map(); - Object.values(vulnerabilities).forEach(({ name, severity, via }) => { + for (const { name, severity, via } of Object.values(vulnerabilities)) { if (Array.isArray(via)) { const [viaFirst] = via; - if (typeof viaFirst.title === "string" && typeof viaFirst.url === "string") { - map.set(name, { name, severity, title: viaFirst.title, url: viaFirst.url }); + const { title, url } = viaFirst; + if (typeof title === "string" && typeof url === "string") { + map.set(name, { name, severity, title, url }); } else if (typeof viaFirst === "string") { // ignore } else { @@ -27,7 +29,7 @@ export default async function audit(execFn = getExecOutput) { } else { throw new Error('"via" is not an array'); } - }); + } return map; } diff --git a/lib/buildCommitBody.js b/lib/buildCommitBody.js index 1cf6c3be..6fb9c749 100644 --- a/lib/buildCommitBody.js +++ b/lib/buildCommitBody.js @@ -2,26 +2,31 @@ * @param {Report} report * @returns {string} */ -export default function buildCommitBody(report) { +export default function buildCommitBody({ updated, added, removed }) { /** @type {string[]} */ const lines = []; lines.push("Summary:"); - lines.push(`- Updated packages: ${report.updated.length}`); - lines.push(`- Added packages: ${report.added.length}`); - lines.push(`- Removed packages: ${report.removed.length}`); + lines.push(`- Updated packages: ${updated.length}`); + lines.push(`- Added packages: ${added.length}`); + lines.push(`- Removed packages: ${removed.length}`); lines.push(""); - if (report.updated.length > 0) { - lines.push("Fixed vulnerabilities:"); - /** @type {Set} */ - const vulnerabilities = new Set(); - report.updated.forEach(({ name, severity, title, url }) => { + /** @type {Set} */ + const vulnerabilities = new Set(); + + for (const entry of [...updated, ...added, ...removed]) { + if ("severity" in entry && "title" in entry && "url" in entry) { + const { name, severity, title, url } = entry; if (severity != null && title != null && url != null) { vulnerabilities.add(`- ${name}: "${title}" (${url})`); } - }); + } + } + + if (vulnerabilities.size > 0) { + lines.push("Fixed vulnerabilities:"); lines.push(...Array.from(vulnerabilities)); } else { lines.push("No fixed vulnerabilities."); diff --git a/lib/buildPullRequestBody.js b/lib/buildPullRequestBody.js index e25fdc41..c7b03670 100644 --- a/lib/buildPullRequestBody.js +++ b/lib/buildPullRequestBody.js @@ -34,6 +34,20 @@ const repoLink = (report, name) => { */ const versionLabel = (version) => `\`${version}\``; +/** + * @param {{ + * severity?: string | null | undefined, + * title?: string | null | undefined, + * url?: string | null | undefined, + * }} params + */ +const detail = ({ severity, title, url }) => { + if (severity != null && title != null && url != null) { + return `**[${severity}]** ${title} ([ref](${url}))`; + } + return EMPTY; +}; + /** * @param {Report} report * @param {string} npmVersion @@ -57,16 +71,12 @@ export default function buildPullRequestBody(report, npmVersion) { lines.push(...header); report.updated.forEach(({ name, version, location, previousVersion, severity, title, url }) => { - let extra = EMPTY; - if (severity != null && title != null && url != null) { - extra = `**[${severity}]** ${title} ([ref](${url}))`; - } lines.push( buildTableRow( npmPackage(name, version, location), `${versionLabel(previousVersion)} → ${versionLabel(version)}`, repoLink(report, name), - extra + detail({ severity, title, url }) ) ); }); @@ -87,7 +97,7 @@ export default function buildPullRequestBody(report, npmVersion) { npmPackage(name, version, location), versionLabel(version), repoLink(report, name), - EMPTY + detail({}) ) ); }); @@ -101,13 +111,13 @@ export default function buildPullRequestBody(report, npmVersion) { lines.push(`Removed (${report.removed.length})`); lines.push(""); lines.push(...header); - report.removed.forEach(({ name, version, location }) => { + report.removed.forEach(({ name, version, location, severity, title, url }) => { lines.push( buildTableRow( npmPackage(name, version, location), versionLabel(version), repoLink(report, name), - EMPTY + detail({ severity, title, url }) ) ); }); diff --git a/lib/index.js b/lib/index.js index 6957c2c4..ba25fcf4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -60,9 +60,11 @@ async function run() { const afterPackages = await core.group("List packages after", () => listPackages()); - const report = await core.group("Aggregate report", () => - aggregateReport(auditReport, beforePackages, afterPackages) - ); + const report = await core.group("Aggregate report", async () => { + const res = await aggregateReport(auditReport, beforePackages, afterPackages); + core.info(JSON.stringify(res, null, 2)); + return res; + }); if (report.packageCount === 0) { core.info("No update."); diff --git a/lib/types.d.ts b/lib/types.d.ts index dbced44c..a3e92ff4 100644 --- a/lib/types.d.ts +++ b/lib/types.d.ts @@ -31,17 +31,16 @@ type UrlInfo = { name: string; url: string; type: string }; type PackageInfo = { name: string; version: string; location: string | null }; +type PackageInfoWithAudit = PackageInfo & { + severity: string | null; + title: string | null; + url: string | null; +}; + type Report = { added: Array; - removed: Array; - updated: Array< - PackageInfo & { - previousVersion: string; - severity: string | null; - title: string | null; - url: string | null; - } - >; + removed: Array; + updated: Array; packageCount: number; packageUrls: Record; };