From 65dabc3c506f8c488d5c4fce97c999fdd07e5a63 Mon Sep 17 00:00:00 2001 From: Peter van der Zee Date: Tue, 1 Apr 2025 12:25:33 +0200 Subject: [PATCH] Validate apiToken on input and threatfeed fixes --- src/commands/analytics/cmd-analytics.test.ts | 36 +++++ src/commands/analytics/cmd-analytics.ts | 90 ++++++++---- src/commands/audit-log/cmd-audit-log.test.ts | 12 +- src/commands/audit-log/cmd-audit-log.ts | 33 ++++- src/commands/audit-log/fetch-audit-log.ts | 36 +---- src/commands/cdxgen/cmd-cdxgen.test.ts | 2 +- src/commands/config/cmd-config-auto.ts | 22 ++- src/commands/config/cmd-config-get.test.ts | 9 +- src/commands/config/cmd-config-get.ts | 22 ++- src/commands/config/cmd-config-list.test.ts | 2 +- src/commands/config/cmd-config-list.ts | 13 ++ src/commands/config/cmd-config-set.test.ts | 10 +- src/commands/config/cmd-config-set.ts | 8 ++ src/commands/config/cmd-config-unset.test.ts | 9 +- src/commands/config/cmd-config-unset.ts | 22 ++- src/commands/config/cmd-config.test.ts | 2 +- .../dependencies/cmd-dependencies.test.ts | 2 +- src/commands/dependencies/cmd-dependencies.ts | 26 ++++ .../dependencies/fetch-dependencies.ts | 30 +--- .../diff-scan/cmd-diff-scan-get.test.ts | 6 +- src/commands/diff-scan/cmd-diff-scan-get.ts | 21 ++- src/commands/diff-scan/cmd-diff-scan.test.ts | 2 +- src/commands/diff-scan/fetch-diff-scan.ts | 29 +--- src/commands/fix/cmd-fix.test.ts | 2 +- src/commands/info/cmd-info.test.ts | 2 +- src/commands/info/cmd-info.ts | 10 +- src/commands/login/cmd-login.test.ts | 2 +- src/commands/logout/cmd-logout.test.ts | 2 +- .../manifest/cmd-manifest-auto.test.ts | 2 +- .../manifest/cmd-manifest-gradle.test.ts | 9 +- src/commands/manifest/cmd-manifest-gradle.ts | 2 +- .../manifest/cmd-manifest-kotlin.test.ts | 9 +- src/commands/manifest/cmd-manifest-kotlin.ts | 2 +- .../manifest/cmd-manifest-scala.test.ts | 9 +- src/commands/manifest/cmd-manifest-scala.ts | 2 +- src/commands/manifest/cmd-manifest.test.ts | 8 +- src/commands/npm/cmd-npm.test.ts | 2 +- src/commands/npx/cmd-npx.test.ts | 2 +- src/commands/oops/cmd-oops.test.ts | 2 +- src/commands/optimize/cmd-optimize.test.ts | 2 +- .../cmd-organization-list.test.ts | 8 +- .../organization/cmd-organization-list.ts | 30 ++-- .../cmd-organization-policy-license.test.ts | 6 +- .../cmd-organization-policy-license.ts | 14 +- .../cmd-organization-policy-security.test.ts | 6 +- .../cmd-organization-policy-security.ts | 14 +- .../cmd-organization-policy.test.ts | 8 +- .../cmd-organization-quota.test.ts | 8 +- .../organization/cmd-organization-quota.ts | 26 +++- .../organization/cmd-organization.test.ts | 2 +- .../organization/fetch-license-policy.ts | 4 +- .../organization/fetch-organization-list.ts | 18 +-- src/commands/organization/fetch-quota.ts | 19 +-- .../organization/fetch-security-policy.ts | 21 +-- .../package/cmd-package-score.test.ts | 99 ++++++++------ src/commands/package/cmd-package-score.ts | 44 ++++-- .../package/cmd-package-shallow.test.ts | 10 +- src/commands/package/cmd-package-shallow.ts | 2 +- src/commands/package/cmd-package.test.ts | 2 +- src/commands/package/fetch-purl-deep-score.ts | 11 +- .../package/fetch-purls-shallow-score.ts | 4 +- src/commands/raw-npm/cmd-raw-npm.test.ts | 2 +- src/commands/raw-npx/cmd-raw-npx.test.ts | 2 +- src/commands/report/cmd-report-create.test.ts | 2 +- src/commands/report/cmd-report-view.test.ts | 2 +- src/commands/report/cmd-report-view.ts | 4 +- src/commands/report/cmd-report.test.ts | 2 +- src/commands/report/fetch-report-data.ts | 2 +- src/commands/repos/cmd-repos-create.test.ts | 15 +- src/commands/repos/cmd-repos-create.ts | 12 +- src/commands/repos/cmd-repos-del.test.ts | 14 +- src/commands/repos/cmd-repos-del.ts | 12 +- src/commands/repos/cmd-repos-list.test.ts | 6 +- src/commands/repos/cmd-repos-list.ts | 41 ++++-- src/commands/repos/cmd-repos-update.test.ts | 15 +- src/commands/repos/cmd-repos-update.ts | 12 +- src/commands/repos/cmd-repos-view.test.ts | 15 +- src/commands/repos/cmd-repos-view.ts | 20 ++- src/commands/repos/cmd-repos.test.ts | 2 +- src/commands/repos/fetch-create-repo.ts | 40 +----- src/commands/repos/fetch-delete-repo.ts | 20 +-- src/commands/repos/fetch-list-repos.ts | 37 +---- src/commands/repos/fetch-update-repo.ts | 39 +----- src/commands/repos/fetch-view-repo.ts | 19 +-- src/commands/repos/handle-delete-repo.ts | 19 +-- src/commands/scan/cmd-scan-create.test.ts | 2 +- src/commands/scan/cmd-scan-create.ts | 4 +- src/commands/scan/cmd-scan-del.test.ts | 14 +- src/commands/scan/cmd-scan-del.ts | 12 +- src/commands/scan/cmd-scan-list.test.ts | 13 +- src/commands/scan/cmd-scan-list.ts | 40 ++++-- src/commands/scan/cmd-scan-metadata.test.ts | 14 +- src/commands/scan/cmd-scan-metadata.ts | 22 ++- src/commands/scan/cmd-scan-report.test.ts | 14 +- src/commands/scan/cmd-scan-report.ts | 14 +- src/commands/scan/cmd-scan-view.test.ts | 14 +- src/commands/scan/cmd-scan-view.ts | 23 +++- src/commands/scan/cmd-scan.test.ts | 2 +- .../scan/fetch-delete-org-full-scan.ts | 20 +-- src/commands/scan/fetch-list-scans.ts | 40 +----- src/commands/scan/fetch-report-data.ts | 2 +- src/commands/scan/fetch-scan-metadata.ts | 20 +-- src/commands/scan/fetch-scan.ts | 6 +- src/commands/scan/handle-scan-metadata.ts | 2 +- src/commands/scan/output-scan-metadata.ts | 2 +- src/commands/scan/streamScan.ts | 2 +- .../threat-feed/cmd-threat-feed.test.ts | 24 ++++ src/commands/threat-feed/cmd-threat-feed.ts | 46 ++++++- src/commands/threat-feed/fetch-threat-feed.ts | 48 +++++++ .../threat-feed/handle-threat-feed.ts | 40 ++++++ ...t-threat-feed.ts => output-threat-feed.ts} | 128 ++++-------------- src/commands/threat-feed/types.ts | 15 ++ src/commands/wrapper/cmd-wrapper.test.ts | 2 +- src/commands/wrapper/cmd-wrapper.ts | 2 +- src/utils/handle-bad-input.ts | 5 +- 115 files changed, 1080 insertions(+), 735 deletions(-) create mode 100644 src/commands/threat-feed/fetch-threat-feed.ts create mode 100644 src/commands/threat-feed/handle-threat-feed.ts rename src/commands/threat-feed/{get-threat-feed.ts => output-threat-feed.ts} (67%) create mode 100644 src/commands/threat-feed/types.ts diff --git a/src/commands/analytics/cmd-analytics.test.ts b/src/commands/analytics/cmd-analytics.test.ts index 4110f6cc1..d484e1c4f 100644 --- a/src/commands/analytics/cmd-analytics.test.ts +++ b/src/commands/analytics/cmd-analytics.test.ts @@ -60,6 +60,42 @@ describe('socket analytics', async () => { cmdit( ['analytics', '--dry-run', '--config', '{}'], '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 + |__ | . | _| '_| -_| _| | Node: , API token set: + |_____|___|___|_,_|___|_|.dev | Command: \`socket analytics\`, cwd: + + \\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[1m\\x1b[37m Input error: \\x1b[39m\\x1b[22m\\x1b[49m \\x1b[1mPlease review the input requirements and try again\\x1b[22m: + + - Scope must be "repo" or "org" (\\x1b[32mok\\x1b[39m) + + - The time filter must either be 7, 30 or 90 (\\x1b[32mok\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" + `) + + expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) + } + ) + + cmdit( + [ + 'analytics', + 'boo', + '--scope', + 'org', + '--repo', + 'bar', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], + 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`) diff --git a/src/commands/analytics/cmd-analytics.ts b/src/commands/analytics/cmd-analytics.ts index 6fe1dad47..e70817774 100644 --- a/src/commands/analytics/cmd-analytics.ts +++ b/src/commands/analytics/cmd-analytics.ts @@ -1,13 +1,14 @@ -import { stripIndents } from 'common-tags' -import colors from 'yoctocolors-cjs' +import assert from 'node:assert' import { logger } from '@socketsecurity/registry/lib/logger' import { displayAnalytics } from './display-analytics' import constants from '../../constants' import { commonFlags, outputFlags } from '../../flags' +import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -84,34 +85,54 @@ async function run( const { file, json, markdown, repo, scope, time } = cli.flags - const badScope = scope !== 'org' && scope !== 'repo' - const badTime = time !== 7 && time !== 30 && time !== 90 - const badRepo = scope === 'repo' && !repo - const badFile = file !== '-' && !json && !markdown - const badFlags = json && markdown + const apiToken = getDefaultToken() - if (badScope || badTime || badRepo || badFile || badFlags) { - // 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( - stripIndents`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields: - - - Scope must be "repo" or "org" ${badScope ? colors.red('(bad!)') : colors.green('(ok)')} - - - The time filter must either be 7, 30 or 90 ${badTime ? colors.red('(bad!)') : colors.green('(ok)')} - - ${scope === 'repo' ? `- Repository name using --repo when scope is "repo" ${badRepo ? colors.red('(bad!)') : colors.green('(ok)')}` : ''} - - ${badFlags ? `- The \`--json\` and \`--markdown\` flags can not be used at the same time ${badFlags ? colors.red('(bad!)') : colors.green('(ok)')}` : ''} - - ${badFile ? `- The \`--file\` flag is only valid when using \`--json\` or \`--markdown\` ${badFile ? colors.red('(bad!)') : colors.green('(ok)')}` : ''} - ` - .split('\n') - .filter(s => !!s.trim()) - .join('\n') - ) + const wasBadInput = handleBadInput( + { + test: scope === 'org' || scope === 'repo', + message: 'Scope must be "repo" or "org"', + pass: 'ok', + fail: 'bad' + }, + { + test: time === 7 || time === 30 || time === 90, + message: 'The time filter must either be 7, 30 or 90', + pass: 'ok', + fail: 'bad' + }, + { + nook: true, + test: scope === 'org' || repo, + message: 'When scope=repo, repo name should be set through --repo', + pass: 'ok', + fail: 'missing' + }, + { + nook: true, + test: file === '-' || json || markdown, + message: + 'The `--file` flag is only valid when using `--json` or `--markdown`', + pass: 'ok', + fail: 'bad' + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' + } + ) + if (wasBadInput) { return } @@ -120,6 +141,9 @@ async function run( return } + assert(assertScope(scope)) + assert(assertTime(time)) + return await displayAnalytics({ scope, time, @@ -128,3 +152,11 @@ async function run( filePath: String(file || '') }) } + +function assertScope(scope: unknown): scope is 'org' | 'repo' { + return scope === 'org' || scope === 'repo' +} + +function assertTime(time: unknown): time is 7 | 30 | 90 { + return time === 7 || time === 30 || time === 90 +} diff --git a/src/commands/audit-log/cmd-audit-log.test.ts b/src/commands/audit-log/cmd-audit-log.test.ts index 63aea2aee..2db830b70 100644 --- a/src/commands/audit-log/cmd-audit-log.test.ts +++ b/src/commands/audit-log/cmd-audit-log.test.ts @@ -69,7 +69,9 @@ describe('socket audit-log', async () => { \\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[1m\\x1b[37m Input error: \\x1b[39m\\x1b[22m\\x1b[49m \\x1b[1mPlease review the input requirements and try again\\x1b[22m: - - Org name should be the first arg (\\x1b[31mmissing\\x1b[39m)" + - Org name should be the first arg (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -77,7 +79,13 @@ describe('socket audit-log', async () => { ) cmdit( - ['audit-log', 'fakeorg', '--dry-run', '--config', '{}'], + [ + 'audit-log', + 'fakeorg', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/audit-log/cmd-audit-log.ts b/src/commands/audit-log/cmd-audit-log.ts index 1db7af287..a80225c67 100644 --- a/src/commands/audit-log/cmd-audit-log.ts +++ b/src/commands/audit-log/cmd-audit-log.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -77,12 +78,32 @@ async function run( const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' - const wasBadInput = handleBadInput({ - test: orgSlug, - message: 'Org name should be the first arg', - pass: 'ok', - fail: 'missing' - }) + const apiToken = getDefaultToken() + + const wasBadInput = handleBadInput( + { + test: orgSlug, + message: 'Org name should be the first arg', + pass: 'ok', + fail: 'missing' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + } + ) if (wasBadInput) { return } diff --git a/src/commands/audit-log/fetch-audit-log.ts b/src/commands/audit-log/fetch-audit-log.ts index 750b6919d..3568d0b48 100644 --- a/src/commands/audit-log/fetch-audit-log.ts +++ b/src/commands/audit-log/fetch-audit-log.ts @@ -1,7 +1,6 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -18,44 +17,13 @@ export async function fetchAuditLog({ perPage: number logType: string }): Promise['data'] | void> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - return await fetchAuditLogWithToken(apiToken, { - logType, - orgSlug, - outputKind, - page, - perPage - }) -} + const sockSdk = await setupSdk() -export async function fetchAuditLogWithToken( - apiToken: string, - { - logType, - orgSlug, - outputKind, - page, - perPage - }: { - outputKind: 'json' | 'markdown' | 'print' - orgSlug: string - page: number - perPage: number - logType: string - } -): Promise['data'] | void> { // Lazily access constants.spinner. const { spinner } = constants spinner.start(`Looking up audit log for ${orgSlug}`) - const sockSdk = await setupSdk(apiToken) const result = await handleApiCall( sockSdk.getAuditLogEvents(orgSlug, { // I'm not sure this is used at all. diff --git a/src/commands/cdxgen/cmd-cdxgen.test.ts b/src/commands/cdxgen/cmd-cdxgen.test.ts index 47f014025..62e08ea6b 100644 --- a/src/commands/cdxgen/cmd-cdxgen.test.ts +++ b/src/commands/cdxgen/cmd-cdxgen.test.ts @@ -86,7 +86,7 @@ describe('socket cdxgen', async () => { // cdxgen does not support --dry-run // cmdit( - // ['cdxgen', '--help', '--config', '{}'], + // ['cdxgen', '--help', '--config', '{"apiToken":"anything"}'], // 'should require args with just dry-run', // async cmd => { // const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/config/cmd-config-auto.ts b/src/commands/config/cmd-config-auto.ts index a04201b11..b7cb2ea97 100644 --- a/src/commands/config/cmd-config-auto.ts +++ b/src/commands/config/cmd-config-auto.ts @@ -65,12 +65,22 @@ async function run( const { json, markdown } = cli.flags const [key = ''] = cli.input - const wasBadInput = handleBadInput({ - test: supportedConfigKeys.has(key as keyof LocalConfig) && key !== 'test', - message: 'Config key should be the first arg', - pass: 'ok', - fail: key ? 'invalid config key' : 'missing' - }) + const wasBadInput = handleBadInput( + { + test: supportedConfigKeys.has(key as keyof LocalConfig) && key !== 'test', + message: 'Config key should be the first arg', + pass: 'ok', + fail: key ? 'invalid config key' : 'missing' + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + } + ) if (wasBadInput) { return } diff --git a/src/commands/config/cmd-config-get.test.ts b/src/commands/config/cmd-config-get.test.ts index 42479ea7b..07ee896bf 100644 --- a/src/commands/config/cmd-config-get.test.ts +++ b/src/commands/config/cmd-config-get.test.ts @@ -79,7 +79,14 @@ describe('socket config get', async () => { ) cmdit( - ['config', 'test', 'test', '--dry-run', '--config', '{}'], + [ + 'config', + 'test', + 'test', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/config/cmd-config-get.ts b/src/commands/config/cmd-config-get.ts index 8bda2dc74..0c3f1ccb3 100644 --- a/src/commands/config/cmd-config-get.ts +++ b/src/commands/config/cmd-config-get.ts @@ -60,12 +60,22 @@ async function run( const { json, markdown } = cli.flags const [key = ''] = cli.input - const wasBadInput = handleBadInput({ - test: supportedConfigKeys.has(key as keyof LocalConfig) || key === 'test', - message: 'Config key should be the first arg', - pass: 'ok', - fail: key ? 'invalid config key' : 'missing' - }) + const wasBadInput = handleBadInput( + { + test: supportedConfigKeys.has(key as keyof LocalConfig) || key === 'test', + message: 'Config key should be the first arg', + pass: 'ok', + fail: key ? 'invalid config key' : 'missing' + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + } + ) if (wasBadInput) { return } diff --git a/src/commands/config/cmd-config-list.test.ts b/src/commands/config/cmd-config-list.test.ts index 76809822a..9a12cbd31 100644 --- a/src/commands/config/cmd-config-list.test.ts +++ b/src/commands/config/cmd-config-list.test.ts @@ -58,7 +58,7 @@ describe('socket config get', async () => { ) cmdit( - ['config', 'list', '--dry-run', '--config', '{}'], + ['config', 'list', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/config/cmd-config-list.ts b/src/commands/config/cmd-config-list.ts index 01dbb442f..c497759e8 100644 --- a/src/commands/config/cmd-config-list.ts +++ b/src/commands/config/cmd-config-list.ts @@ -4,6 +4,7 @@ import { outputConfigList } from './output-config-list' import constants from '../../constants' import { commonFlags, outputFlags } from '../../flags' import { supportedConfigKeys } from '../../utils/config' +import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' @@ -62,6 +63,18 @@ async function run( const { full, json, markdown } = cli.flags + const wasBadInput = handleBadInput({ + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + }) + if (wasBadInput) { + return + } + if (cli.flags['dryRun']) { logger.log(DRY_RUN_BAIL_TEXT) return diff --git a/src/commands/config/cmd-config-set.test.ts b/src/commands/config/cmd-config-set.test.ts index 000a21a17..cc4f4a534 100644 --- a/src/commands/config/cmd-config-set.test.ts +++ b/src/commands/config/cmd-config-set.test.ts @@ -86,7 +86,15 @@ describe('socket config get', async () => { ) cmdit( - ['config', 'set', 'test', 'xyz', '--dry-run', '--config', '{}'], + [ + 'config', + 'set', + 'test', + 'xyz', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/config/cmd-config-set.ts b/src/commands/config/cmd-config-set.ts index 9f4072620..1bc3924c5 100644 --- a/src/commands/config/cmd-config-set.ts +++ b/src/commands/config/cmd-config-set.ts @@ -79,6 +79,14 @@ async function run( 'Key value should be the remaining args (use `unset` to unset a value)', pass: 'ok', fail: 'missing' + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' } ) if (wasBadInput) { diff --git a/src/commands/config/cmd-config-unset.test.ts b/src/commands/config/cmd-config-unset.test.ts index a5a7d1560..bdf21c250 100644 --- a/src/commands/config/cmd-config-unset.test.ts +++ b/src/commands/config/cmd-config-unset.test.ts @@ -79,7 +79,14 @@ describe('socket config unset', async () => { ) cmdit( - ['config', 'unset', 'test', '--dry-run', '--config', '{}'], + [ + 'config', + 'unset', + 'test', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/config/cmd-config-unset.ts b/src/commands/config/cmd-config-unset.ts index dfc9e2889..689c0c3e0 100644 --- a/src/commands/config/cmd-config-unset.ts +++ b/src/commands/config/cmd-config-unset.ts @@ -60,12 +60,22 @@ async function run( const { json, markdown } = cli.flags const [key = ''] = cli.input - const wasBadInput = handleBadInput({ - test: supportedConfigKeys.has(key as keyof LocalConfig) || key === 'test', - message: 'Config key should be the first arg', - pass: 'ok', - fail: key ? 'invalid config key' : 'missing' - }) + const wasBadInput = handleBadInput( + { + test: supportedConfigKeys.has(key as keyof LocalConfig) || key === 'test', + message: 'Config key should be the first arg', + pass: 'ok', + fail: key ? 'invalid config key' : 'missing' + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + } + ) if (wasBadInput) { return } diff --git a/src/commands/config/cmd-config.test.ts b/src/commands/config/cmd-config.test.ts index 3701d1dda..510b78a3c 100644 --- a/src/commands/config/cmd-config.test.ts +++ b/src/commands/config/cmd-config.test.ts @@ -54,7 +54,7 @@ describe('socket config', async () => { ) cmdit( - ['config', '--dry-run', '--config', '{}'], + ['config', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/dependencies/cmd-dependencies.test.ts b/src/commands/dependencies/cmd-dependencies.test.ts index 8d8724401..2ae183e2c 100644 --- a/src/commands/dependencies/cmd-dependencies.test.ts +++ b/src/commands/dependencies/cmd-dependencies.test.ts @@ -51,7 +51,7 @@ describe('socket dependencies', async () => { ) cmdit( - ['dependencies', '--dry-run', '--config', '{}'], + ['dependencies', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/dependencies/cmd-dependencies.ts b/src/commands/dependencies/cmd-dependencies.ts index 88e664880..ff86de9c5 100644 --- a/src/commands/dependencies/cmd-dependencies.ts +++ b/src/commands/dependencies/cmd-dependencies.ts @@ -3,8 +3,10 @@ import { logger } from '@socketsecurity/registry/lib/logger' import { handleDependencies } from './handle-dependencies' import constants from '../../constants' import { commonFlags, outputFlags } from '../../flags' +import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -63,6 +65,30 @@ async function run( const { json, limit, markdown, offset } = cli.flags + const apiToken = getDefaultToken() + + const wasBadInput = handleBadInput( + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' + } + ) + if (wasBadInput) { + return + } + if (cli.flags['dryRun']) { logger.log(DRY_RUN_BAIL_TEXT) return diff --git a/src/commands/dependencies/fetch-dependencies.ts b/src/commands/dependencies/fetch-dependencies.ts index a34b9c23c..ddc4ba8cf 100644 --- a/src/commands/dependencies/fetch-dependencies.ts +++ b/src/commands/dependencies/fetch-dependencies.ts @@ -1,7 +1,6 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -12,42 +11,19 @@ export async function fetchDependencies({ limit: number offset: number }): Promise['data'] | undefined> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - return await fetchDependenciesWithToken(apiToken, { - limit, - offset - }) -} + const sockSdk = await setupSdk() -async function fetchDependenciesWithToken( - apiToken: string, - { - limit, - offset - }: { - limit: number - offset: number - } -): Promise['data'] | undefined> { // Lazily access constants.spinner. const { spinner } = constants spinner.start('Fetching organization dependencies...') - const sockSdk = await setupSdk(apiToken) - const result = await handleApiCall( sockSdk.searchDependencies({ limit, offset }), 'Searching dependencies' ) - spinner?.successAndStop('Received organization dependencies response.') + spinner.successAndStop('Received organization dependencies response.') if (!result.success) { handleUnsuccessfulApiResponse('searchDependencies', result) diff --git a/src/commands/diff-scan/cmd-diff-scan-get.test.ts b/src/commands/diff-scan/cmd-diff-scan-get.test.ts index f3dad15e3..c8704ca3f 100644 --- a/src/commands/diff-scan/cmd-diff-scan-get.test.ts +++ b/src/commands/diff-scan/cmd-diff-scan-get.test.ts @@ -73,7 +73,9 @@ describe('socket diff-scan get', async () => { - Specify a before and after scan ID (\\x1b[31mmissing before and after\\x1b[39m) The args are expecting a full \`aaa0aa0a-aaaa-0000-0a0a-0000000a00a0\` scan ID. - - Org name as the first argument (\\x1b[31mmissing\\x1b[39m)" + - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -87,7 +89,7 @@ describe('socket diff-scan get', async () => { 'fakeorg', '--dry-run', '--config', - '{}', + '{"apiToken":"anything"}', '--before', 'x', '--after', diff --git a/src/commands/diff-scan/cmd-diff-scan-get.ts b/src/commands/diff-scan/cmd-diff-scan-get.ts index 7ba7a312d..fc1772e2f 100644 --- a/src/commands/diff-scan/cmd-diff-scan-get.ts +++ b/src/commands/diff-scan/cmd-diff-scan-get.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -90,6 +91,8 @@ async function run( const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' + const apiToken = getDefaultToken() + const wasBadInput = handleBadInput( { test: !!(before && after), @@ -105,10 +108,26 @@ async function run( }, { test: !!orgSlug, - hide: !!defaultOrgSlug, + nook: true, message: 'Org name as the first argument', pass: 'ok', fail: 'missing' + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { diff --git a/src/commands/diff-scan/cmd-diff-scan.test.ts b/src/commands/diff-scan/cmd-diff-scan.test.ts index ee48cfa88..147c7c2e7 100644 --- a/src/commands/diff-scan/cmd-diff-scan.test.ts +++ b/src/commands/diff-scan/cmd-diff-scan.test.ts @@ -50,7 +50,7 @@ describe('socket diff-scan', async () => { ) cmdit( - ['diff-scan', '--dry-run', '--config', '{}'], + ['diff-scan', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/diff-scan/fetch-diff-scan.ts b/src/commands/diff-scan/fetch-diff-scan.ts index 8ba5a2905..220fdc4d9 100644 --- a/src/commands/diff-scan/fetch-diff-scan.ts +++ b/src/commands/diff-scan/fetch-diff-scan.ts @@ -2,7 +2,6 @@ import colors from 'yoctocolors-cjs' import constants from '../../constants' import { handleApiCall, handleApiError, queryApi } from '../../utils/api' -import { AuthError } from '../../utils/errors' import { getDefaultToken } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -17,31 +16,7 @@ export async function fetchDiffScan({ orgSlug: string }): Promise['data'] | undefined> { const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - return await fetchDiffScanWithToken(apiToken, { - after, - before, - orgSlug - }) -} - -export async function fetchDiffScanWithToken( - apiToken: string, - { - after, - before, - orgSlug - }: { - after: string - before: string - orgSlug: string - } -): Promise['data'] | undefined> { // Lazily access constants.spinner. const { spinner } = constants @@ -49,10 +24,10 @@ export async function fetchDiffScanWithToken( const response = await queryApi( `orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(before)}&after=${encodeURIComponent(after)}`, - apiToken + apiToken || '' ) - spinner?.successAndStop('Received diff-scan response') + spinner.successAndStop('Received diff-scan response') if (!response.ok) { const err = await handleApiError(response.status) diff --git a/src/commands/fix/cmd-fix.test.ts b/src/commands/fix/cmd-fix.test.ts index 2fb22c074..f6894c981 100644 --- a/src/commands/fix/cmd-fix.test.ts +++ b/src/commands/fix/cmd-fix.test.ts @@ -42,7 +42,7 @@ describe('socket fix', async () => { ) cmdit( - ['fix', '--dry-run', '--config', '{}'], + ['fix', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/info/cmd-info.test.ts b/src/commands/info/cmd-info.test.ts index 5b7e5bf15..000ab7ef4 100644 --- a/src/commands/info/cmd-info.test.ts +++ b/src/commands/info/cmd-info.test.ts @@ -74,7 +74,7 @@ describe('socket info', async () => { ) cmdit( - ['info', 'mootools', '--dry-run', '--config', '{}'], + ['info', 'mootools', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/info/cmd-info.ts b/src/commands/info/cmd-info.ts index 8482e9b72..e1a99c46a 100644 --- a/src/commands/info/cmd-info.ts +++ b/src/commands/info/cmd-info.ts @@ -62,11 +62,19 @@ async function run( fail: 'missing' }, { + nook: true, test: cli.input.length === 1, - hide: cli.input.length === 1, message: 'Can only accept one package at a time', pass: 'ok', fail: 'got ' + cli.input.length + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' } ) if (wasBadInput) { diff --git a/src/commands/login/cmd-login.test.ts b/src/commands/login/cmd-login.test.ts index 534fcf18a..7e0fa4534 100644 --- a/src/commands/login/cmd-login.test.ts +++ b/src/commands/login/cmd-login.test.ts @@ -50,7 +50,7 @@ describe('socket login', async () => { ) cmdit( - ['login', 'mootools', '--dry-run', '--config', '{}'], + ['login', 'mootools', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/logout/cmd-logout.test.ts b/src/commands/logout/cmd-logout.test.ts index c097f4b02..88e6ecd26 100644 --- a/src/commands/logout/cmd-logout.test.ts +++ b/src/commands/logout/cmd-logout.test.ts @@ -42,7 +42,7 @@ describe('socket logout', async () => { ) cmdit( - ['logout', 'mootools', '--dry-run', '--config', '{}'], + ['logout', 'mootools', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/manifest/cmd-manifest-auto.test.ts b/src/commands/manifest/cmd-manifest-auto.test.ts index 3610ea7e2..d079b4b55 100644 --- a/src/commands/manifest/cmd-manifest-auto.test.ts +++ b/src/commands/manifest/cmd-manifest-auto.test.ts @@ -50,7 +50,7 @@ describe('socket manifest auto', async () => { ) cmdit( - ['manifest', 'auto', '--dry-run', '--config', '{}'], + ['manifest', 'auto', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/manifest/cmd-manifest-gradle.test.ts b/src/commands/manifest/cmd-manifest-gradle.test.ts index c0755e5ad..a34ff26a5 100644 --- a/src/commands/manifest/cmd-manifest-gradle.test.ts +++ b/src/commands/manifest/cmd-manifest-gradle.test.ts @@ -100,7 +100,14 @@ describe('socket manifest gradle', async () => { ) cmdit( - ['manifest', 'gradle', 'mootools', '--dry-run', '--config', '{}'], + [ + 'manifest', + 'gradle', + 'mootools', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/manifest/cmd-manifest-gradle.ts b/src/commands/manifest/cmd-manifest-gradle.ts index e734e6bbd..bc0e5325b 100644 --- a/src/commands/manifest/cmd-manifest-gradle.ts +++ b/src/commands/manifest/cmd-manifest-gradle.ts @@ -130,8 +130,8 @@ async function run( fail: target === '-' ? 'stdin is not supported' : 'missing' }, { + nook: true, test: cli.input.length === 1, - hide: cli.input.length === 1, message: 'Can only accept one DIR (make sure to escape spaces!)', pass: 'ok', fail: 'received ' + cli.input.length diff --git a/src/commands/manifest/cmd-manifest-kotlin.test.ts b/src/commands/manifest/cmd-manifest-kotlin.test.ts index 2d548c9ed..3873b3247 100644 --- a/src/commands/manifest/cmd-manifest-kotlin.test.ts +++ b/src/commands/manifest/cmd-manifest-kotlin.test.ts @@ -100,7 +100,14 @@ describe('socket manifest kotlin', async () => { ) cmdit( - ['manifest', 'kotlin', 'mootools', '--dry-run', '--config', '{}'], + [ + 'manifest', + 'kotlin', + 'mootools', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/manifest/cmd-manifest-kotlin.ts b/src/commands/manifest/cmd-manifest-kotlin.ts index 3d11e6dcf..f0afcf71a 100644 --- a/src/commands/manifest/cmd-manifest-kotlin.ts +++ b/src/commands/manifest/cmd-manifest-kotlin.ts @@ -135,8 +135,8 @@ async function run( fail: target === '-' ? 'stdin is not supported' : 'missing' }, { + nook: true, test: cli.input.length === 1, - hide: cli.input.length === 1, message: 'Can only accept one DIR (make sure to escape spaces!)', pass: 'ok', fail: 'received ' + cli.input.length diff --git a/src/commands/manifest/cmd-manifest-scala.test.ts b/src/commands/manifest/cmd-manifest-scala.test.ts index 18fe58b7b..977401669 100644 --- a/src/commands/manifest/cmd-manifest-scala.test.ts +++ b/src/commands/manifest/cmd-manifest-scala.test.ts @@ -104,7 +104,14 @@ describe('socket manifest scala', async () => { ) cmdit( - ['manifest', 'scala', 'mootools', '--dry-run', '--config', '{}'], + [ + 'manifest', + 'scala', + 'mootools', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/manifest/cmd-manifest-scala.ts b/src/commands/manifest/cmd-manifest-scala.ts index 2f0dae85b..f9d63e75b 100644 --- a/src/commands/manifest/cmd-manifest-scala.ts +++ b/src/commands/manifest/cmd-manifest-scala.ts @@ -128,8 +128,8 @@ async function run( fail: target === '-' ? 'stdin is not supported' : 'missing' }, { + nook: true, test: cli.input.length === 1, - hide: cli.input.length === 1, message: 'Can only accept one DIR (make sure to escape spaces!)', pass: 'ok', fail: 'received ' + cli.input.length diff --git a/src/commands/manifest/cmd-manifest.test.ts b/src/commands/manifest/cmd-manifest.test.ts index 851c191c7..34162e328 100644 --- a/src/commands/manifest/cmd-manifest.test.ts +++ b/src/commands/manifest/cmd-manifest.test.ts @@ -53,7 +53,13 @@ describe('socket manifest', async () => { ) cmdit( - ['manifest', 'mootools', '--dry-run', '--config', '{}'], + [ + 'manifest', + 'mootools', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/npm/cmd-npm.test.ts b/src/commands/npm/cmd-npm.test.ts index d4f05efbe..dfbc5c289 100644 --- a/src/commands/npm/cmd-npm.test.ts +++ b/src/commands/npm/cmd-npm.test.ts @@ -38,7 +38,7 @@ describe('socket npm', async () => { ) cmdit( - ['npm', '--dry-run', '--config', '{}'], + ['npm', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/npx/cmd-npx.test.ts b/src/commands/npx/cmd-npx.test.ts index 729d21d96..f8e377a11 100644 --- a/src/commands/npx/cmd-npx.test.ts +++ b/src/commands/npx/cmd-npx.test.ts @@ -38,7 +38,7 @@ describe('socket npx', async () => { ) cmdit( - ['npx', '--dry-run', '--config', '{}'], + ['npx', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/oops/cmd-oops.test.ts b/src/commands/oops/cmd-oops.test.ts index b1da36387..b9b5a78af 100644 --- a/src/commands/oops/cmd-oops.test.ts +++ b/src/commands/oops/cmd-oops.test.ts @@ -40,7 +40,7 @@ describe('socket oops', async () => { ) cmdit( - ['oops', '--dry-run', '--config', '{}'], + ['oops', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/optimize/cmd-optimize.test.ts b/src/commands/optimize/cmd-optimize.test.ts index 20b646b62..80e060f9c 100644 --- a/src/commands/optimize/cmd-optimize.test.ts +++ b/src/commands/optimize/cmd-optimize.test.ts @@ -50,7 +50,7 @@ describe('socket optimize', async () => { ) cmdit( - ['optimize', '--dry-run', '--config', '{}'], + ['optimize', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/organization/cmd-organization-list.test.ts b/src/commands/organization/cmd-organization-list.test.ts index e4935185a..c325b18f0 100644 --- a/src/commands/organization/cmd-organization-list.test.ts +++ b/src/commands/organization/cmd-organization-list.test.ts @@ -46,7 +46,13 @@ describe('socket organization list', async () => { ) cmdit( - ['organization', 'list', '--dry-run', '--config', '{}'], + [ + 'organization', + 'list', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should be ok with org name and id', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/organization/cmd-organization-list.ts b/src/commands/organization/cmd-organization-list.ts index 02ecb51fa..4120f9159 100644 --- a/src/commands/organization/cmd-organization-list.ts +++ b/src/commands/organization/cmd-organization-list.ts @@ -6,6 +6,7 @@ import { commonFlags, outputFlags } from '../../flags' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -46,16 +47,27 @@ async function run( parentName }) - const json = Boolean(cli.flags['json']) - const markdown = Boolean(cli.flags['markdown']) + const { json, markdown } = cli.flags + const apiToken = getDefaultToken() - const wasBadInput = handleBadInput({ - hide: !json || !markdown, - test: !json || !markdown, - message: 'The json and markdown flags cannot be both set, pick one', - pass: 'ok', - fail: 'omit one' - }) + const wasBadInput = handleBadInput( + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' + } + ) if (wasBadInput) { return } diff --git a/src/commands/organization/cmd-organization-policy-license.test.ts b/src/commands/organization/cmd-organization-policy-license.test.ts index c10d3847b..3a7bc0c27 100644 --- a/src/commands/organization/cmd-organization-policy-license.test.ts +++ b/src/commands/organization/cmd-organization-policy-license.test.ts @@ -67,7 +67,9 @@ describe('socket organization policy license', async () => { \\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[1m\\x1b[37m Input error: \\x1b[39m\\x1b[22m\\x1b[49m \\x1b[1mPlease review the input requirements and try again\\x1b[22m: - - Org name as the first argument (\\x1b[31mmissing\\x1b[39m)" + - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if input bad').toBe(2) @@ -82,7 +84,7 @@ describe('socket organization policy license', async () => { 'fakeorg', '--dry-run', '--config', - '{}' + '{"apiToken":"anything"}' ], 'should be ok with org name and id', async cmd => { diff --git a/src/commands/organization/cmd-organization-policy-license.ts b/src/commands/organization/cmd-organization-policy-license.ts index 96a81b5e2..5a009ebac 100644 --- a/src/commands/organization/cmd-organization-policy-license.ts +++ b/src/commands/organization/cmd-organization-policy-license.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -60,21 +61,30 @@ async function run( const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' + const apiToken = getDefaultToken() const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', fail: 'missing' }, { - hide: !json || !markdown, + nook: true, test: !json || !markdown, message: 'The json and markdown flags cannot be both set, pick one', pass: 'ok', fail: 'omit one' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { diff --git a/src/commands/organization/cmd-organization-policy-security.test.ts b/src/commands/organization/cmd-organization-policy-security.test.ts index d82c37bdd..a9e2f8f1e 100644 --- a/src/commands/organization/cmd-organization-policy-security.test.ts +++ b/src/commands/organization/cmd-organization-policy-security.test.ts @@ -67,7 +67,9 @@ describe('socket organization policy security', async () => { \\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[1m\\x1b[37m Input error: \\x1b[39m\\x1b[22m\\x1b[49m \\x1b[1mPlease review the input requirements and try again\\x1b[22m: - - Org name as the first argument (\\x1b[31mmissing\\x1b[39m)" + - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if input bad').toBe(2) @@ -82,7 +84,7 @@ describe('socket organization policy security', async () => { 'fakeorg', '--dry-run', '--config', - '{}' + '{"apiToken":"anything"}' ], 'should be ok with org name and id', async cmd => { diff --git a/src/commands/organization/cmd-organization-policy-security.ts b/src/commands/organization/cmd-organization-policy-security.ts index 5c18aa7ef..361485430 100644 --- a/src/commands/organization/cmd-organization-policy-security.ts +++ b/src/commands/organization/cmd-organization-policy-security.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -60,21 +61,30 @@ async function run( const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' + const apiToken = getDefaultToken() const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', fail: 'missing' }, { - hide: !json || !markdown, + nook: true, test: !json || !markdown, message: 'The json and markdown flags cannot be both set, pick one', pass: 'ok', fail: 'omit one' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { diff --git a/src/commands/organization/cmd-organization-policy.test.ts b/src/commands/organization/cmd-organization-policy.test.ts index 7285bea20..5c58dec68 100644 --- a/src/commands/organization/cmd-organization-policy.test.ts +++ b/src/commands/organization/cmd-organization-policy.test.ts @@ -50,7 +50,13 @@ describe('socket organization list', async () => { ) cmdit( - ['organization', 'policy', '--dry-run', '--config', '{}'], + [ + 'organization', + 'policy', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should support --dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/organization/cmd-organization-quota.test.ts b/src/commands/organization/cmd-organization-quota.test.ts index 900e8e410..ec0dccabd 100644 --- a/src/commands/organization/cmd-organization-quota.test.ts +++ b/src/commands/organization/cmd-organization-quota.test.ts @@ -46,7 +46,13 @@ describe('socket organization quota', async () => { ) cmdit( - ['organization', 'quota', '--dry-run', '--config', '{}'], + [ + 'organization', + 'quota', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should be ok with org name and id', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/organization/cmd-organization-quota.ts b/src/commands/organization/cmd-organization-quota.ts index 1afc07eb8..0f92e83a4 100644 --- a/src/commands/organization/cmd-organization-quota.ts +++ b/src/commands/organization/cmd-organization-quota.ts @@ -6,6 +6,7 @@ import { commonFlags, outputFlags } from '../../flags' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -48,14 +49,25 @@ async function run( const json = Boolean(cli.flags['json']) const markdown = Boolean(cli.flags['markdown']) + const apiToken = getDefaultToken() - const wasBadInput = handleBadInput({ - hide: !json || !markdown, - test: !json || !markdown, - message: 'The json and markdown flags cannot be both set, pick one', - pass: 'ok', - fail: 'omit one' - }) + const wasBadInput = handleBadInput( + { + nook: true, + test: !json || !markdown, + message: 'The json and markdown flags cannot be both set, pick one', + pass: 'ok', + fail: 'omit one' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' + } + ) if (wasBadInput) { return } diff --git a/src/commands/organization/cmd-organization.test.ts b/src/commands/organization/cmd-organization.test.ts index ed8fc6865..725221efc 100644 --- a/src/commands/organization/cmd-organization.test.ts +++ b/src/commands/organization/cmd-organization.test.ts @@ -50,7 +50,7 @@ describe('socket organization', async () => { ) cmdit( - ['organization', '--dry-run', '--config', '{}'], + ['organization', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should be ok with org name and id', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/organization/fetch-license-policy.ts b/src/commands/organization/fetch-license-policy.ts index c3206625e..02db8c301 100644 --- a/src/commands/organization/fetch-license-policy.ts +++ b/src/commands/organization/fetch-license-policy.ts @@ -22,11 +22,11 @@ async function fetchLicensePolicyWithToken( apiToken: string, orgSlug: string ): Promise['data'] | undefined> { + const sockSdk = await setupSdk(apiToken) + // Lazily access constants.spinner. const { spinner } = constants - const sockSdk = await setupSdk(apiToken) - spinner.start('Fetching organization license policy...') const result = await handleApiCall( diff --git a/src/commands/organization/fetch-organization-list.ts b/src/commands/organization/fetch-organization-list.ts index 8e973f1f3..90682f5bb 100644 --- a/src/commands/organization/fetch-organization-list.ts +++ b/src/commands/organization/fetch-organization-list.ts @@ -1,27 +1,13 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' export async function fetchOrganization(): Promise< SocketSdkReturnType<'getOrganizations'>['data'] | undefined > { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - return await fetchOrganizationWithToken(apiToken) -} - -async function fetchOrganizationWithToken( - apiToken: string -): Promise['data'] | undefined> { - const sockSdk = await setupSdk(apiToken) + const sockSdk = await setupSdk() // Lazily access constants.spinner. const { spinner } = constants diff --git a/src/commands/organization/fetch-quota.ts b/src/commands/organization/fetch-quota.ts index fed5a647b..f8bde5058 100644 --- a/src/commands/organization/fetch-quota.ts +++ b/src/commands/organization/fetch-quota.ts @@ -1,30 +1,17 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' export async function fetchQuota(): Promise< SocketSdkReturnType<'getQuota'>['data'] | undefined > { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - return await fetchQuotaWithToken(apiToken) -} + const sockSdk = await setupSdk() -async function fetchQuotaWithToken( - apiToken: string -): Promise['data'] | undefined> { // Lazily access constants.spinner. const { spinner } = constants - const sockSdk = await setupSdk(apiToken) - spinner.start('Fetching organization quota...') const result = await handleApiCall( @@ -32,7 +19,7 @@ async function fetchQuotaWithToken( 'looking up organization quota' ) - spinner?.successAndStop('Recieved organization quota response.') + spinner.successAndStop('Received organization quota response.') if (!result.success) { handleUnsuccessfulApiResponse('getQuota', result) diff --git a/src/commands/organization/fetch-security-policy.ts b/src/commands/organization/fetch-security-policy.ts index e9abe2010..5a425aa3d 100644 --- a/src/commands/organization/fetch-security-policy.ts +++ b/src/commands/organization/fetch-security-policy.ts @@ -1,32 +1,17 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' export async function fetchSecurityPolicy( orgSlug: string ): Promise['data'] | undefined> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - return await fetchSecurityPolicyWithToken(apiToken, orgSlug) -} + const sockSdk = await setupSdk() -async function fetchSecurityPolicyWithToken( - apiToken: string, - orgSlug: string -): Promise['data'] | undefined> { // Lazily access constants.spinner. const { spinner } = constants - const sockSdk = await setupSdk(apiToken) - spinner.start('Fetching organization security policy...') const result = await handleApiCall( @@ -34,7 +19,7 @@ async function fetchSecurityPolicyWithToken( 'looking up organization quota' ) - spinner?.successAndStop('Received organization security policy response.') + spinner.successAndStop('Received organization security policy response.') if (!result.success) { handleUnsuccessfulApiResponse('getOrgSecurityPolicy', result) diff --git a/src/commands/package/cmd-package-score.test.ts b/src/commands/package/cmd-package-score.test.ts index df3508a0c..ecac28387 100644 --- a/src/commands/package/cmd-package-score.test.ts +++ b/src/commands/package/cmd-package-score.test.ts @@ -11,48 +11,51 @@ describe('socket package score', async () => { // Lazily access constants.rootBinPath. const entryPath = path.join(constants.rootBinPath, `${CLI}.js`) - cmdit(['package', 'score', '--help'], 'should support --help', async cmd => { - const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) - expect(stdout).toMatchInlineSnapshot( - ` - "Look up score for one package which reflects all of its transitive dependencies as well + cmdit( + ['package', 'score', '--help', '--config', '{}'], + 'should support --help', + async cmd => { + const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) + expect(stdout).toMatchInlineSnapshot( + ` + "Look up score for one package which reflects all of its transitive dependencies as well - Usage - $ socket package score < | > + Usage + $ socket package score < | > - 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 + 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\` + Requirements + - quota: 100 + - scope: \`packages:list\` - Show deep scoring details for one package. The score will reflect the package - itself, any of its dependencies, and any of its transitive dependencies. + Show deep scoring details for one package. The score will reflect the package + itself, any of its dependencies, and any of its transitive dependencies. - When you want to know whether to trust a package, this is the command to run. + When you want to know whether to trust a package, this is the command to run. - See also the \`socket package shallow\` command, which returns the shallow - score for any number of packages. That will not reflect the dependency scores. + See also the \`socket package shallow\` command, which returns the shallow + score for any number of packages. That will not reflect the dependency scores. - Only a few ecosystems are supported like npm, golang, and maven. + 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. + A "purl" is a standard package name formatting: \`pkg:eco/name@version\` + This command will automatically prepend "pkg:" when not present. - The version is optional but when given should be a direct match. + The version is optional but when given should be a direct match. - Examples - $ socket package score npm babel-cli - $ socket package score npm babel-cli@1.9.1 - $ socket package score npm/babel-cli@1.9.1 - $ socket package score pkg:npm/babel-cli@1.9.1" - ` - ) - expect(`\n ${stderr}`).toMatchInlineSnapshot(` + Examples + $ socket package score npm babel-cli + $ socket package score npm babel-cli@1.9.1 + $ socket package score npm/babel-cli@1.9.1 + $ socket package score pkg:npm/babel-cli@1.9.1" + ` + ) + expect(`\n ${stderr}`).toMatchInlineSnapshot(` " _____ _ _ /--------------- | __|___ ___| |_ ___| |_ | Socket.dev CLI ver @@ -60,14 +63,16 @@ describe('socket package score', async () => { |_____|___|___|_,_|___|_|.dev | Command: \`socket package score\`, cwd: " `) - expect(code, 'help should exit with code 2').toBe(2) - expect(stderr, 'header should include command (without params)').toContain( - '`socket package score`' - ) - }) + expect(code, 'help should exit with code 2').toBe(2) + expect( + stderr, + 'header should include command (without params)' + ).toContain('`socket package score`') + } + ) cmdit( - ['package', 'score', '--dry-run'], + ['package', 'score', '--dry-run', '--config', '{}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) @@ -79,11 +84,13 @@ describe('socket package score', async () => { |__ | . | _| '_| -_| _| | Node: , API token set: |_____|___|___|_,_|___|_|.dev | Command: \`socket package score\`, cwd: - \\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[37mInput error\\x1b[39m\\x1b[49m: Please provide the required fields: + \\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[1m\\x1b[37m Input error: \\x1b[39m\\x1b[22m\\x1b[49m \\x1b[1mPlease review the input requirements and try again\\x1b[22m: + + - First parameter must be an ecosystem or the whole purl (\\x1b[31mbad\\x1b[39m) - - First parameter should be an ecosystem or the arg must be a purl \\x1b[31m(bad!)\\x1b[39m + - Expecting at least one package (\\x1b[31mmissing\\x1b[39m) - - Expecting the package to check \\x1b[31m(missing!)\\x1b[39m" + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -91,7 +98,15 @@ describe('socket package score', async () => { ) cmdit( - ['package', 'score', 'npm', 'babel', '--dry-run'], + [ + 'package', + 'score', + 'npm', + 'babel', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/package/cmd-package-score.ts b/src/commands/package/cmd-package-score.ts index f3c3b0028..e94e7af66 100644 --- a/src/commands/package/cmd-package-score.ts +++ b/src/commands/package/cmd-package-score.ts @@ -1,13 +1,13 @@ -import colors from 'yoctocolors-cjs' - import { logger } from '@socketsecurity/registry/lib/logger' import { handlePurlDeepScore } from './handle-purl-deep-score' import { parsePackageSpecifiers } from './parse-package-specifiers' import constants from '../../constants' import { commonFlags, outputFlags } from '../../flags' +import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -76,18 +76,40 @@ async function run( const { json, markdown } = cli.flags const [ecosystem = '', purl] = cli.input + const apiToken = getDefaultToken() const { purls, valid } = parsePackageSpecifiers(ecosystem, purl ? [purl] : []) - 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 the arg must be a purl ${!valid ? colors.red('(bad!)') : colors.green('(ok)')}\n - - Expecting the package to check ${!purls.length ? colors.red('(missing!)') : colors.green('(ok)')}\n - `) + const wasBadInput = handleBadInput( + { + test: valid, + message: 'First parameter must be an ecosystem or the whole purl', + pass: 'ok', + fail: 'bad' + }, + { + test: purls.length === 1, + message: 'Expecting at least one package', + pass: 'ok', + fail: purls.length === 0 ? 'missing' : 'too many' + }, + { + nook: true, + test: !json || !markdown, + message: 'The json and markdown flags cannot be both set, pick one', + pass: 'ok', + fail: 'omit one' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' + } + ) + if (wasBadInput) { return } diff --git a/src/commands/package/cmd-package-shallow.test.ts b/src/commands/package/cmd-package-shallow.test.ts index b1ad08ade..c8c07a9eb 100644 --- a/src/commands/package/cmd-package-shallow.test.ts +++ b/src/commands/package/cmd-package-shallow.test.ts @@ -95,7 +95,15 @@ describe('socket package shallow', async () => { ) cmdit( - ['package', 'shallow', 'npm', 'babel', '--dry-run', '--config', '{}'], + [ + 'package', + 'shallow', + 'npm', + 'babel', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/package/cmd-package-shallow.ts b/src/commands/package/cmd-package-shallow.ts index c238df672..bf4392ea5 100644 --- a/src/commands/package/cmd-package-shallow.ts +++ b/src/commands/package/cmd-package-shallow.ts @@ -100,7 +100,7 @@ async function run( fail: 'missing' }, { - hide: !json || !markdown, + nook: true, test: !json || !markdown, message: 'The json and markdown flags cannot be both set, pick one', pass: 'ok', diff --git a/src/commands/package/cmd-package.test.ts b/src/commands/package/cmd-package.test.ts index 7df30fb16..4a2ea5f3c 100644 --- a/src/commands/package/cmd-package.test.ts +++ b/src/commands/package/cmd-package.test.ts @@ -50,7 +50,7 @@ describe('socket package', async () => { ) cmdit( - ['package', '--dry-run', '--config', '{}'], + ['package', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should be ok with org name and id', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/package/fetch-purl-deep-score.ts b/src/commands/package/fetch-purl-deep-score.ts index 21c63daf7..30b81dbf0 100644 --- a/src/commands/package/fetch-purl-deep-score.ts +++ b/src/commands/package/fetch-purl-deep-score.ts @@ -8,9 +8,6 @@ import { AuthError } from '../../utils/errors' import { getDefaultToken } from '../../utils/sdk' export async function fetchPurlDeepScore(purl: string) { - // Lazily access constants.spinner. - const { spinner } = constants - const apiToken = getDefaultToken() if (!apiToken) { throw new AuthError( @@ -18,13 +15,17 @@ export async function fetchPurlDeepScore(purl: string) { ) } + // Lazily access constants.spinner. + const { spinner } = constants + spinner.start('Getting deep package score...') + let result try { result = await queryApi(`purl/score/${encodeURIComponent(purl)}`, apiToken) - spinner?.successAndStop('Received deep package score response.') + spinner.successAndStop('Received deep package score response.') } catch (e) { - spinner?.failAndStop('The request was unsuccessful.') + spinner.failAndStop('The request was unsuccessful.') const msg = (e as undefined | { message: string })?.message if (msg) { logger.fail(msg) diff --git a/src/commands/package/fetch-purls-shallow-score.ts b/src/commands/package/fetch-purls-shallow-score.ts index 6832442dc..bbff1caa1 100644 --- a/src/commands/package/fetch-purls-shallow-score.ts +++ b/src/commands/package/fetch-purls-shallow-score.ts @@ -16,13 +16,13 @@ export async function fetchPurlsShallowScore( `Requesting shallow score data for ${purls.length} package urls (purl): ${purls.join(', ')}` ) + const sockSdk = await setupSdk(getPublicToken()) + // Lazily access constants.spinner. const { spinner } = constants spinner.start(`Requesting data ...`) - const sockSdk = await setupSdk(getPublicToken()) - const result: Awaited> = await handleApiCall( sockSdk.batchPackageFetch( diff --git a/src/commands/raw-npm/cmd-raw-npm.test.ts b/src/commands/raw-npm/cmd-raw-npm.test.ts index 80ef71497..f1a987868 100644 --- a/src/commands/raw-npm/cmd-raw-npm.test.ts +++ b/src/commands/raw-npm/cmd-raw-npm.test.ts @@ -43,7 +43,7 @@ describe('socket raw-npm', async () => { ) cmdit( - ['raw-npm', '--dry-run', '--config', '{}'], + ['raw-npm', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/raw-npx/cmd-raw-npx.test.ts b/src/commands/raw-npx/cmd-raw-npx.test.ts index 00f43a044..d015ede0a 100644 --- a/src/commands/raw-npx/cmd-raw-npx.test.ts +++ b/src/commands/raw-npx/cmd-raw-npx.test.ts @@ -43,7 +43,7 @@ describe('socket raw-npx', async () => { ) cmdit( - ['raw-npx', '--dry-run', '--config', '{}'], + ['raw-npx', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/report/cmd-report-create.test.ts b/src/commands/report/cmd-report-create.test.ts index 0626ee577..2d7a0ee9d 100644 --- a/src/commands/report/cmd-report-create.test.ts +++ b/src/commands/report/cmd-report-create.test.ts @@ -40,7 +40,7 @@ describe('socket report create', async () => { ) cmdit( - ['report', 'create', '--dry-run', '--config', '{}'], + ['report', 'create', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/report/cmd-report-view.test.ts b/src/commands/report/cmd-report-view.test.ts index 0626ee577..2d7a0ee9d 100644 --- a/src/commands/report/cmd-report-view.test.ts +++ b/src/commands/report/cmd-report-view.test.ts @@ -40,7 +40,7 @@ describe('socket report create', async () => { ) cmdit( - ['report', 'create', '--dry-run', '--config', '{}'], + ['report', 'create', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/report/cmd-report-view.ts b/src/commands/report/cmd-report-view.ts index 0e9301dde..20e2ab093 100644 --- a/src/commands/report/cmd-report-view.ts +++ b/src/commands/report/cmd-report-view.ts @@ -54,14 +54,14 @@ async function run( fail: 'missing' }, { - hide: extraInput.length === 0, + nook: true, test: extraInput.length === 0, message: 'Can only handle a single report ID', pass: 'ok', fail: 'received ' + (extraInput.length + 1) }, { - hide: !json || !markdown, + nook: true, test: !json || !markdown, message: 'The json and markdown flags cannot be both set, pick one', pass: 'ok', diff --git a/src/commands/report/cmd-report.test.ts b/src/commands/report/cmd-report.test.ts index cf9617ef1..727128c16 100644 --- a/src/commands/report/cmd-report.test.ts +++ b/src/commands/report/cmd-report.test.ts @@ -51,7 +51,7 @@ describe('socket report', async () => { ) cmdit( - ['report', '--dry-run', '--config', '{}'], + ['report', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/report/fetch-report-data.ts b/src/commands/report/fetch-report-data.ts index 94ca5ce7a..0c2732a9e 100644 --- a/src/commands/report/fetch-report-data.ts +++ b/src/commands/report/fetch-report-data.ts @@ -45,7 +45,7 @@ export async function fetchReportData( spinner.stop(`Failed to fetch report`) throw err } - spinner?.fail(`Retrying report fetch ${retry} / ${MAX_TIMEOUT_RETRY}`) + spinner.fail(`Retrying report fetch ${retry} / ${MAX_TIMEOUT_RETRY}`) } } diff --git a/src/commands/repos/cmd-repos-create.test.ts b/src/commands/repos/cmd-repos-create.test.ts index 520039053..d2cee262a 100644 --- a/src/commands/repos/cmd-repos-create.test.ts +++ b/src/commands/repos/cmd-repos-create.test.ts @@ -68,7 +68,9 @@ describe('socket repos create', async () => { - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) - - Repository name using --repoNam (\\x1b[31minvalid\\x1b[39m)" + - Repository name using --repoNam (\\x1b[31minvalid\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -76,7 +78,16 @@ describe('socket repos create', async () => { ) cmdit( - ['repos', 'create', 'a', '--repoName', 'b', '--dry-run', '--config', '{}'], + [ + 'repos', + 'create', + 'a', + '--repoName', + 'b', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/repos/cmd-repos-create.ts b/src/commands/repos/cmd-repos-create.ts index 10e1daf98..f8d121dd6 100644 --- a/src/commands/repos/cmd-repos-create.ts +++ b/src/commands/repos/cmd-repos-create.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -82,10 +83,11 @@ async function run( const repoName = cli.flags['repoName'] const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' + const apiToken = getDefaultToken() const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', @@ -96,6 +98,14 @@ async function run( message: 'Repository name using --repoNam', pass: 'ok', fail: typeof repoName !== 'string' ? 'missing' : 'invalid' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { diff --git a/src/commands/repos/cmd-repos-del.test.ts b/src/commands/repos/cmd-repos-del.test.ts index 0ca42bf91..65d0941b8 100644 --- a/src/commands/repos/cmd-repos-del.test.ts +++ b/src/commands/repos/cmd-repos-del.test.ts @@ -63,7 +63,9 @@ describe('socket repos del', async () => { - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) - - Repository name argument (\\x1b[31minvalid\\x1b[39m)" + - Repository name argument (\\x1b[31minvalid\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -71,7 +73,15 @@ describe('socket repos del', async () => { ) cmdit( - ['repos', 'del', 'a', 'b', '--dry-run', '--config', '{}'], + [ + 'repos', + 'del', + 'a', + 'b', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/repos/cmd-repos-del.ts b/src/commands/repos/cmd-repos-del.ts index 09fe59bae..0df2cf8dc 100644 --- a/src/commands/repos/cmd-repos-del.ts +++ b/src/commands/repos/cmd-repos-del.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -52,10 +53,11 @@ async function run( const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' const repoName = (defaultOrgSlug ? cli.input[0] : cli.input[1]) || '' + const apiToken = getDefaultToken() const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', @@ -66,6 +68,14 @@ async function run( message: 'Repository name argument', pass: 'ok', fail: typeof repoName !== 'string' ? 'missing' : 'invalid' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { diff --git a/src/commands/repos/cmd-repos-list.test.ts b/src/commands/repos/cmd-repos-list.test.ts index 413d53006..ce2813a0f 100644 --- a/src/commands/repos/cmd-repos-list.test.ts +++ b/src/commands/repos/cmd-repos-list.test.ts @@ -67,7 +67,9 @@ describe('socket repos list', async () => { \\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[1m\\x1b[37m Input error: \\x1b[39m\\x1b[22m\\x1b[49m \\x1b[1mPlease review the input requirements and try again\\x1b[22m: - - Org name as the first argument (\\x1b[31mmissing\\x1b[39m)" + - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -75,7 +77,7 @@ describe('socket repos list', async () => { ) cmdit( - ['repos', 'list', 'a', '--dry-run', '--config', '{}'], + ['repos', 'list', 'a', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/repos/cmd-repos-list.ts b/src/commands/repos/cmd-repos-list.ts index 76b4b46e0..6937ba5c2 100644 --- a/src/commands/repos/cmd-repos-list.ts +++ b/src/commands/repos/cmd-repos-list.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -73,16 +74,36 @@ async function run( parentName }) + const { json, markdown } = cli.flags const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' + const apiToken = getDefaultToken() - const wasBadInput = handleBadInput({ - hide: defaultOrgSlug, - test: orgSlug, - message: 'Org name as the first argument', - pass: 'ok', - fail: 'missing' - }) + const wasBadInput = handleBadInput( + { + nook: true, + test: orgSlug, + message: 'Org name as the first argument', + pass: 'ok', + fail: 'missing' + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' + } + ) if (wasBadInput) { return } @@ -95,11 +116,7 @@ async function run( await handleListRepos({ direction: cli.flags['direction'] === 'asc' ? 'asc' : 'desc', orgSlug, - outputKind: cli.flags['json'] - ? 'json' - : cli.flags['markdown'] - ? 'markdown' - : 'print', + outputKind: json ? 'json' : markdown ? 'markdown' : 'print', page: Number(cli.flags['page']) || 1, per_page: Number(cli.flags['perPage']) || 30, sort: String(cli.flags['sort'] || 'created_at') diff --git a/src/commands/repos/cmd-repos-update.test.ts b/src/commands/repos/cmd-repos-update.test.ts index 069efd3df..73aa83d6b 100644 --- a/src/commands/repos/cmd-repos-update.test.ts +++ b/src/commands/repos/cmd-repos-update.test.ts @@ -68,7 +68,9 @@ describe('socket repos update', async () => { - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) - - Repository name using --repoName (\\x1b[31minvalid\\x1b[39m)" + - Repository name using --repoName (\\x1b[31minvalid\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -76,7 +78,16 @@ describe('socket repos update', async () => { ) cmdit( - ['repos', 'update', 'a', '--repoName', 'b', '--dry-run', '--config', '{}'], + [ + 'repos', + 'update', + 'a', + '--repoName', + 'b', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/repos/cmd-repos-update.ts b/src/commands/repos/cmd-repos-update.ts index f4eefd313..b0b1c2a48 100644 --- a/src/commands/repos/cmd-repos-update.ts +++ b/src/commands/repos/cmd-repos-update.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -82,10 +83,11 @@ async function run( const repoName = cli.flags['repoName'] const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' + const apiToken = getDefaultToken() const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', @@ -96,6 +98,14 @@ async function run( message: 'Repository name using --repoName', pass: 'ok', fail: typeof repoName !== 'string' ? 'missing' : 'invalid' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { diff --git a/src/commands/repos/cmd-repos-view.test.ts b/src/commands/repos/cmd-repos-view.test.ts index 09a62ee89..268ad6755 100644 --- a/src/commands/repos/cmd-repos-view.test.ts +++ b/src/commands/repos/cmd-repos-view.test.ts @@ -66,7 +66,9 @@ describe('socket repos view', async () => { - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) - - Repository name using --repoName (\\x1b[31minvalid\\x1b[39m)" + - Repository name using --repoName (\\x1b[31minvalid\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -74,7 +76,16 @@ describe('socket repos view', async () => { ) cmdit( - ['repos', 'view', 'a', '--repoName', 'b', '--dry-run', '--config', '{}'], + [ + 'repos', + 'view', + 'a', + '--repoName', + 'b', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/repos/cmd-repos-view.ts b/src/commands/repos/cmd-repos-view.ts index bff212ab7..bb0377505 100644 --- a/src/commands/repos/cmd-repos-view.ts +++ b/src/commands/repos/cmd-repos-view.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -59,10 +60,11 @@ async function run( const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' + const apiToken = getDefaultToken() const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', @@ -73,6 +75,22 @@ async function run( message: 'Repository name using --repoName', pass: 'ok', fail: typeof repoName !== 'string' ? 'missing' : 'invalid' + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { diff --git a/src/commands/repos/cmd-repos.test.ts b/src/commands/repos/cmd-repos.test.ts index 515174f6a..af137a9a8 100644 --- a/src/commands/repos/cmd-repos.test.ts +++ b/src/commands/repos/cmd-repos.test.ts @@ -52,7 +52,7 @@ describe('socket repos', async () => { ) cmdit( - ['repos', '--dry-run', '--config', '{}'], + ['repos', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/repos/fetch-create-repo.ts b/src/commands/repos/fetch-create-repo.ts index 908434f06..3927e27f9 100644 --- a/src/commands/repos/fetch-create-repo.ts +++ b/src/commands/repos/fetch-create-repo.ts @@ -1,7 +1,6 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -20,46 +19,11 @@ export async function fetchCreateRepo({ default_branch: string visibility: string }): Promise['data'] | undefined> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - return await fetchCreateRepoWithToken(apiToken, { - default_branch, - description, - homepage, - orgSlug, - repoName, - visibility - }) -} + const sockSdk = await setupSdk() -async function fetchCreateRepoWithToken( - apiToken: string, - { - default_branch, - description, - homepage, - orgSlug, - repoName, - visibility - }: { - orgSlug: string - repoName: string - description: string - homepage: string - default_branch: string - visibility: string - } -): Promise['data'] | undefined> { // Lazily access constants.spinner. const { spinner } = constants - const sockSdk = await setupSdk(apiToken) - spinner.start('Sending request ot create a repository...') const result = await handleApiCall( diff --git a/src/commands/repos/fetch-delete-repo.ts b/src/commands/repos/fetch-delete-repo.ts index 62c741570..195b03fd4 100644 --- a/src/commands/repos/fetch-delete-repo.ts +++ b/src/commands/repos/fetch-delete-repo.ts @@ -1,7 +1,6 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -9,26 +8,11 @@ export async function fetchDeleteRepo( orgSlug: string, repoName: string ): Promise['data'] | undefined> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - return await fetchDeleteRepoWithToken(orgSlug, repoName, apiToken) -} + const sockSdk = await setupSdk() -async function fetchDeleteRepoWithToken( - orgSlug: string, - repoName: string, - apiToken: string -): Promise['data'] | undefined> { // Lazily access constants.spinner. const { spinner } = constants - const sockSdk = await setupSdk(apiToken) - spinner.start('Sending request to delete a repository...') const result = await handleApiCall( diff --git a/src/commands/repos/fetch-list-repos.ts b/src/commands/repos/fetch-list-repos.ts index d3401f790..8ad3b5388 100644 --- a/src/commands/repos/fetch-list-repos.ts +++ b/src/commands/repos/fetch-list-repos.ts @@ -1,7 +1,6 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -18,43 +17,11 @@ export async function fetchListRepos({ per_page: number sort: string }): Promise['data'] | undefined> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - return await fetchListReposWithToken(apiToken, { - direction, - orgSlug, - page, - per_page, - sort - }) -} + const sockSdk = await setupSdk() -async function fetchListReposWithToken( - apiToken: string, - { - direction, - orgSlug, - page, - per_page, - sort - }: { - direction: string - orgSlug: string - page: number - per_page: number - sort: string - } -): Promise['data'] | undefined> { // Lazily access constants.spinner. const { spinner } = constants - const sockSdk = await setupSdk(apiToken) - spinner.start('Fetching list of repositories...') const result = await handleApiCall( diff --git a/src/commands/repos/fetch-update-repo.ts b/src/commands/repos/fetch-update-repo.ts index 9a7a329e8..cdcc69338 100644 --- a/src/commands/repos/fetch-update-repo.ts +++ b/src/commands/repos/fetch-update-repo.ts @@ -1,7 +1,6 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -20,47 +19,13 @@ export async function fetchUpdateRepo({ default_branch: string visibility: string }): Promise['data'] | undefined> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - return await fetchUpdateRepoWithToken(apiToken, { - default_branch, - description, - homepage, - orgSlug, - repoName, - visibility - }) -} + const sockSdk = await setupSdk() -async function fetchUpdateRepoWithToken( - apiToken: string, - { - default_branch, - description, - homepage, - orgSlug, - repoName, - visibility - }: { - orgSlug: string - repoName: string - description: string - homepage: string - default_branch: string - visibility: string - } -): Promise['data'] | undefined> { // Lazily access constants.spinner. const { spinner } = constants spinner.start('Sending request to update a repository...') - const sockSdk = await setupSdk(apiToken) const result = await handleApiCall( sockSdk.updateOrgRepo(orgSlug, repoName, { orgSlug, diff --git a/src/commands/repos/fetch-view-repo.ts b/src/commands/repos/fetch-view-repo.ts index 25965603e..3594630e0 100644 --- a/src/commands/repos/fetch-view-repo.ts +++ b/src/commands/repos/fetch-view-repo.ts @@ -1,7 +1,6 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -9,25 +8,11 @@ export async function fetchViewRepo( orgSlug: string, repoName: string ): Promise['data'] | undefined> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - return await fetchViewRepoWithToken(orgSlug, repoName, apiToken) -} + const sockSdk = await setupSdk() -async function fetchViewRepoWithToken( - orgSlug: string, - repoName: string, - apiToken: string -): Promise['data'] | undefined> { // Lazily access constants.spinner. const { spinner } = constants - const sockSdk = await setupSdk(apiToken) - spinner.start('Fetching repository data...') const result = await handleApiCall( diff --git a/src/commands/repos/handle-delete-repo.ts b/src/commands/repos/handle-delete-repo.ts index affefc450..86cc201a9 100644 --- a/src/commands/repos/handle-delete-repo.ts +++ b/src/commands/repos/handle-delete-repo.ts @@ -1,33 +1,18 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' export async function handleDeleteRepo( orgSlug: string, repoName: string ): Promise { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - await deleteRepoWithToken(orgSlug, repoName, apiToken) -} + const sockSdk = await setupSdk() -async function deleteRepoWithToken( - orgSlug: string, - repoName: string, - apiToken: string -): Promise { // Lazily access constants.spinner. const { spinner } = constants spinner.start('Deleting repository...') - const sockSdk = await setupSdk(apiToken) const result = await handleApiCall( sockSdk.deleteOrgRepo(orgSlug, repoName), 'deleting repository' diff --git a/src/commands/scan/cmd-scan-create.test.ts b/src/commands/scan/cmd-scan-create.test.ts index 291c12f54..739565c46 100644 --- a/src/commands/scan/cmd-scan-create.test.ts +++ b/src/commands/scan/cmd-scan-create.test.ts @@ -82,7 +82,7 @@ describe('socket scan create', async () => { '--branch', 'abc', '--config', - '{"apiKey": "abc"}' + '{"apiToken": "abc"}' ], 'should require args with just dry-run', async cmd => { diff --git a/src/commands/scan/cmd-scan-create.ts b/src/commands/scan/cmd-scan-create.ts index 30c43bf43..d2cc02807 100644 --- a/src/commands/scan/cmd-scan-create.ts +++ b/src/commands/scan/cmd-scan-create.ts @@ -217,7 +217,7 @@ async function run( const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', @@ -242,7 +242,7 @@ async function run( fail: 'missing' }, { - hide: apiToken, + nook: true, test: apiToken, message: 'This command requires an API token for access`)', pass: 'ok', diff --git a/src/commands/scan/cmd-scan-del.test.ts b/src/commands/scan/cmd-scan-del.test.ts index 2a23ac34c..85509ec9a 100644 --- a/src/commands/scan/cmd-scan-del.test.ts +++ b/src/commands/scan/cmd-scan-del.test.ts @@ -65,7 +65,9 @@ describe('socket scan del', async () => { - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) - - Scan ID to delete (\\x1b[31mmissing\\x1b[39m)" + - Scan ID to delete (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -73,7 +75,15 @@ describe('socket scan del', async () => { ) cmdit( - ['scan', 'del', 'fakeorg', 'scanidee', '--dry-run', '--config', '{}'], + [ + 'scan', + 'del', + 'fakeorg', + 'scanidee', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/scan/cmd-scan-del.ts b/src/commands/scan/cmd-scan-del.ts index 7ea140e2e..66d20521d 100644 --- a/src/commands/scan/cmd-scan-del.ts +++ b/src/commands/scan/cmd-scan-del.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -53,10 +54,11 @@ async function run( const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' const scanId = (defaultOrgSlug ? cli.input[0] : cli.input[1]) || '' + const apiToken = getDefaultToken() const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', @@ -67,6 +69,14 @@ async function run( message: 'Scan ID to delete', pass: 'ok', fail: 'missing' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { diff --git a/src/commands/scan/cmd-scan-list.test.ts b/src/commands/scan/cmd-scan-list.test.ts index 1893f644d..ae1686f4b 100644 --- a/src/commands/scan/cmd-scan-list.test.ts +++ b/src/commands/scan/cmd-scan-list.test.ts @@ -69,7 +69,9 @@ describe('socket scan list', async () => { \\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[1m\\x1b[37m Input error: \\x1b[39m\\x1b[22m\\x1b[49m \\x1b[1mPlease review the input requirements and try again\\x1b[22m: - - Org name as the first argument (\\x1b[31mmissing\\x1b[39m)" + - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -77,7 +79,14 @@ describe('socket scan list', async () => { ) cmdit( - ['scan', 'list', 'fakeorg', '--dry-run', '--config', '{}'], + [ + 'scan', + 'list', + 'fakeorg', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/scan/cmd-scan-list.ts b/src/commands/scan/cmd-scan-list.ts index d5a90487c..0f5fcf7b0 100644 --- a/src/commands/scan/cmd-scan-list.ts +++ b/src/commands/scan/cmd-scan-list.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig, @@ -90,16 +91,35 @@ async function run( parentName }) + const { json, markdown } = cli.flags const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' + const apiToken = getDefaultToken() - const wasBadInput = handleBadInput({ - hide: defaultOrgSlug, - test: orgSlug, - message: 'Org name as the first argument', - pass: 'ok', - fail: 'missing' - }) + const wasBadInput = handleBadInput( + { + nook: true, + test: orgSlug, + message: 'Org name as the first argument', + pass: 'ok', + fail: 'missing' + }, + { + nook: true, + test: !json || !markdown, + message: 'The json and markdown flags cannot be both set, pick one', + pass: 'ok', + fail: 'omit one' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' + } + ) if (wasBadInput) { return } @@ -113,11 +133,7 @@ async function run( direction: String(cli.flags['direction'] || ''), from_time: String(cli.flags['fromTime'] || ''), orgSlug, - outputKind: cli.flags['json'] - ? 'json' - : cli.flags['markdown'] - ? 'markdown' - : 'print', + outputKind: json ? 'json' : markdown ? 'markdown' : 'print', page: Number(cli.flags['page'] || 1), per_page: Number(cli.flags['perPage'] || 30), sort: String(cli.flags['sort'] || '') diff --git a/src/commands/scan/cmd-scan-metadata.test.ts b/src/commands/scan/cmd-scan-metadata.test.ts index a79f114e1..0dbf74cc0 100644 --- a/src/commands/scan/cmd-scan-metadata.test.ts +++ b/src/commands/scan/cmd-scan-metadata.test.ts @@ -65,7 +65,9 @@ describe('socket scan metadata', async () => { - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) - - Scan ID to inspect as argument (\\x1b[31mmissing\\x1b[39m)" + - Scan ID to inspect as argument (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -73,7 +75,15 @@ describe('socket scan metadata', async () => { ) cmdit( - ['scan', 'metadata', 'fakeorg', 'scanidee', '--dry-run', '--config', '{}'], + [ + 'scan', + 'metadata', + 'fakeorg', + 'scanidee', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/scan/cmd-scan-metadata.ts b/src/commands/scan/cmd-scan-metadata.ts index 35b4bd075..13c109ecf 100644 --- a/src/commands/scan/cmd-scan-metadata.ts +++ b/src/commands/scan/cmd-scan-metadata.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig, @@ -53,13 +54,15 @@ async function run( parentName }) + const { json, markdown } = cli.flags const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' const scanId = (defaultOrgSlug ? cli.input[0] : cli.input[1]) || '' + const apiToken = getDefaultToken() const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', @@ -70,6 +73,21 @@ async function run( message: 'Scan ID to inspect as argument', pass: 'ok', fail: 'missing' + }, + { + nook: true, + test: !json || !markdown, + message: 'The json and markdown flags cannot be both set, pick one', + pass: 'ok', + fail: 'omit one' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { @@ -84,6 +102,6 @@ async function run( await handleOrgScanMetadata( orgSlug, scanId, - cli.flags['json'] ? 'json' : cli.flags['markdown'] ? 'markdown' : 'print' + json ? 'json' : markdown ? 'markdown' : 'text' ) } diff --git a/src/commands/scan/cmd-scan-report.test.ts b/src/commands/scan/cmd-scan-report.test.ts index 5acf5bf95..52b57184d 100644 --- a/src/commands/scan/cmd-scan-report.test.ts +++ b/src/commands/scan/cmd-scan-report.test.ts @@ -85,7 +85,9 @@ describe('socket scan report', async () => { - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) - - Scan ID to fetch (\\x1b[31mmissing\\x1b[39m)" + - Scan ID to fetch (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -93,7 +95,15 @@ describe('socket scan report', async () => { ) cmdit( - ['scan', 'report', 'org', 'report-id', '--dry-run', '--config', '{}'], + [ + 'scan', + 'report', + 'org', + 'report-id', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should be ok with org name and id', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/scan/cmd-scan-report.ts b/src/commands/scan/cmd-scan-report.ts index e29324d78..a61607857 100644 --- a/src/commands/scan/cmd-scan-report.ts +++ b/src/commands/scan/cmd-scan-report.ts @@ -7,6 +7,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig, @@ -108,10 +109,11 @@ async function run( const orgSlug = defaultOrgSlug || cli.input[0] || '' const scanId = (defaultOrgSlug ? cli.input[0] : cli.input[1]) || '' const file = (defaultOrgSlug ? cli.input[1] : cli.input[2]) || '-' + const apiToken = getDefaultToken() const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', @@ -124,11 +126,19 @@ async function run( fail: 'missing' }, { - hide: !json || !markdown, + nook: true, test: !json || !markdown, message: 'The json and markdown flags cannot be both set, pick one', pass: 'ok', fail: 'omit one' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { diff --git a/src/commands/scan/cmd-scan-view.test.ts b/src/commands/scan/cmd-scan-view.test.ts index b15b9deea..73627ad09 100644 --- a/src/commands/scan/cmd-scan-view.test.ts +++ b/src/commands/scan/cmd-scan-view.test.ts @@ -67,7 +67,9 @@ describe('socket scan view', async () => { - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) - - Scan ID to delete (\\x1b[31mmissing\\x1b[39m)" + - Scan ID to delete (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" `) expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) @@ -75,7 +77,15 @@ describe('socket scan view', async () => { ) cmdit( - ['scan', 'view', 'fakeorg', 'scanidee', '--dry-run', '--config', '{}'], + [ + 'scan', + 'view', + 'fakeorg', + 'scanidee', + '--dry-run', + '--config', + '{"apiToken":"anything"}' + ], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/scan/cmd-scan-view.ts b/src/commands/scan/cmd-scan-view.ts index d8d655bd2..db3d5ff59 100644 --- a/src/commands/scan/cmd-scan-view.ts +++ b/src/commands/scan/cmd-scan-view.ts @@ -8,6 +8,7 @@ import { getConfigValue } from '../../utils/config' import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig, @@ -56,14 +57,16 @@ async function run( parentName }) + const { json, markdown } = cli.flags const defaultOrgSlug = getConfigValue('defaultOrg') const orgSlug = defaultOrgSlug || cli.input[0] || '' const scanId = (defaultOrgSlug ? cli.input[0] : cli.input[1]) || '' const file = (defaultOrgSlug ? cli.input[1] : cli.input[2]) || '-' + const apiToken = getDefaultToken() const wasBadInput = handleBadInput( { - hide: defaultOrgSlug, + nook: true, test: orgSlug, message: 'Org name as the first argument', pass: 'ok', @@ -74,6 +77,22 @@ async function run( message: 'Scan ID to delete', pass: 'ok', fail: 'missing' + }, + { + nook: true, + test: !json || !markdown, + message: + 'The `--json` and `--markdown` flags can not be used at the same time', + pass: 'ok', + fail: 'bad' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' } ) if (wasBadInput) { @@ -85,7 +104,7 @@ async function run( return } - if (cli.flags['json']) { + if (json) { await streamScan(orgSlug, scanId, file) } else { await handleScanView(orgSlug, scanId, file) diff --git a/src/commands/scan/cmd-scan.test.ts b/src/commands/scan/cmd-scan.test.ts index 7bc557cef..c4574418d 100644 --- a/src/commands/scan/cmd-scan.test.ts +++ b/src/commands/scan/cmd-scan.test.ts @@ -52,7 +52,7 @@ describe('socket scan', async () => { ) cmdit( - ['scan', '--dry-run', '--config', '{}'], + ['scan', '--dry-run', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/scan/fetch-delete-org-full-scan.ts b/src/commands/scan/fetch-delete-org-full-scan.ts index 2afa94fab..7f02ddc32 100644 --- a/src/commands/scan/fetch-delete-org-full-scan.ts +++ b/src/commands/scan/fetch-delete-org-full-scan.ts @@ -1,7 +1,6 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -9,26 +8,11 @@ export async function fetchDeleteOrgFullScan( orgSlug: string, scanId: string ): Promise['data'] | void> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - await fetchDeleteOrgFullScanWithToken(apiToken, orgSlug, scanId) -} + const sockSdk = await setupSdk() -async function fetchDeleteOrgFullScanWithToken( - apiToken: string, - orgSlug: string, - scanId: string -): Promise['data'] | void> { // Lazily access constants.spinner. const { spinner } = constants - const sockSdk = await setupSdk(apiToken) - spinner.start('Requesting the scan to be deleted...') const result = await handleApiCall( diff --git a/src/commands/scan/fetch-list-scans.ts b/src/commands/scan/fetch-list-scans.ts index 0860a3a84..26705f992 100644 --- a/src/commands/scan/fetch-list-scans.ts +++ b/src/commands/scan/fetch-list-scans.ts @@ -1,7 +1,6 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -20,46 +19,11 @@ export async function fetchListScans({ per_page: number sort: string }): Promise['data'] | void> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - await fetchListScansWithToken(apiToken, { - direction, - from_time, - orgSlug, - page, - per_page, - sort - }) -} + const sockSdk = await setupSdk() -async function fetchListScansWithToken( - apiToken: string, - { - direction, - from_time, - orgSlug, - page, - per_page, - sort - }: { - direction: string - from_time: string // seconds - orgSlug: string - page: number - per_page: number - sort: string - } -): Promise['data'] | void> { // Lazily access constants.spinner. const { spinner } = constants - const sockSdk = await setupSdk(apiToken) - spinner.start('Fetching list of scans...') const result = await handleApiCall( diff --git a/src/commands/scan/fetch-report-data.ts b/src/commands/scan/fetch-report-data.ts index 82eb95c33..ce703bfce 100644 --- a/src/commands/scan/fetch-report-data.ts +++ b/src/commands/scan/fetch-report-data.ts @@ -73,7 +73,7 @@ export async function fetchReportData( `Fetching ${needs.join(needs.length > 2 ? ', ' : ' and ')}...${haves.length ? ` Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.` : ''}` ) } else { - spinner?.successAndStop( + spinner.successAndStop( `Completed fetching ${haves.join(haves.length > 2 ? ', ' : ' and ')}.` ) } diff --git a/src/commands/scan/fetch-scan-metadata.ts b/src/commands/scan/fetch-scan-metadata.ts index a162d8c23..bb0c0bbe9 100644 --- a/src/commands/scan/fetch-scan-metadata.ts +++ b/src/commands/scan/fetch-scan-metadata.ts @@ -1,7 +1,6 @@ import constants from '../../constants' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken, setupSdk } from '../../utils/sdk' +import { setupSdk } from '../../utils/sdk' import type { SocketSdkReturnType } from '@socketsecurity/sdk' @@ -9,26 +8,11 @@ export async function fetchScanMetadata( orgSlug: string, scanId: string ): Promise['data'] | void> { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } - - await fetchScanMetadataWithToken(apiToken, orgSlug, scanId) -} + const sockSdk = await setupSdk() -async function fetchScanMetadataWithToken( - apiToken: string, - orgSlug: string, - scanId: string -): Promise['data'] | void> { // Lazily access constants.spinner. const { spinner } = constants - const sockSdk = await setupSdk(apiToken) - spinner.start('Fetching meta data for a full scan...') const result = await handleApiCall( diff --git a/src/commands/scan/fetch-scan.ts b/src/commands/scan/fetch-scan.ts index 130ca25d8..feb91b363 100644 --- a/src/commands/scan/fetch-scan.ts +++ b/src/commands/scan/fetch-scan.ts @@ -13,9 +13,6 @@ export async function fetchScan( orgSlug: string, scanId: string ): Promise | undefined> { - // Lazily access constants.spinner. - const { spinner } = constants - const apiToken = getDefaultToken() if (!apiToken) { throw new AuthError( @@ -23,6 +20,9 @@ export async function fetchScan( ) } + // Lazily access constants.spinner. + const { spinner } = constants + spinner.start('Fetching full-scan...') const response = await queryApi( diff --git a/src/commands/scan/handle-scan-metadata.ts b/src/commands/scan/handle-scan-metadata.ts index 0292f93fb..ee8e14666 100644 --- a/src/commands/scan/handle-scan-metadata.ts +++ b/src/commands/scan/handle-scan-metadata.ts @@ -4,7 +4,7 @@ import { outputScanMetadata } from './output-scan-metadata' export async function handleOrgScanMetadata( orgSlug: string, scanId: string, - outputKind: 'json' | 'markdown' | 'print' + outputKind: 'json' | 'markdown' | 'text' ): Promise { const data = await fetchScanMetadata(orgSlug, scanId) if (!data) { diff --git a/src/commands/scan/output-scan-metadata.ts b/src/commands/scan/output-scan-metadata.ts index c5061ed5a..8d3f171f1 100644 --- a/src/commands/scan/output-scan-metadata.ts +++ b/src/commands/scan/output-scan-metadata.ts @@ -5,7 +5,7 @@ import type { SocketSdkReturnType } from '@socketsecurity/sdk' export async function outputScanMetadata( data: SocketSdkReturnType<'getOrgFullScanMetadata'>['data'], scanId: string, - outputKind: 'json' | 'markdown' | 'print' + outputKind: 'json' | 'markdown' | 'text' ): Promise { if (outputKind === 'json') { logger.log(data) diff --git a/src/commands/scan/streamScan.ts b/src/commands/scan/streamScan.ts index ababc1c4c..589c48bcb 100644 --- a/src/commands/scan/streamScan.ts +++ b/src/commands/scan/streamScan.ts @@ -29,7 +29,7 @@ export async function streamScan( 'Fetching a scan' ) - spinner?.successAndStop( + spinner.successAndStop( file ? `Full scan details written to ${file}` : 'stdout' ) diff --git a/src/commands/threat-feed/cmd-threat-feed.test.ts b/src/commands/threat-feed/cmd-threat-feed.test.ts index 244e36fdb..636fb4273 100644 --- a/src/commands/threat-feed/cmd-threat-feed.test.ts +++ b/src/commands/threat-feed/cmd-threat-feed.test.ts @@ -83,6 +83,30 @@ describe('socket threat-feed', async () => { cmdit( ['threat-feed', '--dry-run', '--config', '{}'], '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 + |__ | . | _| '_| -_| _| | Node: , API token set: + |_____|___|___|_,_|___|_|.dev | Command: \`socket threat-feed\`, cwd: + + \\x1b[31m\\xd7\\x1b[39m \\x1b[41m\\x1b[1m\\x1b[37m Input error: \\x1b[39m\\x1b[22m\\x1b[49m \\x1b[1mPlease review the input requirements and try again\\x1b[22m: + + - Org name as the first argument (\\x1b[31mmissing\\x1b[39m) + + - You need to be logged in to use this command. See \`socket login\`. (\\x1b[31mmissing API token\\x1b[39m)" + `) + + expect(code, 'dry-run should exit with code 2 if missing input').toBe(2) + } + ) + + cmdit( + ['threat-feed', 'boo', '--dry-run', '--config', '{"apiToken":"anything"}'], + 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`) diff --git a/src/commands/threat-feed/cmd-threat-feed.ts b/src/commands/threat-feed/cmd-threat-feed.ts index 4d1742a0d..a253e9265 100644 --- a/src/commands/threat-feed/cmd-threat-feed.ts +++ b/src/commands/threat-feed/cmd-threat-feed.ts @@ -1,10 +1,13 @@ import { logger } from '@socketsecurity/registry/lib/logger' -import { getThreatFeed } from './get-threat-feed' +import { handleThreatFeed } from './handle-threat-feed' import constants from '../../constants' import { commonFlags, outputFlags } from '../../flags' +import { getConfigValue } from '../../utils/config' +import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import { getFlagListOutput } from '../../utils/output-formatting' +import { getDefaultToken } from '../../utils/sdk' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' @@ -105,20 +108,49 @@ async function run( parentName }) + const { json, markdown } = cli.flags + const defaultOrgSlug = getConfigValue('defaultOrg') + const orgSlug = defaultOrgSlug || cli.input[0] || '' + const apiToken = getDefaultToken() + + const wasBadInput = handleBadInput( + { + nook: true, + test: orgSlug, + message: 'Org name as the first argument', + pass: 'ok', + fail: 'missing' + }, + { + nook: true, + test: !json || !markdown, + message: 'The json and markdown flags cannot be both set, pick one', + pass: 'ok', + fail: 'omit one' + }, + { + nook: true, + test: apiToken, + message: + 'You need to be logged in to use this command. See `socket login`.', + pass: 'ok', + fail: 'missing API token' + } + ) + if (wasBadInput) { + return + } + if (cli.flags['dryRun']) { logger.log(DRY_RUN_BAIL_TEXT) return } - await getThreatFeed({ + await handleThreatFeed({ direction: String(cli.flags['direction'] || 'desc'), ecosystem: String(cli.flags['eco'] || ''), filter: String(cli.flags['filter'] || 'mal'), - outputKind: cli.flags['json'] - ? 'json' - : cli.flags['markdown'] - ? 'markdown' - : 'print', + outputKind: json ? 'json' : markdown ? 'markdown' : 'text', page: String(cli.flags['page'] || '1'), perPage: Number(cli.flags['perPage']) || 30 }) diff --git a/src/commands/threat-feed/fetch-threat-feed.ts b/src/commands/threat-feed/fetch-threat-feed.ts new file mode 100644 index 000000000..883fa338d --- /dev/null +++ b/src/commands/threat-feed/fetch-threat-feed.ts @@ -0,0 +1,48 @@ +import constants from '../../constants' +import { queryApi } from '../../utils/api' +import { AuthError } from '../../utils/errors' +import { getDefaultToken } from '../../utils/sdk' + +import type { ThreadFeedResponse } from './types' + +export async function fetchThreatFeed({ + direction, + ecosystem, + filter, + page, + perPage +}: { + direction: string + ecosystem: string + filter: string + page: string + perPage: number +}): Promise { + const queryParams = new URLSearchParams([ + ['direction', direction], + ['ecosystem', ecosystem], + ['filter', filter], + ['page', page], + ['per_page', String(perPage)] + ]) + + const apiToken = getDefaultToken() + if (!apiToken) { + throw new AuthError( + 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' + ) + } + + // Lazily access constants.spinner. + const { spinner } = constants + + spinner.start('Fetching Threat Feed data...') + + const response = await queryApi(`threat-feed?${queryParams}`, apiToken) + + spinner.successAndStop('Threat feed data fetched') + + const data = await response.json() + + return data as ThreadFeedResponse | { error: { message: string } } +} diff --git a/src/commands/threat-feed/handle-threat-feed.ts b/src/commands/threat-feed/handle-threat-feed.ts new file mode 100644 index 000000000..e5813c115 --- /dev/null +++ b/src/commands/threat-feed/handle-threat-feed.ts @@ -0,0 +1,40 @@ +import { fetchThreatFeed } from './fetch-threat-feed' +import { outputThreatFeed } from './output-threat-feed' + +import type { ThreadFeedResponse } from './types' + +export async function handleThreatFeed({ + direction, + ecosystem, + filter, + outputKind, + page, + perPage +}: { + direction: string + ecosystem: string + filter: string + outputKind: 'json' | 'markdown' | 'text' + page: string + perPage: number +}): Promise { + const data = await fetchThreatFeed({ + direction, + ecosystem, + filter, + page, + perPage + }) + if (!data) { + return + } + + if ('error' in data && data.error) { + console.log(data.error.message) + return + } + + await outputThreatFeed(data as ThreadFeedResponse, { + outputKind + }) +} diff --git a/src/commands/threat-feed/get-threat-feed.ts b/src/commands/threat-feed/output-threat-feed.ts similarity index 67% rename from src/commands/threat-feed/get-threat-feed.ts rename to src/commands/threat-feed/output-threat-feed.ts index 654717af7..bac3575c6 100644 --- a/src/commands/threat-feed/get-threat-feed.ts +++ b/src/commands/threat-feed/output-threat-feed.ts @@ -1,5 +1,3 @@ -import process from 'node:process' - // @ts-ignore import BoxWidget from 'blessed/lib/widgets/box' // @ts-ignore @@ -9,101 +7,37 @@ import TableWidget from 'blessed-contrib/lib/widget/table' import { logger } from '@socketsecurity/registry/lib/logger' -import constants from '../../constants' -import { queryApi } from '../../utils/api' -import { AuthError } from '../../utils/errors' -import { getDefaultToken } from '../../utils/sdk' - -import type { Widgets } from 'blessed' // Note: Widgets does not seem to actually work as code :'( - -type ThreatResult = { - createdAt: string - description: string - id: number - locationHtmlUrl: string - packageHtmlUrl: string - purl: string - removedAt: string - threatType: string -} - -export async function getThreatFeed({ - direction, - ecosystem, - filter, - outputKind, - page, - perPage -}: { - direction: string - ecosystem: string - filter: string - outputKind: 'json' | 'markdown' | 'print' - page: string - perPage: number -}): Promise { - const apiToken = getDefaultToken() - if (!apiToken) { - throw new AuthError( - 'User must be authenticated to run this command. To log in, run the command `socket login` and enter your API key.' - ) - } +import { ThreadFeedResponse, ThreatResult } from './types' - await getThreatFeedWithToken({ - apiToken, - direction, - ecosystem, - filter, - outputKind, - page, - perPage - }) -} +import type { Widgets } from 'blessed' -async function getThreatFeedWithToken({ - apiToken, - direction, - ecosystem, - filter, - outputKind, - page, - perPage -}: { - apiToken: string - direction: string - ecosystem: string - filter: string - outputKind: 'json' | 'markdown' | 'print' - page: string - perPage: number -}): Promise { - // Lazily access constants.spinner. - const { spinner } = constants - - const queryParams = new URLSearchParams([ - ['direction', direction], - ['ecosystem', ecosystem], - ['filter', filter], - ['page', page], - ['per_page', String(perPage)] - ]) - - spinner.start('Fetching Threat Feed data...') - - const response = await queryApi(`threat-feed?${queryParams}`, apiToken) - const data = (await response.json()) as { - results: ThreatResult[] - nextPage: string +export async function outputThreatFeed( + data: ThreadFeedResponse, + { + outputKind + }: { + outputKind: 'json' | 'markdown' | 'text' } - - spinner.stop('Threat feed data fetched') - +) { if (outputKind === 'json') { logger.log(data) return } + if (!data?.results?.length) { + logger.error('Did not receive any data to display...') + return + } + + const formattedOutput = formatResults(data.results) + const descriptions = data.results.map(d => d.description) + + // Note: this temporarily takes over the terminal (just like `man` does). const screen: Widgets.Screen = new ScreenWidget() + // Register these keys first so you can always exit, even when it gets stuck + // If we don't do this and the code crashes, the user must hard-kill the + // node process just to exit it. That's very bad UX. + screen.key(['escape', 'q', 'C-c'], () => process.exit(0)) const table: any = new TableWidget({ keys: 'true', @@ -142,15 +76,6 @@ async function getThreatFeedWithToken({ } }) - // allow control the table with the keyboard - table.focus() - - screen.append(table) - screen.append(detailsBox) - - const formattedOutput = formatResults(data.results) - const descriptions = data.results.map(d => d.description) - table.setData({ headers: [ ' Ecosystem', @@ -163,6 +88,12 @@ async function getThreatFeedWithToken({ data: formattedOutput }) + // allow control the table with the keyboard + table.focus() + + screen.append(table) + screen.append(detailsBox) + // Update details box when selection changes table.rows.on('select item', () => { const selectedIndex = table.rows.selected @@ -186,12 +117,11 @@ async function getThreatFeedWithToken({ screen.render() - screen.key(['escape', 'q', 'C-c'], () => process.exit(0)) screen.key(['return'], () => { const selectedIndex = table.rows.selected screen.destroy() const selectedRow = formattedOutput[selectedIndex] - console.log(selectedRow) + logger.log('Last selection:\n', selectedRow) }) } diff --git a/src/commands/threat-feed/types.ts b/src/commands/threat-feed/types.ts new file mode 100644 index 000000000..5946e3175 --- /dev/null +++ b/src/commands/threat-feed/types.ts @@ -0,0 +1,15 @@ +export interface ThreadFeedResponse { + results: ThreatResult[] + nextPage: string +} + +export type ThreatResult = { + createdAt: string + description: string + id: number + locationHtmlUrl: string + packageHtmlUrl: string + purl: string + removedAt: string | null + threatType: string +} diff --git a/src/commands/wrapper/cmd-wrapper.test.ts b/src/commands/wrapper/cmd-wrapper.test.ts index d810cd208..5c67ae971 100644 --- a/src/commands/wrapper/cmd-wrapper.test.ts +++ b/src/commands/wrapper/cmd-wrapper.test.ts @@ -72,7 +72,7 @@ describe('socket wrapper', async () => { ) cmdit( - ['wrapper', '--dry-run', '--enable'], + ['wrapper', '--dry-run', '--enable', '--config', '{"apiToken":"anything"}'], 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) diff --git a/src/commands/wrapper/cmd-wrapper.ts b/src/commands/wrapper/cmd-wrapper.ts index 6522ecaab..5a42df500 100644 --- a/src/commands/wrapper/cmd-wrapper.ts +++ b/src/commands/wrapper/cmd-wrapper.ts @@ -80,7 +80,7 @@ async function run( fail: 'missing' }, { - hide: !enable || !disable, + nook: true, test: !enable || !disable, message: 'Do not use both --enable and --disable', pass: 'ok', diff --git a/src/utils/handle-bad-input.ts b/src/utils/handle-bad-input.ts index 2ca426248..93163d641 100644 --- a/src/utils/handle-bad-input.ts +++ b/src/utils/handle-bad-input.ts @@ -5,7 +5,7 @@ import { logger } from '@socketsecurity/registry/lib/logger' export function handleBadInput( ...arr: Array<{ message: string - hide?: unknown // Truthy checked through !! + nook?: unknown // Only display error when test is not OK test: unknown // Truthy checked through !! pass: string fail: string @@ -20,7 +20,8 @@ export function handleBadInput( '' ] for (const data of arr) { - if (data.hide) { + // If nook, then ignore when test is ok + if (data.nook && data.test) { continue } const lines = data.message.split('\n')