Skip to content

Commit 7ab93cc

Browse files
authored
Refactor the scan commands (#322)
1 parent c6a6695 commit 7ab93cc

18 files changed

+797
-811
lines changed

src/cli.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { rawNpmCommand } from './commands/raw-npm'
2626
import { rawNpxCommand } from './commands/raw-npx'
2727
import { reportCommand } from './commands/report'
2828
import { reposCommand } from './commands/repos'
29-
import { scanCommand } from './commands/scan'
29+
import { cmdScan } from './commands/scan/cmd-scan.ts'
3030
import { threatFeedCommand } from './commands/threat-feed'
3131
import { wrapperCommand } from './commands/wrapper'
3232
import constants from './constants'
@@ -61,7 +61,7 @@ void (async () => {
6161
'raw-npx': rawNpxCommand,
6262
report: reportCommand,
6363
wrapper: wrapperCommand,
64-
scan: scanCommand,
64+
scan: cmdScan,
6565
'audit-log': auditLogCommand,
6666
repos: reposCommand,
6767
dependencies: dependenciesCommand,

src/commands/scan/cmd-create.ts

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
import process from 'node:process'
2+
3+
import meowOrDie from 'meow'
4+
import colors from 'yoctocolors-cjs'
5+
6+
import { Spinner } from '@socketsecurity/registry/lib/spinner'
7+
8+
import { createFullScan } from './create-full-scan.ts'
9+
import { handleUnsuccessfulApiResponse } from '../../utils/api'
10+
import { AuthError } from '../../utils/errors'
11+
import { getFlagListOutput } from '../../utils/output-formatting'
12+
import { getPackageFilesFullScans } from '../../utils/path-resolve'
13+
import { getDefaultToken, setupSdk } from '../../utils/sdk'
14+
15+
import type { CliCommandConfig } from '../../utils/meow-with-subcommands'
16+
17+
const config: CliCommandConfig = {
18+
commandName: 'create',
19+
description: 'Create a scan',
20+
hidden: false,
21+
flags: {
22+
repo: {
23+
type: 'string',
24+
shortFlag: 'r',
25+
default: '',
26+
description: 'Repository name'
27+
},
28+
branch: {
29+
type: 'string',
30+
shortFlag: 'b',
31+
default: '',
32+
description: 'Branch name'
33+
},
34+
commitMessage: {
35+
type: 'string',
36+
shortFlag: 'm',
37+
default: '',
38+
description: 'Commit message'
39+
},
40+
commitHash: {
41+
type: 'string',
42+
shortFlag: 'ch',
43+
default: '',
44+
description: 'Commit hash'
45+
},
46+
pullRequest: {
47+
type: 'number',
48+
shortFlag: 'pr',
49+
description: 'Commit hash'
50+
},
51+
committers: {
52+
type: 'string',
53+
shortFlag: 'c',
54+
default: '',
55+
description: 'Committers'
56+
},
57+
defaultBranch: {
58+
type: 'boolean',
59+
shortFlag: 'db',
60+
default: false,
61+
description: 'Make default branch'
62+
},
63+
pendingHead: {
64+
type: 'boolean',
65+
shortFlag: 'ph',
66+
default: false,
67+
description: 'Set as pending head'
68+
},
69+
tmp: {
70+
type: 'boolean',
71+
shortFlag: 't',
72+
default: false,
73+
description:
74+
'Set the visibility (true/false) of the scan in your dashboard'
75+
}
76+
},
77+
help: (parentName, config) => `
78+
Usage
79+
$ ${parentName} ${config.commandName} [...options] <org>
80+
81+
Options
82+
${getFlagListOutput(config.flags, 6)}
83+
84+
Examples
85+
$ ${parentName} ${config.commandName} --org=FakeOrg --repo=test-repo --branch=main ./package.json
86+
`
87+
}
88+
89+
export const cmdScanCreate = {
90+
description: config.description,
91+
hidden: config.hidden,
92+
run
93+
}
94+
95+
async function run(
96+
argv: readonly string[],
97+
importMeta: ImportMeta,
98+
{ parentName }: { parentName: string }
99+
): Promise<void> {
100+
const cli = meowOrDie(config.help(parentName, config), {
101+
argv,
102+
description: config.description,
103+
importMeta,
104+
flags: config.flags
105+
})
106+
107+
const orgSlug = cli.input[0] ?? '' // TODO: if nobody uses this then get rid of it in favor of --org
108+
const cwd = process.cwd()
109+
110+
const socketSdk = await setupSdk()
111+
const supportedFiles = await socketSdk
112+
.getReportSupportedFiles()
113+
.then(res => {
114+
if (!res.success)
115+
handleUnsuccessfulApiResponse(
116+
'getReportSupportedFiles',
117+
res,
118+
new Spinner()
119+
)
120+
// TODO: verify type at runtime? Consider it trusted data and assume type?
121+
return (res as any).data
122+
})
123+
.catch((cause: Error) => {
124+
throw new Error('Failed getting supported files for report', { cause })
125+
})
126+
127+
const packagePaths = await getPackageFilesFullScans(
128+
cwd,
129+
cli.input,
130+
supportedFiles
131+
)
132+
133+
const { branch: branchName, repo: repoName } = cli.flags
134+
135+
if (!orgSlug || !repoName || !branchName || !packagePaths.length) {
136+
console.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
137+
- Org name as the argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
138+
- Repository name using --repo ${!repoName ? colors.red('(missing!)') : colors.green('(ok)')}\n
139+
- Branch name using --branch ${!branchName ? colors.red('(missing!)') : colors.green('(ok)')}\n
140+
- At least one file path (e.g. ./package.json) ${!packagePaths.length ? colors.red('(missing or no matching/supported files found!)') : colors.green('(ok)')}`)
141+
config.help(parentName, config)
142+
return
143+
}
144+
145+
const apiToken = getDefaultToken()
146+
if (!apiToken) {
147+
throw new AuthError(
148+
'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
149+
)
150+
}
151+
152+
await createFullScan({
153+
apiToken,
154+
orgSlug,
155+
repoName: repoName as string,
156+
branchName: branchName as string,
157+
commitMessage: (cli.flags['commitMessage'] as string) ?? '',
158+
defaultBranch: Boolean(cli.flags['defaultBranch']),
159+
pendingHead: Boolean(cli.flags['pendingHead']),
160+
tmp: Boolean(cli.flags['tmp']),
161+
packagePaths,
162+
commitHash: (cli.flags['commitHash'] as string) ?? '',
163+
committers: (cli.flags['committers'] as string) ?? '',
164+
pullRequest: (cli.flags['pullRequest'] as number) ?? undefined
165+
})
166+
}

src/commands/scan/cmd-delete.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import meow from 'meow'
2+
import colors from 'yoctocolors-cjs'
3+
4+
import { deleteOrgFullScan } from './delete-full-scan.ts'
5+
import { commonFlags, outputFlags } from '../../flags'
6+
import { AuthError } from '../../utils/errors'
7+
import { getFlagListOutput } from '../../utils/output-formatting'
8+
import { getDefaultToken } from '../../utils/sdk'
9+
10+
import type { CliCommandConfig } from '../../utils/meow-with-subcommands'
11+
12+
const config: CliCommandConfig = {
13+
commandName: 'del',
14+
description: 'Delete a scan',
15+
hidden: false,
16+
flags: {
17+
...commonFlags,
18+
...outputFlags
19+
},
20+
help: (parentName, config) => `
21+
Usage
22+
$ ${parentName} ${config.commandName} <org slug> <scan ID>
23+
24+
Options
25+
${getFlagListOutput(config.flags, 6)}
26+
27+
Examples
28+
$ ${parentName} ${config.commandName} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0
29+
`
30+
}
31+
32+
export const cmdScanDelete = {
33+
description: config.description,
34+
hidden: config.hidden,
35+
run
36+
}
37+
38+
async function run(
39+
argv: readonly string[],
40+
importMeta: ImportMeta,
41+
{ parentName }: { parentName: string }
42+
): Promise<void> {
43+
const cli = meow(config.help(parentName, config), {
44+
argv,
45+
description: config.description,
46+
importMeta,
47+
flags: config.flags
48+
})
49+
50+
const [orgSlug = '', fullScanId = ''] = cli.input
51+
52+
if (!orgSlug || !fullScanId) {
53+
console.error(
54+
`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
55+
- Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n
56+
- Full Scan ID to delete as second argument ${!fullScanId ? colors.red('(missing!)') : colors.green('(ok)')}
57+
`
58+
)
59+
config.help(parentName, config)
60+
return
61+
}
62+
63+
const apiToken = getDefaultToken()
64+
if (!apiToken) {
65+
throw new AuthError(
66+
'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
67+
)
68+
}
69+
70+
await deleteOrgFullScan(orgSlug, fullScanId, apiToken)
71+
}

src/commands/scan/cmd-list.ts

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import meow from 'meow'
2+
import colors from 'yoctocolors-cjs'
3+
4+
import { Spinner } from '@socketsecurity/registry/lib/spinner'
5+
6+
import { listFullScans } from './list-full-scans.ts'
7+
import { commonFlags, outputFlags } from '../../flags'
8+
import { AuthError } from '../../utils/errors'
9+
import { getFlagListOutput } from '../../utils/output-formatting'
10+
import { getDefaultToken } from '../../utils/sdk'
11+
12+
import type {
13+
CliCommandConfig,
14+
CliSubcommand
15+
} from '../../utils/meow-with-subcommands'
16+
17+
const config: CliCommandConfig = {
18+
commandName: 'list',
19+
description: 'List the full scans for an organization',
20+
hidden: false,
21+
flags: {
22+
...commonFlags,
23+
...outputFlags,
24+
sort: {
25+
type: 'string',
26+
shortFlag: 's',
27+
default: 'created_at',
28+
description:
29+
'Sorting option (`name` or `created_at`) - default is `created_at`'
30+
},
31+
direction: {
32+
type: 'string',
33+
shortFlag: 'd',
34+
default: 'desc',
35+
description: 'Direction option (`desc` or `asc`) - Default is `desc`'
36+
},
37+
perPage: {
38+
type: 'number',
39+
shortFlag: 'pp',
40+
default: 30,
41+
description: 'Results per page - Default is 30'
42+
},
43+
page: {
44+
type: 'number',
45+
shortFlag: 'p',
46+
default: 1,
47+
description: 'Page number - Default is 1'
48+
},
49+
fromTime: {
50+
type: 'string',
51+
shortFlag: 'f',
52+
default: '',
53+
description: 'From time - as a unix timestamp'
54+
},
55+
untilTime: {
56+
type: 'string',
57+
shortFlag: 'u',
58+
default: '',
59+
description: 'Until time - as a unix timestamp'
60+
}
61+
},
62+
help: (parentName, config) => `
63+
Usage
64+
$ ${parentName} ${config.commandName} <org slug>
65+
66+
Options
67+
${getFlagListOutput(config.flags, 6)}
68+
69+
Examples
70+
$ ${parentName} ${config.commandName} FakeOrg
71+
`
72+
}
73+
74+
export const cmdScanList: CliSubcommand = {
75+
description: config.description,
76+
hidden: config.hidden,
77+
run
78+
}
79+
80+
async function run(
81+
argv: readonly string[],
82+
importMeta: ImportMeta,
83+
{ parentName }: { parentName: string }
84+
) {
85+
const cli = meow(config.help(parentName, config), {
86+
argv,
87+
description: config.description,
88+
importMeta,
89+
flags: config.flags
90+
})
91+
92+
const orgSlug = cli.input[0]
93+
94+
if (!orgSlug) {
95+
console.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n
96+
- Org name as the argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}`)
97+
config.help(parentName, config)
98+
return
99+
}
100+
101+
const apiToken = getDefaultToken()
102+
if (!apiToken) {
103+
throw new AuthError(
104+
'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.'
105+
)
106+
}
107+
const spinnerText = 'Listing scans... \n'
108+
const spinner = new Spinner({ text: spinnerText }).start()
109+
await listFullScans(
110+
orgSlug,
111+
// TODO: refine this object to what we need
112+
{
113+
outputJson: cli.flags['json'],
114+
outputMarkdown: cli.flags['markdown'],
115+
orgSlug,
116+
sort: cli.flags['sort'],
117+
direction: cli.flags['direction'],
118+
per_page: cli.flags['perPage'],
119+
page: cli.flags['page'],
120+
from_time: cli.flags['fromTime'],
121+
until_time: cli.flags['untilTime']
122+
} as {
123+
outputJson: boolean
124+
outputMarkdown: boolean
125+
orgSlug: string
126+
sort: string
127+
direction: string
128+
per_page: number
129+
page: number
130+
from_time: string
131+
until_time: string
132+
},
133+
spinner,
134+
apiToken
135+
)
136+
}

0 commit comments

Comments
 (0)