diff --git a/src/commands/report/cmd-report-create.test.ts b/src/commands/report/cmd-report-create.test.ts index 2d7a0ee9d..4425868ed 100644 --- a/src/commands/report/cmd-report-create.test.ts +++ b/src/commands/report/cmd-report-create.test.ts @@ -44,13 +44,15 @@ describe('socket report create', async () => { 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) - expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`) + expect(stdout).toMatchInlineSnapshot(`""`) expect(`\n ${stderr}`).toMatchInlineSnapshot(` " _____ _ _ /--------------- | __|___ ___| |_ ___| |_ | Socket.dev CLI ver |__ | . | _| '_| -_| _| | Node: , API token set: - |_____|___|___|_,_|___|_|.dev | Command: \`socket report create\`, cwd: " + |_____|___|___|_,_|___|_|.dev | Command: \`socket report create\`, cwd: + + \\x1b[31m\\xd7\\x1b[39m This command has been sunset. Instead, please look at \`socket scan create\` to create scans and \`socket scan report\` to view a report of your scans." `) expect(code, 'dry-run should exit with code 0 if input ok').toBe(0) diff --git a/src/commands/report/cmd-report-create.ts b/src/commands/report/cmd-report-create.ts index 7fad642b1..08fa7bb6d 100644 --- a/src/commands/report/cmd-report-create.ts +++ b/src/commands/report/cmd-report-create.ts @@ -1,19 +1,10 @@ -import path from 'node:path' - import { logger } from '@socketsecurity/registry/lib/logger' -import { createReport } from './create-report' -import { getSocketConfig } from './get-socket-config' -import { viewReport } from './view-report' -import constants from '../../constants' import { commonFlags, outputFlags, validationFlags } from '../../flags' -import { ColorOrMarkdown } from '../../utils/color-or-markdown' import { meowOrExit } from '../../utils/meow-with-subcommands' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' -const { DRY_RUN_BAIL_TEXT } = constants - const config: CliCommandConfig = { commandName: 'create', description: '[Deprecated] Create a project report', @@ -51,52 +42,14 @@ async function run( importMeta: ImportMeta, { parentName }: { parentName: string } ): Promise { - const cli = meowOrExit({ + meowOrExit({ argv, config, importMeta, parentName }) - // TODO: Allow setting a custom cwd and/or configFile path? - const cwd = process.cwd() - const absoluteConfigPath = path.join(cwd, 'socket.yml') - - const dryRun = Boolean(cli.flags['dryRun']) - const json = Boolean(cli.flags['json']) - const markdown = Boolean(cli.flags['markdown']) - const strict = Boolean(cli.flags['strict']) - const includeAllIssues = Boolean(cli.flags['all']) - const view = Boolean(cli.flags['view']) - - // Note exiting earlier to skirt a hidden auth requirement - if (cli.flags['dryRun']) { - logger.log(DRY_RUN_BAIL_TEXT) - return - } - - const socketConfig = await getSocketConfig(absoluteConfigPath) - - const result = await createReport(socketConfig, cli.input, { cwd, dryRun }) - - const commandName = `${parentName} ${config.commandName}` - - if (result?.success) { - if (view) { - const reportId = result.data.id - await viewReport(reportId, { - all: includeAllIssues, - commandName, - outputKind: json ? 'json' : markdown ? 'markdown' : 'print', - strict - }) - } else if (json) { - logger.log(JSON.stringify(result.data, undefined, 2)) - } else { - const format = new ColorOrMarkdown(markdown) - logger.log( - `New report: ${format.hyperlink(result.data.id, result.data.url, { fallbackToUrl: true })}` - ) - } - } + logger.fail( + 'This command has been sunset. Instead, please look at `socket scan create` to create scans and `socket scan report` to view a report of your scans.' + ) } diff --git a/src/commands/report/cmd-report-view.test.ts b/src/commands/report/cmd-report-view.test.ts index 2d7a0ee9d..4425868ed 100644 --- a/src/commands/report/cmd-report-view.test.ts +++ b/src/commands/report/cmd-report-view.test.ts @@ -44,13 +44,15 @@ describe('socket report create', async () => { 'should require args with just dry-run', async cmd => { const { code, stderr, stdout } = await invokeNpm(entryPath, cmd) - expect(stdout).toMatchInlineSnapshot(`"[DryRun]: Bailing now"`) + expect(stdout).toMatchInlineSnapshot(`""`) expect(`\n ${stderr}`).toMatchInlineSnapshot(` " _____ _ _ /--------------- | __|___ ___| |_ ___| |_ | Socket.dev CLI ver |__ | . | _| '_| -_| _| | Node: , API token set: - |_____|___|___|_,_|___|_|.dev | Command: \`socket report create\`, cwd: " + |_____|___|___|_,_|___|_|.dev | Command: \`socket report create\`, cwd: + + \\x1b[31m\\xd7\\x1b[39m This command has been sunset. Instead, please look at \`socket scan create\` to create scans and \`socket scan report\` to view a report of your scans." `) expect(code, 'dry-run should exit with code 0 if input ok').toBe(0) diff --git a/src/commands/report/cmd-report-view.ts b/src/commands/report/cmd-report-view.ts index 20e2ab093..80c09206b 100644 --- a/src/commands/report/cmd-report-view.ts +++ b/src/commands/report/cmd-report-view.ts @@ -1,15 +1,10 @@ import { logger } from '@socketsecurity/registry/lib/logger' -import { viewReport } from './view-report' -import constants from '../../constants' import { commonFlags, outputFlags, validationFlags } from '../../flags' -import { handleBadInput } from '../../utils/handle-bad-input' import { meowOrExit } from '../../utils/meow-with-subcommands' import type { CliCommandConfig } from '../../utils/meow-with-subcommands' -const { DRY_RUN_BAIL_TEXT } = constants - const config: CliCommandConfig = { commandName: 'view', description: '[Deprecated] View a project report', @@ -36,51 +31,14 @@ async function run( importMeta: ImportMeta, { parentName }: { parentName: string } ): Promise { - const cli = meowOrExit({ + meowOrExit({ argv, config, importMeta, parentName }) - const { json, markdown } = cli.flags - const [reportId = '', ...extraInput] = cli.input - - const wasBadInput = handleBadInput( - { - test: reportId, - message: 'Need at least one report ID', - pass: 'ok', - fail: 'missing' - }, - { - nook: true, - test: extraInput.length === 0, - message: 'Can only handle a single report ID', - pass: 'ok', - fail: 'received ' + (extraInput.length + 1) - }, - { - nook: true, - test: !json || !markdown, - message: 'The json and markdown flags cannot be both set, pick one', - pass: 'ok', - fail: 'omit one' - } + logger.fail( + 'This command has been sunset. Instead, please look at `socket scan create` to create scans and `socket scan report` to view a report of your scans.' ) - if (wasBadInput) { - return - } - - if (cli.flags['dryRun']) { - logger.log(DRY_RUN_BAIL_TEXT) - return - } - - await viewReport(reportId, { - all: Boolean(cli.flags['all']), - commandName: `${parentName} ${config.commandName}`, - outputKind: json ? 'json' : markdown ? 'markdown' : 'print', - strict: Boolean(cli.flags['strict']) - }) } diff --git a/src/commands/report/create-report.ts b/src/commands/report/create-report.ts deleted file mode 100644 index 84e8410a8..000000000 --- a/src/commands/report/create-report.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { debugLog, isDebug } from '@socketsecurity/registry/lib/debug' -import { pluralize } from '@socketsecurity/registry/lib/words' - -import constants from '../../constants' -import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { getPackageFilesForScan } from '../../utils/path-resolve' -import { setupSdk } from '../../utils/sdk' - -import type { SocketYml } from '@socketsecurity/config' -import type { - SocketSdkResultType, - SocketSdkReturnType -} from '@socketsecurity/sdk' - -const { DRY_RUN_LABEL } = constants - -export async function createReport( - socketConfig: SocketYml | undefined, - inputPaths: string[], - { - cwd, - dryRun - }: { - cwd: string - dryRun: boolean - } -): Promise> { - // Lazily access constants.spinner. - const { spinner } = constants - const sockSdk = await setupSdk() - const supportedFiles = await sockSdk - .getReportSupportedFiles() - .then(res => { - if (!res.success) { - handleUnsuccessfulApiResponse('getReportSupportedFiles', res) - } - return (res as SocketSdkReturnType<'getReportSupportedFiles'>).data - }) - .catch((cause: Error) => { - throw new Error('Failed getting supported files for report', { - cause - }) - }) - const packagePaths = await getPackageFilesForScan( - cwd, - inputPaths, - supportedFiles, - socketConfig - ) - const packagePathsCount = packagePaths.length - if (packagePathsCount && isDebug()) { - for (const pkgPath of packagePaths) { - debugLog(`Uploading: ${pkgPath}`) - } - } - if (dryRun) { - debugLog(`${DRY_RUN_LABEL}: Skipped actual upload`) - return - } - spinner.start( - `Creating report with ${packagePathsCount} package ${pluralize('file', packagePathsCount)}` - ) - const apiCall = sockSdk.createReportFromFilePaths( - packagePaths, - cwd, - socketConfig?.issueRules - ) - const result = await handleApiCall(apiCall, 'creating report') - if (!result.success) { - handleUnsuccessfulApiResponse('createReport', result) - } - spinner.successAndStop() - return result -} diff --git a/src/commands/report/fetch-report-data.ts b/src/commands/report/fetch-report-data.ts deleted file mode 100644 index bd89364ea..000000000 --- a/src/commands/report/fetch-report-data.ts +++ /dev/null @@ -1,76 +0,0 @@ -import constants from '../../constants' -import { - formatSeverityCount, - getSeverityCount -} from '../../utils/alert/severity' -import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api' -import { setupSdk } from '../../utils/sdk' - -import type { - SocketSdkResultType, - SocketSdkReturnType -} from '@socketsecurity/sdk' - -export type ReportData = SocketSdkReturnType<'getReport'>['data'] - -const MAX_TIMEOUT_RETRY = 5 -const HTTP_CODE_TIMEOUT = 524 - -export async function fetchReportData( - reportId: string, - includeAllIssues: boolean, - strict: boolean -): Promise { - // Lazily access constants.spinner. - const { spinner } = constants - - spinner.log('Fetching report with ID ${reportId} (this could take a while)') - spinner.start(`Fetch started... (this could take a while)`) - - const sockSdk = await setupSdk() - let result: SocketSdkResultType<'getReport'> | undefined - for (let retry = 1; !result; ++retry) { - try { - // eslint-disable-next-line no-await-in-loop - result = await handleApiCall( - sockSdk.getReport(reportId), - 'fetching report' - ) - } catch (err) { - if ( - retry >= MAX_TIMEOUT_RETRY || - !(err instanceof Error) || - (err.cause as any)?.cause?.response?.statusCode !== HTTP_CODE_TIMEOUT - ) { - spinner.stop(`Failed to fetch report`) - throw err - } - spinner.fail(`Retrying report fetch ${retry} / ${MAX_TIMEOUT_RETRY}`) - } - } - - if (!result.success) { - handleUnsuccessfulApiResponse('getReport', result) - } - - // Conclude the status of the API call. - if (strict) { - if (result.data.healthy) { - spinner.success('Report result is healthy and great!') - } else { - spinner.error('Report result deemed unhealthy for project') - } - } else if (!result.data.healthy) { - const severityCount = getSeverityCount( - result.data.issues, - includeAllIssues ? undefined : 'high' - ) - const issueSummary = formatSeverityCount(severityCount) - spinner.success(`Report has these issues: ${issueSummary}`) - } else { - spinner.success('Report has no issues') - } - spinner.stop() - - return result.data -} diff --git a/src/commands/report/format-report-data.ts b/src/commands/report/format-report-data.ts deleted file mode 100644 index 3d080710a..000000000 --- a/src/commands/report/format-report-data.ts +++ /dev/null @@ -1,44 +0,0 @@ -import process from 'node:process' - -import { stripIndents } from 'common-tags' -import colors from 'yoctocolors-cjs' - -import { logger } from '@socketsecurity/registry/lib/logger' - -import { ColorOrMarkdown } from '../../utils/color-or-markdown' - -import type { ReportData } from './fetch-report-data' - -export function formatReportDataOutput( - reportId: string, - data: ReportData, - commandName: string, - outputKind: 'json' | 'markdown' | 'print', - strict: boolean, - artifacts: any -): void { - if (outputKind === 'json') { - logger.log(JSON.stringify(data, undefined, 2)) - } else { - const format = new ColorOrMarkdown(outputKind === 'markdown') - logger.log(stripIndents` - Detailed info on socket.dev: ${format.hyperlink(reportId, data.url, { - fallbackToUrl: true - })}`) - if (outputKind === 'print') { - logger.log(data) - logger.log( - colors.dim( - `Or rerun ${colors.italic(commandName)} using the ${colors.italic('--json')} flag to get full JSON output` - ) - ) - logger.log('The scan:') - logger.log(artifacts) - } - } - - if (strict && !data.healthy) { - // eslint-disable-next-line n/no-process-exit - process.exit(1) - } -} diff --git a/src/commands/report/get-socket-config.ts b/src/commands/report/get-socket-config.ts deleted file mode 100644 index ba66c51f0..000000000 --- a/src/commands/report/get-socket-config.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { betterAjvErrors } from '@apideck/better-ajv-errors' - -import { SocketValidationError, readSocketConfig } from '@socketsecurity/config' - -import { InputError } from '../../utils/errors' - -export async function getSocketConfig(absoluteConfigPath: string) { - const socketConfig = await readSocketConfig(absoluteConfigPath).catch( - (cause: unknown) => { - if ( - cause && - typeof cause === 'object' && - cause instanceof SocketValidationError - ) { - // Inspired by workbox-build: - // https://github.com/GoogleChrome/workbox/blob/95f97a207fd51efb3f8a653f6e3e58224183a778/packages/workbox-build/src/lib/validate-options.ts#L68-L71 - const betterErrors = betterAjvErrors({ - basePath: 'config', - data: cause.data, - errors: cause.validationErrors, - schema: cause.schema as Parameters< - typeof betterAjvErrors - >[0]['schema'] - }) - throw new InputError( - 'The socket.yml config is not valid', - betterErrors - .map( - err => - `[${err.path}] ${err.message}.${err.suggestion ? err.suggestion : ''}` - ) - .join('\n') - ) - } else { - throw new Error('Failed to read socket.yml config', { cause }) - } - } - ) - - return socketConfig -} diff --git a/src/commands/report/view-report.ts b/src/commands/report/view-report.ts deleted file mode 100644 index 41b407729..000000000 --- a/src/commands/report/view-report.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { fetchReportData } from './fetch-report-data' -import { formatReportDataOutput } from './format-report-data' -import { fetchScan } from '../scan/fetch-scan' - -import type { components } from '@socketsecurity/sdk/types/api' - -export async function viewReport( - reportId: string, - { - all, - commandName, - outputKind, - strict - }: { - commandName: string - all: boolean - outputKind: 'json' | 'markdown' | 'print' - strict: boolean - } -) { - const result = await fetchReportData(reportId, all, strict) - - const artifacts: Array | undefined = - await fetchScan('socketdev', reportId) - - if (result) { - formatReportDataOutput( - reportId, - result, - commandName, - outputKind, - strict, - artifacts - ) - } -} diff --git a/src/commands/scan/cmd-scan-report.ts b/src/commands/scan/cmd-scan-report.ts index a61607857..584e32d7d 100644 --- a/src/commands/scan/cmd-scan-report.ts +++ b/src/commands/scan/cmd-scan-report.ts @@ -20,7 +20,7 @@ const config: CliCommandConfig = { commandName: 'report', description: 'Check whether a scan result passes the organizational policies (security, license)', - hidden: true, // [beta] + hidden: false, flags: { ...commonFlags, ...outputFlags, diff --git a/src/commands/scan/cmd-scan.test.ts b/src/commands/scan/cmd-scan.test.ts index b4450db62..5007dabd7 100644 --- a/src/commands/scan/cmd-scan.test.ts +++ b/src/commands/scan/cmd-scan.test.ts @@ -28,6 +28,7 @@ describe('socket scan', async () => { del Delete a scan list List the scans for an organization metadata Get a scan's metadata + report Check whether a scan result passes the organizational policies (security, license) view View the raw results of a scan Options