From c050b0418632650cdde8014410911e7944809ab7 Mon Sep 17 00:00:00 2001 From: Charlie Gerard Date: Mon, 13 Nov 2023 11:30:51 +1100 Subject: [PATCH 1/4] Render issues --- lib/commands/info/index.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/commands/info/index.js b/lib/commands/info/index.js index abee2416..5ded7c78 100644 --- a/lib/commands/info/index.js +++ b/lib/commands/info/index.js @@ -145,6 +145,28 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict if (objectSome(severityCount)) { const issueSummary = formatSeverityCount(severityCount) spinner[strict ? 'fail' : 'succeed'](`Package has these issues: ${issueSummary}`) + + // Return the alert types for critical and high alerts + const issueDetails = result.data.filter(d => d.value?.severity === 'high' || d.value?.severity === 'critical') + const uniqueIssues = issueDetails.reduce((/** @type {{ [key: string]: number }} */ acc, issue) => { + const { type } = issue + if (type) { + let count = 0 + if (!acc[type]) { + count += 1 + acc[type] = count + } else { + acc[type]++ + } + } + return acc + }, {}) + Object.keys(uniqueIssues).map(issue => { + if (uniqueIssues[issue] === 1) { + return console.log(`- ${issue}`) + } + return console.log(`- ${issue}: ${uniqueIssues[issue]}`) + }) } else { spinner.succeed('Package has no issues') } @@ -165,7 +187,7 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict if (outputJson) { console.log(JSON.stringify(data, undefined, 2)) } else { - console.log('\nPackage report card:\n') + console.log('\nPackage report card:') const scoreResult = { 'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100), From 83941c002e7dbc52340e280611eb69f4813dfb1b Mon Sep 17 00:00:00 2001 From: Charlie Gerard Date: Mon, 13 Nov 2023 15:17:44 +1100 Subject: [PATCH 2/4] refactor --- lib/commands/info/index.js | 84 +++++++++++++++++++++----------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/lib/commands/info/index.js b/lib/commands/info/index.js index 5ded7c78..70179796 100644 --- a/lib/commands/info/index.js +++ b/lib/commands/info/index.js @@ -20,10 +20,12 @@ export const info = { const name = parentName + ' info' const input = setupCommand(name, info.description, argv, importMeta) - const packageData = input && await fetchPackageData(input.pkgName, input.pkgVersion, input) - - if (packageData) { - formatPackageDataOutput(packageData, { name, ...input }) + if (input) { + const spinner = ora(`Looking up data for version ${input.pkgVersion} of ${input.pkgName}\n`).start() + const packageData = await fetchPackageData(input.pkgName, input.pkgVersion, input, spinner) + if (packageData) { + formatPackageDataOutput(packageData, { name, ...input }, spinner) + } } } } @@ -121,12 +123,12 @@ function setupCommand (name, description, argv, importMeta) { /** * @param {string} pkgName * @param {string} pkgVersion - * @param {Pick} context + * @param {Pick} context + * @param {import('ora').Ora} spinner * @returns {Promise} */ -async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict }) { +async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues }, spinner) { const socketSdk = await setupSdk(getDefaultKey() || FREE_API_KEY) - const spinner = ora(`Looking up data for version ${pkgVersion} of ${pkgName}`).start() const result = await handleApiCall(socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion), 'looking up package') const scoreResult = await handleApiCall(socketSdk.getScoreByNPMPackage(pkgName, pkgVersion), 'looking up package score') @@ -142,33 +144,8 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict const severityCount = getSeverityCount(result.data, includeAllIssues ? undefined : 'high') - if (objectSome(severityCount)) { - const issueSummary = formatSeverityCount(severityCount) - spinner[strict ? 'fail' : 'succeed'](`Package has these issues: ${issueSummary}`) - - // Return the alert types for critical and high alerts - const issueDetails = result.data.filter(d => d.value?.severity === 'high' || d.value?.severity === 'critical') - const uniqueIssues = issueDetails.reduce((/** @type {{ [key: string]: number }} */ acc, issue) => { - const { type } = issue - if (type) { - let count = 0 - if (!acc[type]) { - count += 1 - acc[type] = count - } else { - acc[type]++ - } - } - return acc - }, {}) - Object.keys(uniqueIssues).map(issue => { - if (uniqueIssues[issue] === 1) { - return console.log(`- ${issue}`) - } - return console.log(`- ${issue}: ${uniqueIssues[issue]}`) - }) - } else { - spinner.succeed('Package has no issues') + if (!objectSome(severityCount)) { + spinner.succeed('\nPackage has no issues') } return { @@ -181,14 +158,14 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict /** * @param {PackageData} packageData * @param {{ name: string } & CommandContext} context + * @param {import('ora').Ora} spinner * @returns {void} */ - function formatPackageDataOutput ({ data, severityCount, score }, { name, outputJson, outputMarkdown, pkgName, pkgVersion, strict }) { + function formatPackageDataOutput ({ data, severityCount, score }, { name, outputJson, outputMarkdown, pkgName, pkgVersion, strict }, spinner) { if (outputJson) { console.log(JSON.stringify(data, undefined, 2)) } else { console.log('\nPackage report card:') - const scoreResult = { 'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100), 'Maintenance': Math.floor(score.maintenance.score * 100), @@ -198,9 +175,15 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict } Object.entries(scoreResult).map(score => console.log(`- ${score[0]}: ${formatScore(score[1])}`)) + // Package issues list + const issueSummary = formatSeverityCount(severityCount) + console.log('\n') + spinner[strict ? 'fail' : 'succeed'](`Package has these issues: ${issueSummary}`) + formatPackageIssuesDetails(data) + + // Link to issues list const format = new ChalkOrMarkdown(!!outputMarkdown) const url = `https://socket.dev/npm/package/${pkgName}/overview/${pkgVersion}` - console.log('\nDetailed info on socket.dev: ' + format.hyperlink(`${pkgName} v${pkgVersion}`, url, { fallbackToUrl: true })) if (!outputMarkdown) { console.log(chalk.dim('\nOr rerun', chalk.italic(name), 'using the', chalk.italic('--json'), 'flag to get full JSON output')) @@ -212,6 +195,33 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues, strict } } +/** + * @param {import('@socketsecurity/sdk').SocketSdkReturnType<'getIssuesByNPMPackage'>["data"]} packageData + * @returns {void[]} + */ +function formatPackageIssuesDetails (packageData) { + const issueDetails = packageData.filter(d => d.value?.severity === 'high' || d.value?.severity === 'critical') + const uniqueIssues = issueDetails.reduce((/** @type {{ [key: string]: number }} */ acc, issue) => { + const { type } = issue + if (type) { + let count = 0 + if (!acc[type]) { + count += 1 + acc[type] = count + } else { + acc[type]++ + } + } + return acc + }, {}) + return Object.keys(uniqueIssues).map(issue => { + if (uniqueIssues[issue] === 1) { + return console.log(`- ${issue}`) + } + return console.log(`- ${issue}: ${uniqueIssues[issue]}`) + }) +} + /** * @param {number} score * @returns {string} From b48d5d31fcaf584ec2d1a03c038a08f0d33baf26 Mon Sep 17 00:00:00 2001 From: Charlie Gerard Date: Mon, 13 Nov 2023 15:26:29 +1100 Subject: [PATCH 3/4] update --- lib/commands/info/index.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/commands/info/index.js b/lib/commands/info/index.js index 70179796..023a909c 100644 --- a/lib/commands/info/index.js +++ b/lib/commands/info/index.js @@ -141,13 +141,8 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues }, spin } // Conclude the status of the API call - const severityCount = getSeverityCount(result.data, includeAllIssues ? undefined : 'high') - if (!objectSome(severityCount)) { - spinner.succeed('\nPackage has no issues') - } - return { data: result.data, severityCount, @@ -176,10 +171,15 @@ async function fetchPackageData (pkgName, pkgVersion, { includeAllIssues }, spin Object.entries(scoreResult).map(score => console.log(`- ${score[0]}: ${formatScore(score[1])}`)) // Package issues list - const issueSummary = formatSeverityCount(severityCount) - console.log('\n') - spinner[strict ? 'fail' : 'succeed'](`Package has these issues: ${issueSummary}`) - formatPackageIssuesDetails(data) + if (objectSome(severityCount)) { + const issueSummary = formatSeverityCount(severityCount) + console.log('\n') + spinner[strict ? 'fail' : 'succeed'](`Package has these issues: ${issueSummary}`) + formatPackageIssuesDetails(data) + } else { + console.log('\n') + spinner.succeed('Package has no issues') + } // Link to issues list const format = new ChalkOrMarkdown(!!outputMarkdown) From 1a0d214865693cf4157e52a08c03a0eec28ede7c Mon Sep 17 00:00:00 2001 From: Charlie Gerard Date: Tue, 14 Nov 2023 06:17:48 +1100 Subject: [PATCH 4/4] small refactor --- lib/commands/info/index.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/commands/info/index.js b/lib/commands/info/index.js index 023a909c..579322f1 100644 --- a/lib/commands/info/index.js +++ b/lib/commands/info/index.js @@ -204,10 +204,8 @@ function formatPackageIssuesDetails (packageData) { const uniqueIssues = issueDetails.reduce((/** @type {{ [key: string]: number }} */ acc, issue) => { const { type } = issue if (type) { - let count = 0 if (!acc[type]) { - count += 1 - acc[type] = count + acc[type] = 1 } else { acc[type]++ }