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
2 changes: 2 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { cmdNpx } from './commands/npx/cmd-npx'
import { cmdOops } from './commands/oops/cmd-oops'
import { cmdOptimize } from './commands/optimize/cmd-optimize'
import { cmdOrganization } from './commands/organization/cmd-organization'
import { cmdPackage } from './commands/package/cmd-package'
import { cmdRawNpm } from './commands/raw-npm/cmd-raw-npm'
import { cmdRawNpx } from './commands/raw-npx/cmd-raw-npx'
import { cmdReport } from './commands/report/cmd-report'
Expand Down Expand Up @@ -61,6 +62,7 @@ void (async () => {
oops: cmdOops,
optimize: cmdOptimize,
organization: cmdOrganization,
package: cmdPackage,
'raw-npm': cmdRawNpm,
'raw-npx': cmdRawNpx,
report: cmdReport,
Expand Down
115 changes: 115 additions & 0 deletions src/commands/package/cmd-package-shallow.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import path from 'node:path'

import { describe, expect } from 'vitest'

import constants from '../../../dist/constants.js'
import { cmdit, invokeNpm } from '../../../test/utils'

const { CLI } = constants

describe('socket package shallow', async () => {
// Lazily access constants.rootBinPath.
const entryPath = path.join(constants.rootBinPath, `${CLI}.js`)

cmdit(
['package', 'shallow', '--help'],
'should support --help',
async cmd => {
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
expect(stdout).toMatchInlineSnapshot(
`
"Look up info regarding one or more packages but not their transitives

Usage
$ socket package shallow <<ecosystem> <name> [<name> ...] | <purl> [<purl> ...]>

Options
--dryRun Do input validation for a command and exit 0 when input is ok
--help Print this help.
--json Output result as json
--markdown Output result as markdown

Requirements
- quota: 100
- scope: \`packages:list\`

Show scoring details for one or more packages purely based on their own package.
This means that any dependency scores are not reflected by the score. You can
use the \`socket package score <pkg>\` command to get its full transitive score.

Only a few ecosystems are supported like npm, golang, and maven.

A "purl" is a standard package name formatting: \`pkg:eco/name@version\`
This command will automatically prepend "pkg:" when not present.

If the first arg is an ecosystem, remaining args that are not a purl are
assumed to be scoped to that ecosystem.

Examples
$ socket package shallow npm webtorrent
$ socket package shallow npm webtorrent@1.9.1
$ socket package shallow npm/webtorrent@1.9.1
$ socket package shallow pkg:npm/webtorrent@1.9.1
$ socket package shallow maven webtorrent babel
$ socket package shallow npm/webtorrent golang/babel
$ socket package shallow npm npm/webtorrent@1.0.1 babel"
`
)
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
"
_____ _ _ /---------------
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
|_____|___|___|_,_|___|_|.dev | Command: \`socket package shallow\`, cwd: <redacted>"
`)

expect(code, 'help should exit with code 2').toBe(2)
expect(
stderr,
'header should include command (without params)'
).toContain('`socket package shallow`')
}
)

cmdit(
['package', 'shallow', '--dry-run'],
'should require args with just dry-run',
async cmd => {
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
expect(stdout).toMatchInlineSnapshot(`""`)
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
"
_____ _ _ /---------------
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
|_____|___|___|_,_|___|_|.dev | Command: \`socket package shallow\`, cwd: <redacted>

\\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[37mInput error\\x1b[39m\\x1b[49m: Please provide the required fields:

- First parameter should be an ecosystem or all args must be purls \\x1b[31m(bad!)\\x1b[39m

- Expecting at least one package \\x1b[31m(missing!)\\x1b[39m"
`)

expect(code, 'dry-run should exit with code 2 if missing input').toBe(2)
}
)

cmdit(
['package', 'shallow', 'npm', 'babel', '--dry-run'],
'should require args with just dry-run',
async cmd => {
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`)
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
"
_____ _ _ /---------------
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
|_____|___|___|_,_|___|_|.dev | Command: \`socket package shallow\`, cwd: <redacted>"
`)

expect(code, 'dry-run should exit with code 0 if input ok').toBe(0)
}
)
})
110 changes: 110 additions & 0 deletions src/commands/package/cmd-package-shallow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import colors from 'yoctocolors-cjs'

import { logger } from '@socketsecurity/registry/lib/logger'

import { parsePackageSpecifiers } from './parse-package-specifiers'
import { showPurlInfo } from './show-purl-info'
import constants from '../../constants'
import { commonFlags, outputFlags } from '../../flags'
import { meowOrExit } from '../../utils/meow-with-subcommands'
import { getFlagListOutput } from '../../utils/output-formatting'

import type { CliCommandConfig } from '../../utils/meow-with-subcommands'

const { DRY_RUN_BAIL_TEXT } = constants

