Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/commands/info/cmd-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ async function run(
parentName
})

const { all, json, markdown, strict } = cli.flags
const [rawPkgName = ''] = cli.input

if (!rawPkgName || cli.input.length > 1) {
Expand All @@ -78,11 +79,10 @@ async function run(

await getPackageInfo({
commandName: `${parentName} ${config.commandName}`,
includeAllIssues: Boolean(cli.flags['all']),
outputJson: Boolean(cli.flags['json']),
outputMarkdown: Boolean(cli.flags['markdown']),
includeAllIssues: Boolean(all),
outputKind: json ? 'json' : markdown ? 'markdown' : 'print',
pkgName,
pkgVersion,
strict: Boolean(cli.flags['strict'])
strict: Boolean(strict)
})
}
17 changes: 13 additions & 4 deletions src/commands/info/fetch-package-info.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
import { Spinner } from '@socketsecurity/registry/lib/spinner'

import { PackageData } from './get-package-info'
import constants from '../../constants'
import { getSeverityCount } from '../../utils/alert/severity'
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'
import { getPublicToken, setupSdk } from '../../utils/sdk'

export async function fetchPackageInfo(
pkgName: string,
pkgVersion: string,
includeAllIssues: boolean,
spinner: Spinner
includeAllIssues: boolean
): Promise<void | PackageData> {
// Lazily access constants.spinner.
const { spinner } = constants

spinner.start(
pkgVersion === 'latest'
? `Looking up data for the latest version of ${pkgName}`
: `Looking up data for version ${pkgVersion} of ${pkgName}`
)

const socketSdk = await setupSdk(getPublicToken())
const result = await handleApiCall(
socketSdk.getIssuesByNPMPackage(pkgName, pkgVersion),
Expand Down Expand Up @@ -42,6 +49,8 @@ export async function fetchPackageInfo(
includeAllIssues ? undefined : 'high'
)

spinner?.successAndStop('Data fetched')

return {
data: result.data,
severityCount,
Expand Down
109 changes: 55 additions & 54 deletions src/commands/info/format-package-info.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import process from 'node:process'

import colors from 'yoctocolors-cjs'

import constants from '@socketsecurity/registry/lib/constants'
import { logger } from '@socketsecurity/registry/lib/logger'
import { Spinner } from '@socketsecurity/registry/lib/spinner'

import { PackageData } from './get-package-info'
import { formatSeverityCount } from '../../utils/alert/severity'
Expand All @@ -23,69 +20,73 @@ export function formatPackageInfo(
{ data, score, severityCount }: PackageData,
{
name,
outputJson,
outputMarkdown,
outputKind,
pkgName,
pkgVersion,
strict
pkgVersion
}: {
includeAllIssues: boolean
outputJson: boolean
outputMarkdown: boolean
name: string
outputKind: 'json' | 'markdown' | 'print'
pkgName: string
pkgVersion: string
strict: boolean
} & { name: string },
spinner: Spinner
}
): void {
if (outputJson) {
if (outputKind === 'json') {
logger.log(JSON.stringify(data, undefined, 2))
return
}

if (outputKind === 'markdown') {
logger.log(`\n# Package report for ${pkgName}\n`)
logger.log('Package report card:\n')
} else {
logger.log('\nPackage report card:')
const scoreResult = {
'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100),
Maintenance: Math.floor(score.maintenance.score * 100),
Quality: Math.floor(score.quality.score * 100),
Vulnerabilities: Math.floor(score.vulnerability.score * 100),
License: Math.floor(score.license.score * 100)
logger.log(`\nPackage report card for ${pkgName}:\n`)
}
const scoreResult = {
'Supply Chain Risk': Math.floor(score.supplyChainRisk.score * 100),
Maintenance: Math.floor(score.maintenance.score * 100),
Quality: Math.floor(score.quality.score * 100),
Vulnerabilities: Math.floor(score.vulnerability.score * 100),
License: Math.floor(score.license.score * 100)
}
Object.entries(scoreResult).map(score =>
logger.log(`- ${score[0]}: ${formatScore(score[1])}`)
)
logger.log('\n')

if (objectSome(severityCount)) {
if (outputKind === 'markdown') {
logger.log('# Issues\n')
}
Object.entries(scoreResult).map(score =>
logger.log(`- ${score[0]}: ${formatScore(score[1])}`)
logger.log(
`Package has these issues: ${formatSeverityCount(severityCount)}\n`
)
logger.log('\n')
if (objectSome(severityCount)) {
spinner[strict ? 'error' : 'success'](
`Package has these issues: ${formatSeverityCount(severityCount)}`
)
formatPackageIssuesDetails(data, outputMarkdown)
} else {
spinner.successAndStop('Package has no issues')
}
formatPackageIssuesDetails(data, outputKind === 'markdown')
} else {
logger.log('Package has no issues')
}

const format = new ColorOrMarkdown(!!outputMarkdown)
const url = getSocketDevPackageOverviewUrl(NPM, pkgName, pkgVersion)
const format = new ColorOrMarkdown(outputKind === 'markdown')
const url = getSocketDevPackageOverviewUrl(NPM, pkgName, pkgVersion)

logger.log('\n')
if (pkgVersion === 'latest') {
logger.log(
`Detailed info on socket.dev: ${format.hyperlink(`${pkgName}`, url, { fallbackToUrl: true })}`
)
} else {
logger.log(
`Detailed info on socket.dev: ${format.hyperlink(`${pkgName} v${pkgVersion}`, url, { fallbackToUrl: true })}`
)
}
if (!outputMarkdown) {
logger.log(
colors.dim(
`\nOr rerun ${colors.italic(name)} using the ${colors.italic('--json')} flag to get full JSON output`
)
)
}
logger.log('\n')
if (pkgVersion === 'latest') {
logger.log(
`Detailed info on socket.dev: ${format.hyperlink(`${pkgName}`, url, { fallbackToUrl: true })}`
)
} else {
logger.log(
`Detailed info on socket.dev: ${format.hyperlink(`${pkgName} v${pkgVersion}`, url, { fallbackToUrl: true })}`
)
}

if (strict && objectSome(severityCount)) {
process.exit(1)
if (outputKind !== 'markdown') {
logger.log(
colors.dim(
`\nOr rerun ${colors.italic(name)} using the ${colors.italic('--json')} flag to get full JSON output`
)
)
} else {
logger.log('')
}
}

Expand Down Expand Up @@ -118,7 +119,7 @@ function formatPackageIssuesDetails(
{}
)

const format = new ColorOrMarkdown(!!outputMarkdown)
const format = new ColorOrMarkdown(outputMarkdown)
for (const issue of Object.keys(uniqueIssues)) {
const issueWithLink = format.hyperlink(
`${uniqueIssues[issue]?.label}`,
Expand Down
48 changes: 19 additions & 29 deletions src/commands/info/get-package-info.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import process from 'node:process'

import { fetchPackageInfo } from './fetch-package-info'
import { formatPackageInfo } from './format-package-info'
import constants from '../../constants'
import { objectSome } from '../../utils/objects'

import type { SocketSdkAlert } from '../../utils/alert/severity'
import type { SocketSdkReturnType } from '@socketsecurity/sdk'
Expand All @@ -14,48 +16,36 @@ export interface PackageData {
export async function getPackageInfo({
commandName,
includeAllIssues,
outputJson,
outputMarkdown,
outputKind,
pkgName,
pkgVersion,
strict
}: {
commandName: string
includeAllIssues: boolean
outputJson: boolean
outputMarkdown: boolean
outputKind: 'json' | 'markdown' | 'print'
pkgName: string
pkgVersion: string
strict: boolean
}) {
// Lazily access constants.spinner.
const { spinner } = constants

spinner.start(
pkgVersion === 'latest'
? `Looking up data for the latest version of ${pkgName}`
: `Looking up data for version ${pkgVersion} of ${pkgName}`
)

const packageData = await fetchPackageInfo(
pkgName,
pkgVersion,
includeAllIssues,
spinner
includeAllIssues
)

if (packageData) {
formatPackageInfo(
packageData,
{
name: commandName,
includeAllIssues,
outputJson,
outputMarkdown,
pkgName,
pkgVersion,
strict
},
spinner
)
formatPackageInfo(packageData, {
name: commandName,
includeAllIssues,
outputKind,
pkgName,
pkgVersion
})

if (strict && objectSome(packageData.severityCount)) {
// Let NodeJS exit gracefully but with exit(1)
process.exitCode = 1
}
}
}
Loading