Skip to content

Commit 531ab84

Browse files
pvdzjdalton
andcommitted
[package shallow] Add replacement for info command (#369)
* [package score] Add replacement for info command * tests * In light of recent findings, rename to `socket package shallow` --------- Co-authored-by: John-David Dalton <[email protected]>
1 parent 37f1f37 commit 531ab84

11 files changed

+686
-1
lines changed

src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { cmdNpx } from './commands/npx/cmd-npx'
2525
import { cmdOops } from './commands/oops/cmd-oops'
2626
import { cmdOptimize } from './commands/optimize/cmd-optimize'
2727
import { cmdOrganization } from './commands/organization/cmd-organization'
28+
import { cmdPackage } from './commands/package/cmd-package'
2829
import { cmdRawNpm } from './commands/raw-npm/cmd-raw-npm'
2930
import { cmdRawNpx } from './commands/raw-npx/cmd-raw-npx'
3031
import { cmdReport } from './commands/report/cmd-report'
@@ -61,6 +62,7 @@ void (async () => {
6162
oops: cmdOops,
6263
optimize: cmdOptimize,
6364
organization: cmdOrganization,
65+
package: cmdPackage,
6466
'raw-npm': cmdRawNpm,
6567
'raw-npx': cmdRawNpx,
6668
report: cmdReport,
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import path from 'node:path'
2+
3+
import { describe, expect } from 'vitest'
4+
5+
import constants from '../../../dist/constants.js'
6+
import { cmdit, invokeNpm } from '../../../test/utils'
7+
8+
const { CLI } = constants
9+
10+
describe('socket package shallow', async () => {
11+
// Lazily access constants.rootBinPath.
12+
const entryPath = path.join(constants.rootBinPath, `${CLI}.js`)
13+
14+
cmdit(
15+
['package', 'shallow', '--help'],
16+
'should support --help',
17+
async cmd => {
18+
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
19+
expect(stdout).toMatchInlineSnapshot(
20+
`
21+
"Look up info regarding one or more packages but not their transitives
22+
23+
Usage
24+
$ socket package shallow <<ecosystem> <name> [<name> ...] | <purl> [<purl> ...]>
25+
26+
Options
27+
--dryRun Do input validation for a command and exit 0 when input is ok
28+
--help Print this help.
29+
--json Output result as json
30+
--markdown Output result as markdown
31+
32+
Requirements
33+
- quota: 100
34+
- scope: \`packages:list\`
35+
36+
Show scoring details for one or more packages purely based on their own package.
37+
This means that any dependency scores are not reflected by the score. You can
38+
use the \`socket package score <pkg>\` command to get its full transitive score.
39+
40+
Only a few ecosystems are supported like npm, golang, and maven.
41+
42+
A "purl" is a standard package name formatting: \`pkg:eco/name@version\`
43+
This command will automatically prepend "pkg:" when not present.
44+
45+
If the first arg is an ecosystem, remaining args that are not a purl are
46+
assumed to be scoped to that ecosystem.
47+
48+
Examples
49+
$ socket package shallow npm webtorrent
50+
$ socket package shallow npm [email protected]
51+
$ socket package shallow npm/[email protected]
52+
$ socket package shallow pkg:npm/[email protected]
53+
$ socket package shallow maven webtorrent babel
54+
$ socket package shallow npm/webtorrent golang/babel
55+
$ socket package shallow npm npm/[email protected] babel"
56+
`
57+
)
58+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
59+
"
60+
_____ _ _ /---------------
61+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
62+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
63+
|_____|___|___|_,_|___|_|.dev | Command: \`socket package shallow\`, cwd: <redacted>"
64+
`)
65+
66+
expect(code, 'help should exit with code 2').toBe(2)
67+
expect(
68+
stderr,
69+
'header should include command (without params)'
70+
).toContain('`socket package shallow`')
71+
}
72+
)
73+
74+
cmdit(
75+
['package', 'shallow', '--dry-run'],
76+
'should require args with just dry-run',
77+
async cmd => {
78+
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
79+
expect(stdout).toMatchInlineSnapshot(`""`)
80+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
81+
"
82+
_____ _ _ /---------------
83+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
84+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
85+
|_____|___|___|_,_|___|_|.dev | Command: \`socket package shallow\`, cwd: <redacted>
86+
87+
\\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[37mInput error\\x1b[39m\\x1b[49m: Please provide the required fields:
88+
89+
- First parameter should be an ecosystem or all args must be purls \\x1b[31m(bad!)\\x1b[39m
90+
91+
- Expecting at least one package \\x1b[31m(missing!)\\x1b[39m"
92+
`)
93+
94+
expect(code, 'dry-run should exit with code 2 if missing input').toBe(2)
95+
}
96+
)
97+
98+
cmdit(
99+
['package', 'shallow', 'npm', 'babel', '--dry-run'],
100+
'should require args with just dry-run',
101+
async cmd => {
102+
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
103+
expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`)
104+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
105+
"
106+
_____ _ _ /---------------
107+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
108+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
109+
|_____|___|___|_,_|___|_|.dev | Command: \`socket package shallow\`, cwd: <redacted>"
110+
`)
111+
112+
expect(code, 'dry-run should exit with code 0 if input ok').toBe(0)
113+
}
114+
)
115+
})
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import colors from 'yoctocolors-cjs'
2+
3+
import { logger } from '@socketsecurity/registry/lib/logger'
4+
5+
import { parsePackageSpecifiers } from './parse-package-specifiers'
6+
import { showPurlInfo } from './show-purl-info'
7+
import constants from '../../constants'
8+
import { commonFlags, outputFlags } from '../../flags'
9+
import { meowOrExit } from '../../utils/meow-with-subcommands'
10+
import { getFlagListOutput } from '../../utils/output-formatting'
11+
12+
import type { CliCommandConfig } from '../../utils/meow-with-subcommands'
13+
14+
const { DRY_RUN_BAIL_TEXT } = constants
15+
16+
const config: CliCommandConfig = {
17+
commandName: 'shallow',
18+
description:
19+
'Look up info regarding one or more packages but not their transitives',
20+
hidden: true,
21+
flags: {
22+
...commonFlags,
23+
...outputFlags
24+
},
25+
help: (command, config) => `
26+
Usage
27+
$ ${command} <<ecosystem> <name> [<name> ...] | <purl> [<purl> ...]>
28+
29+
Options
30+
${getFlagListOutput(config.flags, 6)}
31+
32+
Requirements
33+
- quota: 100
34+
- scope: \`packages:list\`
35+
36+
Show scoring details for one or more packages purely based on their own package.
37+
This means that any dependency scores are not reflected by the score. You can
38+
use the \`socket package score <pkg>\` command to get its full transitive score.
39+
40+
Only a few ecosystems are supported like npm, golang, and maven.
41+
42+
A "purl" is a standard package name formatting: \`pkg:eco/name@version\`
43+
This command will automatically prepend "pkg:" when not present.
44+
45+
If the first arg is an ecosystem, remaining args that are not a purl are
46+
assumed to be scoped to that ecosystem.
47+
48+
Examples
49+
$ ${command} npm webtorrent
50+
$ ${command} npm [email protected]
51+
$ ${command} npm/[email protected]
52+
$ ${command} pkg:npm/[email protected]
53+
$ ${command} maven webtorrent babel
54+
$ ${command} npm/webtorrent golang/babel
55+
$ ${command} npm npm/[email protected] babel
56+
`
57+
}
58+
59+
export const cmdPackageShallow = {
60+
description: config.description,
61+
hidden: config.hidden,
62+
alias: {
63+
shallowScore: {
64+
description: config.description,
65+
hidden: true,
66+
argv: []
67+
}
68+
},
69+
run
70+
}
71+
72+
async function run(
73+
argv: string[] | readonly string[],
74+
importMeta: ImportMeta,
75+
{ parentName }: { parentName: string }
76+
): Promise<void> {
77+
const cli = meowOrExit({
78+
argv,
79+
config,
80+
importMeta,
81+
parentName
82+
})
83+
84+
const { json, markdown } = cli.flags
85+
const [ecosystem = '', ...pkgs] = cli.input
86+
87+
const { purls, valid } = parsePackageSpecifiers(ecosystem, pkgs)
88+
89+
if (!valid || !purls.length) {
90+
// Use exit status of 2 to indicate incorrect usage, generally invalid
91+
// options or missing arguments.
92+
// https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html
93+
process.exitCode = 2
94+
logger.fail(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
95+
- First parameter should be an ecosystem or all args must be purls ${!valid ? colors.red('(bad!)') : colors.green('(ok)')}\n
96+
- Expecting at least one package ${!purls.length ? colors.red('(missing!)') : colors.green('(ok)')}\n
97+
`)
98+
return
99+
}
100+
101+
if (cli.flags['dryRun']) {
102+
logger.log(DRY_RUN_BAIL_TEXT)
103+
return
104+
}
105+
106+
await showPurlInfo({
107+
outputKind: json ? 'json' : markdown ? 'markdown' : 'text',
108+
purls
109+
})
110+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import path from 'node:path'
2+
3+
import { describe, expect } from 'vitest'
4+
5+
import constants from '../../../dist/constants.js'
6+
import { cmdit, invokeNpm } from '../../../test/utils'
7+
8+
const { CLI } = constants
9+
10+
describe('socket package', async () => {
11+
// Lazily access constants.rootBinPath.
12+
const entryPath = path.join(constants.rootBinPath, `${CLI}.js`)
13+
14+
cmdit(['package', '--help'], 'should support --help', async cmd => {
15+
const { code, stderr, stdout } = await invokeNpm(entryPath, cmd)
16+
expect(stdout).toMatchInlineSnapshot(
17+
`
18+
"Commands relating to looking up published packages
19+
20+
Usage
21+
$ socket package <command>
22+
23+
Commands
24+
25+
26+
Options
27+
--dryRun Do input validation for a command and exit 0 when input is ok
28+
--help Print this help.
29+
30+
Examples
31+
$ socket package --help"
32+
`
33+
)
34+
expect(`\n ${stderr}`).toMatchInlineSnapshot(`
35+
"
36+
_____ _ _ /---------------
37+
| __|___ ___| |_ ___| |_ | Socket.dev CLI ver <redacted>
38+
|__ | . | _| '_| -_| _| | Node: <redacted>, API token set: <redacted>
39+
|_____|___|___|_,_|___|_|.dev | Command: \`socket package\`, cwd: <redacted>"
40+
`)
41+
42+
expect(code, 'help should exit with code 2').toBe(2)
43+
expect(stderr, 'header should include command (without params)').toContain(
44+
'`socket package`'
45+
)
46+
})
47+
})