const config: CliCommandConfig = {
commandName: 'shallow',
description:
'Look up info regarding one or more packages but not their transitives',
hidden: true,
flags: {
...commonFlags,
...outputFlags
},
help: (command, config) => `
Usage
$ ${command} <<ecosystem> <name> [<name> ...] | <purl> [<purl> ...]>

Options
${getFlagListOutput(config.flags, 6)}

Requirements
- quota: 100
- scope: \`packages:list\`

Show scoring details for one or more packages purely based on their own package.
This means that any dependency scores are not reflected by the score. You can
use the \`socket package score <pkg>\` command to get its full transitive score.

Only a few ecosystems are supported like npm, golang, and maven.

A "purl" is a standard package name formatting: \`pkg:eco/name@version\`
This command will automatically prepend "pkg:" when not present.

If the first arg is an ecosystem, remaining args that are not a purl are
assumed to be scoped to that ecosystem.

Examples
$ ${command} npm webtorrent
$ ${command} npm webtorrent@1.9.1
$ ${command} npm/webtorrent@1.9.1
$ ${command} pkg:npm/webtorrent@1.9.1
$ ${command} maven webtorrent babel
$ ${command} npm/webtorrent golang/babel
$ ${command} npm npm/webtorrent@1.0.1 babel
`
}

export const cmdPackageShallow = {
description: config.description,
hidden: config.hidden,
alias: {
shallowScore: {
description: config.description,
hidden: true,
argv: []
}
},
run
}

async function run(
argv: string[] | readonly string[],
importMeta: ImportMeta,
{ parentName }: { parentName: string }
): Promise<void> {
const cli = meowOrExit({
argv,
config,
importMeta,
parentName
})

const { json, markdown } = cli.flags
const [ecosystem = '', ...pkgs] = cli.input

const { purls, valid } = parsePackageSpecifiers(ecosystem, pkgs)

if (!valid || !purls.length) {
// Use exit status of 2 to indicate incorrect usage, generally invalid
// options or missing arguments.
// https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
process.exitCode = 2
logger.fail(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
- First parameter should be an ecosystem or all args must be purls ${!valid ? colors.red('(bad!)') : colors.green('(ok)')}\n
- Expecting at least one package ${!purls.length ? colors.red('(missing!)') : colors.green('(ok)')}\n
`)
return
}

if (cli.flags['dryRun']) {
logger.log(DRY_RUN_BAIL_TEXT)
return
}

await showPurlInfo({
outputKind: json ? 'json' : markdown ? 'markdown' : 'text',
purls
})
}
47 changes: 47 additions & 0 deletions src/commands/package/cmd-package.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import path from 'node:path'

import { describe, expect } from 'vitest'

import constants from '../../../dist/constants.js'
import { cmdit, invokeNpm } from '../../../test/utils'

const { CLI } = constants

describe('socket package', async () => {
// Lazily access constants.rootBinPath.
const entryPath = path.join(constants.rootBinPath, `${CLI}.js`)

cmdit(['package', '--help'], 'should support --help', async cmd => {
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
expect(stdout).toMatchInlineSnapshot(
`
"Commands relating to looking up published packages

Usage
$ socket package <command>

Commands


Options
--dryRun Do input validation for a command and exit 0 when input is ok
--help Print this help.

Examples
$ socket package --help"
`
)
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
"
_____ _ _ /---------------
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
|_____|___|___|_,_|___|_|.dev | Command: \`socket package\`, cwd: <redacted>"
`)

expect(code, 'help should exit with code 2').toBe(2)
expect(stderr, 'header should include command (without params)').toContain(
'`socket package`'
)
})
})
31 changes: 31 additions & 0 deletions src/commands/package/cmd-package.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { cmdPackageShallow } from './cmd-package-shallow'
import { meowWithSubcommands } from '../../utils/meow-with-subcommands'

import type { CliSubcommand } from '../../utils/meow-with-subcommands'

const description = 'Commands relating to looking up published packages'

export const cmdPackage: CliSubcommand = {
description,
hidden: true, // [beta]
async run(argv, importMeta, { parentName }) {
await meowWithSubcommands(
{
shallow: cmdPackageShallow
},
{
aliases: {
pkg: {
description,
hidden: true,
argv: []
}
},
argv,
description,
importMeta,
name: parentName + ' package'
}
)
}
}
47 changes: 47 additions & 0 deletions src/commands/package/fetch-package-info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { logger } from '@socketsecurity/registry/lib/logger'

import constants from '../../constants'
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'
import { getPublicToken, setupSdk } from '../../utils/sdk'

import type {
SocketSdkResultType,
SocketSdkReturnType
} from '@socketsecurity/sdk'

export async function fetchPackageInfo(
purls: string[]
): Promise<SocketSdkReturnType<'batchPackageFetch'>> {
const socketSdk = await setupSdk(getPublicToken())

// Lazily access constants.spinner.
const { spinner } = constants

logger.error(
`Requesting shallow score data for ${purls.length} package urls (purl): ${purls.join(', ')}`
)
spinner.start(`Requesting data ...`)

const result: Awaited<SocketSdkResultType<'batchPackageFetch'>> =
await handleApiCall(
socketSdk.batchPackageFetch(
{
alerts: 'true'
// compact: false,
// fixable: false,
// licenseattrib: false,
// licensedetails: false
},
{ components: purls.map(purl => ({ purl })) }
),
'looking up package'
)

spinner.successAndStop('Request completed')

if (result.success) {
return result
} else {
handleUnsuccessfulApiResponse('batchPackageFetch', result)
}
}
Loading
Loading