@socketsecurity/cli 0.9.3 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,245 @@
1
+ /* eslint-disable no-console */
2
+
3
+ import { stdin as inputText, stdout as output } from 'node:process'
4
+ import * as readline from 'node:readline/promises'
5
+
6
+ import chalk from 'chalk'
7
+ import meow from 'meow'
8
+ import open from 'open'
9
+ import ora from 'ora'
10
+ import { ErrorWithCause } from 'pony-cause'
11
+
12
+ import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
13
+ import { prepareFlags } from '../../utils/flags.js'
14
+ import { printFlagList } from '../../utils/formatting.js'
15
+ import { createDebugLogger } from '../../utils/misc.js'
16
+ import { getPackageFilesFullScans } from '../../utils/path-resolve.js'
17
+ import { getDefaultKey, setupSdk } from '../../utils/sdk.js'
18
+
19
+ /** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
20
+ export const create = {
21
+ description: 'Create a scan',
22
+ async run (argv, importMeta, { parentName }) {
23
+ const name = parentName + ' create'
24
+
25
+ const input = await setupCommand(name, create.description, argv, importMeta)
26
+ if (input) {
27
+ const spinnerText = 'Creating a scan... \n'
28
+ const spinner = ora(spinnerText).start()
29
+
30
+ await createFullScan(input, spinner)
31
+ }
32
+ }
33
+ }
34
+
35
+ const createFullScanFlags = prepareFlags({
36
+ repo: {
37
+ type: 'string',
38
+ shortFlag: 'r',
39
+ default: '',
40
+ description: 'Repository name',
41
+ },
42
+ branch: {
43
+ type: 'string',
44
+ shortFlag: 'b',
45
+ default: '',
46
+ description: 'Branch name',
47
+ },
48
+ commitMessage: {
49
+ type: 'string',
50
+ shortFlag: 'm',
51
+ default: '',
52
+ description: 'Commit message',
53
+ },
54
+ commitHash: {
55
+ type: 'string',
56
+ shortFlag: 'ch',
57
+ default: '',
58
+ description: 'Commit hash',
59
+ },
60
+ pullRequest: {
61
+ type: 'number',
62
+ shortFlag: 'pr',
63
+ description: 'Commit hash',
64
+ },
65
+ committers: {
66
+ type: 'string',
67
+ shortFlag: 'c',
68
+ default: '',
69
+ description: 'Committers',
70
+ },
71
+ defaultBranch: {
72
+ type: 'boolean',
73
+ shortFlag: 'db',
74
+ default: false,
75
+ description: 'Make default branch',
76
+ },
77
+ pendingHead: {
78
+ type: 'boolean',
79
+ shortFlag: 'ph',
80
+ default: false,
81
+ description: 'Set as pending head',
82
+ },
83
+ tmp: {
84
+ type: 'boolean',
85
+ shortFlag: 't',
86
+ default: false,
87
+ description: 'Set the visibility (true/false) of the scan in your dashboard',
88
+ }
89
+ })
90
+
91
+ // Internal functions
92
+
93
+ /**
94
+ * @typedef CommandContext
95
+ * @property {string} orgSlug
96
+ * @property {string} repoName
97
+ * @property {string} branchName
98
+ * @property {string} committers
99
+ * @property {string} commitMessage
100
+ * @property {string} commitHash
101
+ * @property {number | undefined} pullRequest
102
+ * @property {boolean} defaultBranch
103
+ * @property {boolean} pendingHead
104
+ * @property {boolean} tmp
105
+ * @property {string[]} packagePaths
106
+ */
107
+
108
+ /**
109
+ * @param {string} name
110
+ * @param {string} description
111
+ * @param {readonly string[]} argv
112
+ * @param {ImportMeta} importMeta
113
+ * @returns {Promise<void|CommandContext>}
114
+ */
115
+ async function setupCommand (name, description, argv, importMeta) {
116
+ const flags = {
117
+ ...createFullScanFlags
118
+ }
119
+
120
+ const cli = meow(`
121
+ Usage
122
+ $ ${name} [...options]
123
+
124
+ Options
125
+ ${printFlagList(flags, 6)}
126
+
127
+ Examples
128
+ $ ${name} --org=FakeOrg --repo=test-repo --branch=main ./package.json
129
+ `, {
130
+ argv,
131
+ description,
132
+ importMeta,
133
+ flags
134
+ })
135
+
136
+ const {
137
+ repo: repoName,
138
+ branch: branchName,
139
+ commitMessage,
140
+ defaultBranch,
141
+ pendingHead,
142
+ tmp,
143
+ committers,
144
+ commitHash,
145
+ pullRequest
146
+ } = cli.flags
147
+
148
+ if (!cli.input[0]) {
149
+ cli.showHelp()
150
+ return
151
+ }
152
+
153
+ const [orgSlug = ''] = cli.input
154
+
155
+ const cwd = process.cwd()
156
+ const socketSdk = await setupSdk()
157
+ const supportedFiles = await socketSdk.getReportSupportedFiles()
158
+ .then(res => {
159
+ if (!res.success) handleUnsuccessfulApiResponse('getReportSupportedFiles', res, ora())
160
+ return res.data
161
+ }).catch(
162
+ /** @type {(cause: Error) => never} */
163
+ (cause) => {
164
+ throw new ErrorWithCause('Failed getting supported files for report', { cause })
165
+ })
166
+ const debugLog = createDebugLogger(false)
167
+ const packagePaths = await getPackageFilesFullScans(cwd, cli.input, supportedFiles, debugLog)
168
+
169
+ if (!repoName || !branchName || !packagePaths.length) {
170
+ console.error(`${chalk.bgRed('Input error')}: Please provide the required fields: \n
171
+ - Repository name using --repo, \n
172
+ - Branch name using --branch \n
173
+ - At least one file path (e.g. ./package.json) .\n`)
174
+ cli.showHelp()
175
+ return
176
+ }
177
+
178
+ return {
179
+ orgSlug,
180
+ repoName,
181
+ branchName,
182
+ commitMessage,
183
+ defaultBranch,
184
+ pendingHead,
185
+ tmp,
186
+ packagePaths,
187
+ commitHash,
188
+ committers,
189
+ pullRequest
190
+ }
191
+ }
192
+
193
+ /**
194
+ * @typedef FullScanData
195
+ * @property {import('@socketsecurity/sdk').SocketSdkReturnType<'CreateOrgFullScan'>["data"]} data
196
+ */
197
+
198
+ /**
199
+ * @param {CommandContext} input
200
+ * @param {import('ora').Ora} spinner
201
+ * @returns {Promise<void|FullScanData>}
202
+ */
203
+ async function createFullScan (input, spinner) {
204
+ const socketSdk = await setupSdk(getDefaultKey())
205
+ const {
206
+ orgSlug,
207
+ repoName,
208
+ branchName,
209
+ commitMessage,
210
+ defaultBranch,
211
+ pendingHead,
212
+ tmp,
213
+ packagePaths
214
+ } = input
215
+
216
+ const result = await handleApiCall(socketSdk.createOrgFullScan(orgSlug, {
217
+ repo: repoName,
218
+ branch: branchName,
219
+ commit_message: commitMessage,
220
+ make_default_branch: defaultBranch,
221
+ set_as_pending_head: pendingHead,
222
+ tmp
223
+ }, packagePaths), 'Creating scan')
224
+
225
+ if (!result.success) {
226
+ return handleUnsuccessfulApiResponse('CreateOrgFullScan', result, spinner)
227
+ }
228
+ spinner.stop()
229
+
230
+ console.log('\n✅ Scan created successfully \n')
231
+ const link = chalk.hex('#00FFFF').underline(`${result.data.html_report_url}`)
232
+ console.log(`Available at: ${link} \n`)
233
+
234
+ const rl = readline.createInterface({ input: inputText, output })
235
+
236
+ const answer = await rl.question('Would you like to open it in your browser? (y/n) ')
237
+
238
+ answer.toLowerCase() === 'y' && open(`${result.data.html_report_url}`)
239
+
240
+ rl.close()
241
+
242
+ return {
243
+ data: result.data
244
+ }
245
+ }
@@ -0,0 +1,112 @@
1
+ /* eslint-disable no-console */
2
+
3
+ import chalk from 'chalk'
4
+ import meow from 'meow'
5
+ import ora from 'ora'
6
+
7
+ import { outputFlags } from '../../flags/index.js'
8
+ import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
9
+ import { printFlagList } from '../../utils/formatting.js'
10
+ import { getDefaultKey, setupSdk } from '../../utils/sdk.js'
11
+
12
+ /** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
13
+ export const del = {
14
+ description: 'Delete a scan',
15
+ async run (argv, importMeta, { parentName }) {
16
+ const name = parentName + ' del'
17
+
18
+ const input = setupCommand(name, del.description, argv, importMeta)
19
+ if (input) {
20
+ const spinnerText = 'Deleting scan...'
21
+ const spinner = ora(spinnerText).start()
22
+ await deleteOrgFullScan(input.orgSlug, input.fullScanId, spinner)
23
+ }
24
+ }
25
+ }
26
+
27
+ // Internal functions
28
+
29
+ /**
30
+ * @typedef CommandContext
31
+ * @property {boolean} outputJson
32
+ * @property {boolean} outputMarkdown
33
+ * @property {string} orgSlug
34
+ * @property {string} fullScanId
35
+ */
36
+
37
+ /**
38
+ * @param {string} name
39
+ * @param {string} description
40
+ * @param {readonly string[]} argv
41
+ * @param {ImportMeta} importMeta
42
+ * @returns {void|CommandContext}
43
+ */
44
+ function setupCommand (name, description, argv, importMeta) {
45
+ const flags = {
46
+ ...outputFlags
47
+ }
48
+
49
+ const cli = meow(`
50
+ Usage
51
+ $ ${name} <org slug> <scan ID>
52
+
53
+ Options
54
+ ${printFlagList(flags, 6)}
55
+
56
+ Examples
57
+ $ ${name} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0
58
+ `, {
59
+ argv,
60
+ description,
61
+ importMeta,
62
+ flags
63
+ })
64
+
65
+ const {
66
+ json: outputJson,
67
+ markdown: outputMarkdown,
68
+ } = cli.flags
69
+
70
+ if (cli.input.length < 2) {
71
+ console.error(`${chalk.bgRed('Input error')}: Please specify an organization slug and a scan ID.\n`)
72
+ cli.showHelp()
73
+ return
74
+ }
75
+
76
+ const [orgSlug = '', fullScanId = ''] = cli.input
77
+
78
+ return {
79
+ outputJson,
80
+ outputMarkdown,
81
+ orgSlug,
82
+ fullScanId
83
+ }
84
+ }
85
+
86
+ /**
87
+ * @typedef FullScanData
88
+ * @property {import('@socketsecurity/sdk').SocketSdkReturnType<'deleteOrgFullScan'>["data"]} data
89
+ */
90
+
91
+ /**
92
+ * @param {string} orgSlug
93
+ * @param {string} fullScanId
94
+ * @param {import('ora').Ora} spinner
95
+ * @returns {Promise<void|FullScanData>}
96
+ */
97
+ async function deleteOrgFullScan (orgSlug, fullScanId, spinner) {
98
+ const socketSdk = await setupSdk(getDefaultKey())
99
+ const result = await handleApiCall(socketSdk.deleteOrgFullScan(orgSlug, fullScanId), 'Deleting scan')
100
+
101
+ if (!result.success) {
102
+ return handleUnsuccessfulApiResponse('deleteOrgFullScan', result, spinner)
103
+ }
104
+
105
+ console.log('\n ✅ Scan deleted successfully. \n')
106
+
107
+ spinner.stop()
108
+
109
+ return {
110
+ data: result.data
111
+ }
112
+ }
@@ -0,0 +1,30 @@
1
+ import { create } from './create.js'
2
+ import { del } from './delete.js'
3
+ import { list } from './list.js'
4
+ import { metadata } from './metadata.js'
5
+ import { stream } from './stream.js'
6
+ import { meowWithSubcommands } from '../../utils/meow-with-subcommands.js'
7
+
8
+ const description = 'Scans related commands'
9
+
10
+ /** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
11
+ export const scan = {
12
+ description,
13
+ run: async (argv, importMeta, { parentName }) => {
14
+ await meowWithSubcommands(
15
+ {
16
+ create,
17
+ stream,
18
+ list,
19
+ del,
20
+ metadata
21
+ },
22
+ {
23
+ argv,
24
+ description,
25
+ importMeta,
26
+ name: parentName + ' scan',
27
+ }
28
+ )
29
+ }
30
+ }
@@ -0,0 +1,192 @@
1
+ /* eslint-disable no-console */
2
+
3
+ import chalk from 'chalk'
4
+ // @ts-ignore
5
+ import chalkTable from 'chalk-table'
6
+ import meow from 'meow'
7
+ import ora from 'ora'
8
+
9
+ import { outputFlags } from '../../flags/index.js'
10
+ import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
11
+ import { prepareFlags } from '../../utils/flags.js'
12
+ import { printFlagList } from '../../utils/formatting.js'
13
+ import { getDefaultKey, setupSdk } from '../../utils/sdk.js'
14
+
15
+ /** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
16
+ export const list = {
17
+ description: 'List scans for an organization',
18
+ async run (argv, importMeta, { parentName }) {
19
+ const name = parentName + ' list'
20
+
21
+ const input = setupCommand(name, list.description, argv, importMeta)
22
+ if (input) {
23
+ const spinnerText = 'Listing scans... \n'
24
+ const spinner = ora(spinnerText).start()
25
+ await listOrgFullScan(input.orgSlug, input, spinner)
26
+ }
27
+ }
28
+ }
29
+
30
+ const listFullScanFlags = prepareFlags({
31
+ sort: {
32
+ type: 'string',
33
+ shortFlag: 's',
34
+ default: 'created_at',
35
+ description: 'Sorting option (`name` or `created_at`) - default is `created_at`',
36
+ },
37
+ direction: {
38
+ type: 'string',
39
+ shortFlag: 'd',
40
+ default: 'desc',
41
+ description: 'Direction option (`desc` or `asc`) - Default is `desc`',
42
+ },
43
+ perPage: {
44
+ type: 'number',
45
+ shortFlag: 'pp',
46
+ default: 30,
47
+ description: 'Results per page - Default is 30',
48
+ },
49
+ page: {
50
+ type: 'number',
51
+ shortFlag: 'p',
52
+ default: 1,
53
+ description: 'Page number - Default is 1',
54
+ },
55
+ fromTime: {
56
+ type: 'string',
57
+ shortFlag: 'f',
58
+ default: '',
59
+ description: 'From time - as a unix timestamp',
60
+ },
61
+ untilTime: {
62
+ type: 'string',
63
+ shortFlag: 'u',
64
+ default: '',
65
+ description: 'Until time - as a unix timestamp',
66
+ }
67
+ })
68
+
69
+ // Internal functions
70
+
71
+ /**
72
+ * @typedef CommandContext
73
+ * @property {boolean} outputJson
74
+ * @property {boolean} outputMarkdown
75
+ * @property {string} orgSlug
76
+ * @property {string} sort
77
+ * @property {string} direction
78
+ * @property {number} per_page
79
+ * @property {number} page
80
+ * @property {string} from_time
81
+ * @property {string} until_time
82
+ */
83
+
84
+ /**
85
+ * @param {string} name
86
+ * @param {string} description
87
+ * @param {readonly string[]} argv
88
+ * @param {ImportMeta} importMeta
89
+ * @returns {void|CommandContext}
90
+ */
91
+ function setupCommand (name, description, argv, importMeta) {
92
+ const flags = {
93
+ ...outputFlags,
94
+ ...listFullScanFlags
95
+ }
96
+
97
+ const cli = meow(`
98
+ Usage
99
+ $ ${name} <org slug>
100
+
101
+ Options
102
+ ${printFlagList(flags, 6)}
103
+
104
+ Examples
105
+ $ ${name} FakeOrg
106
+ `, {
107
+ argv,
108
+ description,
109
+ importMeta,
110
+ flags
111
+ })
112
+
113
+ const {
114
+ json: outputJson,
115
+ markdown: outputMarkdown,
116
+ sort,
117
+ direction,
118
+ perPage,
119
+ page,
120
+ fromTime,
121
+ untilTime
122
+ } = cli.flags
123
+
124
+ if (!cli.input[0]) {
125
+ console.error(`${chalk.bgRed('Input error')}: Please specify an organization slug.\n`)
126
+ cli.showHelp()
127
+ return
128
+ }
129
+
130
+ const [orgSlug = ''] = cli.input
131
+
132
+ return {
133
+ outputJson,
134
+ outputMarkdown,
135
+ orgSlug,
136
+ sort,
137
+ direction,
138
+ per_page: perPage,
139
+ page,
140
+ from_time: fromTime,
141
+ until_time: untilTime
142
+ }
143
+ }
144
+
145
+ /**
146
+ * @typedef FullScansData
147
+ * @property {import('@socketsecurity/sdk').SocketSdkReturnType<'getOrgFullScanList'>["data"]} data
148
+ */
149
+
150
+ /**
151
+ * @param {string} orgSlug
152
+ * @param {CommandContext} input
153
+ * @param {import('ora').Ora} spinner
154
+ * @returns {Promise<void|FullScansData>}
155
+ */
156
+ async function listOrgFullScan (orgSlug, input, spinner) {
157
+ const socketSdk = await setupSdk(getDefaultKey())
158
+ const result = await handleApiCall(socketSdk.getOrgFullScanList(orgSlug, input), 'Listing scans')
159
+
160
+ if (!result.success) {
161
+ return handleUnsuccessfulApiResponse('getOrgFullScanList', result, spinner)
162
+ }
163
+ spinner.stop()
164
+
165
+ console.log(`\n Listing scans for: ${orgSlug} \n`)
166
+
167
+ const options = {
168
+ columns: [
169
+ { field: 'id', name: chalk.magenta('ID') },
170
+ { field: 'report_url', name: chalk.magenta('Scan URL') },
171
+ { field: 'branch', name: chalk.magenta('Branch') },
172
+ { field: 'created_at', name: chalk.magenta('Created at') }
173
+ ]
174
+ }
175
+
176
+ const formattedResults = result.data.results.map(d => {
177
+ return {
178
+ id: d.id,
179
+ report_url: chalk.underline(`${d.html_report_url}`),
180
+ created_at: d.created_at ? new Date(d.created_at).toLocaleDateString('en-us', { year: 'numeric', month: 'numeric', day: 'numeric' }) : '',
181
+ branch: d.branch
182
+ }
183
+ })
184
+
185
+ const table = chalkTable(options, formattedResults)
186
+
187
+ console.log(table, '\n')
188
+
189
+ return {
190
+ data: result.data
191
+ }
192
+ }
@@ -0,0 +1,113 @@
1
+ /* eslint-disable no-console */
2
+
3
+ import chalk from 'chalk'
4
+ import meow from 'meow'
5
+ import ora from 'ora'
6
+
7
+ import { outputFlags } from '../../flags/index.js'
8
+ import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js'
9
+ import { printFlagList } from '../../utils/formatting.js'
10
+ import { getDefaultKey, setupSdk } from '../../utils/sdk.js'
11
+
12
+ /** @type {import('../../utils/meow-with-subcommands.js').CliSubcommand} */
13
+ export const metadata = {
14
+ description: 'Get a scan\'s metadata',
15
+ async run (argv, importMeta, { parentName }) {
16
+ const name = parentName + ' metadata'
17
+
18
+ const input = setupCommand(name, metadata.description, argv, importMeta)
19
+ if (input) {
20
+ const spinnerText = 'Getting scan\'s metadata... \n'
21
+ const spinner = ora(spinnerText).start()
22
+ await getOrgScanMetadata(input.orgSlug, input.scanID, spinner)
23
+ }
24
+ }
25
+ }
26
+
27
+ // Internal functions
28
+
29
+ /**
30
+ * @typedef CommandContext
31
+ * @property {boolean} outputJson
32
+ * @property {boolean} outputMarkdown
33
+ * @property {string} orgSlug
34
+ * @property {string} scanID
35
+ */
36
+
37
+ /**
38
+ * @param {string} name
39
+ * @param {string} description
40
+ * @param {readonly string[]} argv
41
+ * @param {ImportMeta} importMeta
42
+ * @returns {void|CommandContext}
43
+ */
44
+ function setupCommand (name, description, argv, importMeta) {
45
+ const flags = {
46
+ ...outputFlags,
47
+ }
48
+
49
+ const cli = meow(`
50
+ Usage
51
+ $ ${name} <org slug> <scan id>
52
+
53
+ Options
54
+ ${printFlagList(flags, 6)}
55
+
56
+ Examples
57
+ $ ${name} FakeOrg 000aaaa1-0000-0a0a-00a0-00a0000000a0
58
+ `, {
59
+ argv,
60
+ description,
61
+ importMeta,
62
+ flags
63
+ })
64
+
65
+ const {
66
+ json: outputJson,
67
+ markdown: outputMarkdown,
68
+ } = cli.flags
69
+
70
+ if (cli.input.length < 2) {
71
+ console.error(`${chalk.bgRed('Input error')}: Please specify an organization slug and a scan ID.\n`)
72
+ cli.showHelp()
73
+ return
74
+ }
75
+
76
+ const [orgSlug = '', scanID = ''] = cli.input
77
+
78
+ return {
79
+ outputJson,
80
+ outputMarkdown,
81
+ orgSlug,
82
+ scanID
83
+ }
84
+ }
85
+
86
+ /**
87
+ * @typedef FullScansData
88
+ * @property {import('@socketsecurity/sdk').SocketSdkReturnType<'getOrgFullScanMetadata'>["data"]} data
89
+ */
90
+
91
+ /**
92
+ * @param {string} orgSlug
93
+ * @param {string} scanId
94
+ * @param {import('ora').Ora} spinner
95
+ * @returns {Promise<void|FullScansData>}
96
+ */
97
+ async function getOrgScanMetadata (orgSlug, scanId, spinner) {
98
+ const socketSdk = await setupSdk(getDefaultKey())
99
+ const result = await handleApiCall(socketSdk.getOrgFullScanMetadata(orgSlug, scanId), 'Listing scans')
100
+
101
+ if (!result.success) {
102
+ return handleUnsuccessfulApiResponse('getOrgFullScanMetadata', result, spinner)
103
+ }
104
+ spinner.stop()
105
+
106
+ console.log('\nScan metadata: \n')
107
+
108
+ console.log(result.data)
109
+
110
+ return {
111
+ data: result.data
112
+ }
113
+ }