src/commands/package/cmd-package.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { cmdPackageShallow } from './cmd-package-shallow'
2+
import { meowWithSubcommands } from '../../utils/meow-with-subcommands'
3+
4+
import type { CliSubcommand } from '../../utils/meow-with-subcommands'
5+
6+
const description = 'Commands relating to looking up published packages'
7+
8+
export const cmdPackage: CliSubcommand = {
9+
description,
10+
hidden: true, // [beta]
11+
async run(argv, importMeta, { parentName }) {
12+
await meowWithSubcommands(
13+
{
14+
shallow: cmdPackageShallow
15+
},
16+
{
17+
aliases: {
18+
pkg: {
19+
description,
20+
hidden: true,
21+
argv: []
22+
}
23+
},
24+
argv,
25+
description,
26+
importMeta,
27+
name: parentName + ' package'
28+
}
29+
)
30+
}
31+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { logger } from '@socketsecurity/registry/lib/logger'
2+
3+
import constants from '../../constants'
4+
import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api'
5+
import { getPublicToken, setupSdk } from '../../utils/sdk'
6+
7+
import type {
8+
SocketSdkResultType,
9+
SocketSdkReturnType
10+
} from '@socketsecurity/sdk'
11+
12+
export async function fetchPackageInfo(
13+
purls: string[]
14+
): Promise<SocketSdkReturnType<'batchPackageFetch'>> {
15+
const socketSdk = await setupSdk(getPublicToken())
16+
17+
// Lazily access constants.spinner.
18+
const { spinner } = constants
19+
20+
logger.error(
21+
`Requesting shallow score data for ${purls.length} package urls (purl): ${purls.join(', ')}`
22+
)
23+
spinner.start(`Requesting data ...`)
24+
25+
const result: Awaited<SocketSdkResultType<'batchPackageFetch'>> =
26+
await handleApiCall(
27+
socketSdk.batchPackageFetch(
28+
{
29+
alerts: 'true'
30+
// compact: false,
31+
// fixable: false,
32+
// licenseattrib: false,
33+
// licensedetails: false
34+
},
35+
{ components: purls.map(purl => ({ purl })) }
36+
),
37+
'looking up package'
38+
)
39+
40+
spinner.successAndStop('Request completed')
41+
42+
if (result.success) {
43+
return result
44+
} else {
45+
handleUnsuccessfulApiResponse('batchPackageFetch', result)
46+
}
47+
}

0 commit comments

Comments
 (0)