From 7ef063f3eea85823eaad84af3db42c6193135cea Mon Sep 17 00:00:00 2001 From: Peter van der Zee Date: Tue, 4 Mar 2025 11:30:35 +0100 Subject: [PATCH 1/2] Scrub and hide the diff-scan command --- package.json | 2 + .../dependencies/find-dependencies.ts | 9 ++ src/commands/diff-scan/cmd-diff-scan-get.ts | 66 ++++---- src/commands/diff-scan/cmd-diff-scan.ts | 5 + src/commands/diff-scan/get-diff-scan.ts | 142 +++++++++++++----- test/dry-run.test.ts | 2 + 6 files changed, 161 insertions(+), 65 deletions(-) diff --git a/package.json b/package.json index bf255769f..90689c59e 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,8 @@ "lint-staged": "lint-staged", "precommit": "lint-staged", "prepare": "husky", + "bs": "npm run build:dist; npm exec socket --", + "s": "npm exec socket --", "test": "run-s check test:*", "test:prepare": "cross-env VITEST=1 npm run build", "test:unit": "vitest --run", diff --git a/src/commands/dependencies/find-dependencies.ts b/src/commands/dependencies/find-dependencies.ts index 08248c690..a30ac3852 100644 --- a/src/commands/dependencies/find-dependencies.ts +++ b/src/commands/dependencies/find-dependencies.ts @@ -48,6 +48,15 @@ export async function findDependencies({ return } + logger.log( + 'Request details: Offset:', + offset, + ', limit:', + limit, + ', is there more data after this?', + result.data.end ? 'no' : 'yes' + ) + const options = { columns: [ { field: 'namespace', name: colors.cyan('Namespace') }, diff --git a/src/commands/diff-scan/cmd-diff-scan-get.ts b/src/commands/diff-scan/cmd-diff-scan-get.ts index 42bb40c2c..4f9131191 100644 --- a/src/commands/diff-scan/cmd-diff-scan-get.ts +++ b/src/commands/diff-scan/cmd-diff-scan-get.ts @@ -4,11 +4,9 @@ import { logger } from '@socketsecurity/registry/lib/logger' import { getDiffScan } from './get-diff-scan' import constants from '../../constants' -import { commonFlags, outputFlags } from '../../flags' -import { AuthError } from '../../utils/errors' +import { commonFlags } from '../../flags' 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' @@ -20,17 +18,30 @@ const config: CliCommandConfig = { hidden: false, flags: { ...commonFlags, + after: { + type: 'string', + shortFlag: 'a', + default: '', + description: 'The full scan ID of the head scan' + }, before: { type: 'string', shortFlag: 'b', default: '', description: 'The full scan ID of the base scan' }, - after: { - type: 'string', - shortFlag: 'a', - default: '', - description: 'The full scan ID of the head scan' + depth: { + type: 'number', + default: 2, + description: + 'Max depth of JSON to display before truncating, use zero for no limit (without --json/--file)' + }, + json: { + type: 'boolean', + shortFlag: 'j', + default: false, + description: + 'Output result as json. This can be big. Use --file to store it to disk without truncation.' }, preview: { type: 'boolean', @@ -42,14 +53,18 @@ const config: CliCommandConfig = { type: 'string', shortFlag: 'f', default: '', - description: 'Path to a local file where the output should be saved' - }, - ...outputFlags + description: + 'Path to a local file where the output should be saved. Use `-` to force stdout.' + } }, help: (command, config) => ` Usage $ ${command} --before= --after= + This command displays the package changes between two scans. The full output + can be pretty large depending on the size of your repo and time range. It is + best stored to disk to be further analyzed by other tools. + Options ${getFlagListOutput(config.flags, 6)} @@ -88,6 +103,7 @@ async function run( logger.error(`${colors.bgRed(colors.white('Input error'))}: Please provide the required fields:\n - Specify a before and after full scan ID ${!before && !after ? colors.red('(missing before and after!)') : !before ? colors.red('(missing before!)') : !after ? colors.red('(missing after!)') : colors.green('(ok)')}\n - To get full scans IDs, you can run the command "socket scan list ". + The args are expecting a full \`aaa0aa0a-aaaa-0000-0a0a-0000000a00a0\` ID.\n - Org name as the first argument ${!orgSlug ? colors.red('(missing!)') : colors.green('(ok)')}\n`) return } @@ -97,23 +113,13 @@ async function run( return } - 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 getDiffScan( - { - outputJson: Boolean(cli.flags['json']), - outputMarkdown: Boolean(cli.flags['markdown']), - before, - after, - preview: Boolean(cli.flags['preview']), - orgSlug, - file: String(cli.flags['file'] || '') - }, - apiToken - ) + await getDiffScan({ + outputJson: Boolean(cli.flags['json']), + before, + after, + depth: Number(cli.flags['depth']), + preview: Boolean(cli.flags['preview']), + orgSlug, + file: String(cli.flags['file'] || '') + }) } diff --git a/src/commands/diff-scan/cmd-diff-scan.ts b/src/commands/diff-scan/cmd-diff-scan.ts index 518d0f1df..680624aaf 100644 --- a/src/commands/diff-scan/cmd-diff-scan.ts +++ b/src/commands/diff-scan/cmd-diff-scan.ts @@ -7,6 +7,11 @@ const description = 'Diff scans related commands' export const cmdDiffScan: CliSubcommand = { description, + // Hidden because it was broken all this time (nobody could be using it) + // and we're not sure if it's useful to anyone in its current state. + // Until we do, we'll hide this to keep the help tidier. + // And later, we may simply move this under `scan`, anyways. + hidden: true, async run(argv, importMeta, { parentName }) { await meowWithSubcommands( { diff --git a/src/commands/diff-scan/get-diff-scan.ts b/src/commands/diff-scan/get-diff-scan.ts index 71148c521..147fb5e5e 100644 --- a/src/commands/diff-scan/get-diff-scan.ts +++ b/src/commands/diff-scan/get-diff-scan.ts @@ -4,38 +4,75 @@ import util from 'node:util' import colors from 'yoctocolors-cjs' import { logger } from '@socketsecurity/registry/lib/logger' +import { SocketSdkReturnType } from '@socketsecurity/sdk' import constants from '../../constants' -import { handleAPIError, queryAPI } from '../../utils/api' +import { handleAPIError, handleApiCall, queryAPI } from '../../utils/api' +import { AuthError } from '../../utils/errors' +import { getDefaultToken } from '../../utils/sdk' -export async function getDiffScan( - { +export async function getDiffScan({ + after, + before, + depth, + file, + orgSlug, + outputJson, + preview +}: { + after: string + before: string + depth: number + file: string + orgSlug: string + outputJson: boolean + preview: boolean +}): 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 getDiffScanWithToken({ after, before, + depth, file, orgSlug, - outputJson - }: { - outputJson: boolean - outputMarkdown: boolean - before: string - after: string - preview: boolean - orgSlug: string - file: string - }, + outputJson, + apiToken, + preview + }) +} +export async function getDiffScanWithToken({ + after, + apiToken, + before, + depth, + file, + orgSlug, + outputJson +}: { + after: string apiToken: string -): Promise { + depth: number + before: string + file: string + orgSlug: string + outputJson: boolean + preview: boolean +}): Promise { // Lazily access constants.spinner. const { spinner } = constants spinner.start('Getting diff scan...') const response = await queryAPI( - `${orgSlug}/full-scans/diff?before=${before}&after=${after}&preview`, + `orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(before)}&after=${encodeURIComponent(after)}&preview`, apiToken ) - const data = await response.json() if (!response.ok) { const err = await handleAPIError(response.status) @@ -45,34 +82,69 @@ export async function getDiffScan( return } + const result = await handleApiCall( + (await response.json()) as Promise< + SocketSdkReturnType<'GetOrgDiffScan'>['data'] + >, + 'Deserializing json' + ) + spinner.stop() - if (file && !outputJson) { - fs.writeFile(file, JSON.stringify(data), err => { - err - ? logger.error(err) - : logger.log(`Data successfully written to ${file}`) - }) - return - } + const dashboardUrl = (result as any)?.['diff_report_url'] + const dashboardMessage = dashboardUrl + ? `\n View this diff scan in the Socket dashboard: ${colors.cyan(dashboardUrl)}` + : '' + + // When forcing json, or dumping to file, serialize to string such that it + // won't get truncated. The only way to dump the full raw JSON to stdout is + // to use `--json --file -` (the dash is a standard notation for stdout) + if (outputJson || file) { + let json + try { + json = JSON.stringify(result, null, 2) + } catch (e) { + // Most likely caused by a circular reference (or OOM) + logger.error('There was a problem converting the data to JSON') + process.exitCode = 1 + return + } + + if (file && file !== '-') { + logger.log(`Writing json to \`${file}\``) + fs.writeFile(file, JSON.stringify(result, null, 2), err => { + if (err) { + logger.error(`Writing to \`${file}\` failed...`) + logger.error(err) + } else { + logger.log(`Data successfully written to \`${file}\``) + } + logger.error(dashboardMessage) + }) + } else { + // TODO: expose different method for writing to stderr when simply dodging stdout + logger.error(`\n Diff scan result: \n`) + logger.log(json) + logger.error(dashboardMessage) + } - if (outputJson) { - logger.log(`\n Diff scan result: \n`) - logger.log( - util.inspect(data, { showHidden: false, depth: null, colors: true }) - ) - logger.log( - `\n View this diff scan in the Socket dashboard: ${colors.cyan((data as any)?.['diff_report_url'])}` - ) return } + // In this case neither the --json nor the --file flag was passed + // Dump the JSON to CLI and let NodeJS deal with truncation + logger.log('Diff scan result:') - logger.log(data) logger.log( - `\n 📝 To display the detailed report in the terminal, use the --json flag \n` + util.inspect(result, { + showHidden: false, + depth: depth > 0 ? depth : null, + colors: true, + maxArrayLength: null + }) ) logger.log( - `\n View this diff scan in the Socket dashboard: ${colors.cyan((data as any)?.['diff_report_url'])}` + `\n 📝 To display the detailed report in the terminal, use the --json flag \n` ) + logger.log(dashboardMessage) } diff --git a/test/dry-run.test.ts b/test/dry-run.test.ts index 04e164d38..b8242d5c4 100644 --- a/test/dry-run.test.ts +++ b/test/dry-run.test.ts @@ -242,6 +242,8 @@ describe('dry-run on all commands', async () => { - Specify a before and after full scan ID \\x1b[31m(missing before and after!)\\x1b[39m - To get full scans IDs, you can run the command "socket scan list ". + The args are expecting a full \`aaa0aa0a-aaaa-0000-0a0a-0000000a00a0\` ID. + - Org name as the first argument \\x1b[31m(missing!)\\x1b[39m" `) From aedf4b530d3206b7275b5bef4f8172fd036577c1 Mon Sep 17 00:00:00 2001 From: Peter van der Zee Date: Tue, 4 Mar 2025 11:40:36 +0100 Subject: [PATCH 2/2] Drop the preview --- src/commands/diff-scan/cmd-diff-scan-get.ts | 7 ------- src/commands/diff-scan/get-diff-scan.ts | 10 +++------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/src/commands/diff-scan/cmd-diff-scan-get.ts b/src/commands/diff-scan/cmd-diff-scan-get.ts index 4f9131191..8c07e380c 100644 --- a/src/commands/diff-scan/cmd-diff-scan-get.ts +++ b/src/commands/diff-scan/cmd-diff-scan-get.ts @@ -43,12 +43,6 @@ const config: CliCommandConfig = { description: 'Output result as json. This can be big. Use --file to store it to disk without truncation.' }, - preview: { - type: 'boolean', - shortFlag: 'p', - default: true, - description: 'A boolean flag to persist or not the diff scan result' - }, file: { type: 'string', shortFlag: 'f', @@ -118,7 +112,6 @@ async function run( before, after, depth: Number(cli.flags['depth']), - preview: Boolean(cli.flags['preview']), orgSlug, file: String(cli.flags['file'] || '') }) diff --git a/src/commands/diff-scan/get-diff-scan.ts b/src/commands/diff-scan/get-diff-scan.ts index 147fb5e5e..b8443aa89 100644 --- a/src/commands/diff-scan/get-diff-scan.ts +++ b/src/commands/diff-scan/get-diff-scan.ts @@ -17,8 +17,7 @@ export async function getDiffScan({ depth, file, orgSlug, - outputJson, - preview + outputJson }: { after: string before: string @@ -26,7 +25,6 @@ export async function getDiffScan({ file: string orgSlug: string outputJson: boolean - preview: boolean }): Promise { const apiToken = getDefaultToken() if (!apiToken) { @@ -42,8 +40,7 @@ export async function getDiffScan({ file, orgSlug, outputJson, - apiToken, - preview + apiToken }) } export async function getDiffScanWithToken({ @@ -62,7 +59,6 @@ export async function getDiffScanWithToken({ file: string orgSlug: string outputJson: boolean - preview: boolean }): Promise { // Lazily access constants.spinner. const { spinner } = constants @@ -70,7 +66,7 @@ export async function getDiffScanWithToken({ spinner.start('Getting diff scan...') const response = await queryAPI( - `orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(before)}&after=${encodeURIComponent(after)}&preview`, + `orgs/${orgSlug}/full-scans/diff?before=${encodeURIComponent(before)}&after=${encodeURIComponent(after)}`, apiToken )