diff --git a/CHANGELOG.md b/CHANGELOG.md index ba30a5594..7f01df407 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). +## [1.1.52](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.52) - 2026-01-02 + +### Added +- Added `--silence` flag to `socket fix` to suppress intermediate output and show only the final result. + +### Changed +- Updated the Coana CLI to v `14.12.139`. + ## [1.1.51](https://github.com/SocketDev/socket-cli/releases/tag/v1.1.51) - 2025-12-23 ### Added diff --git a/package.json b/package.json index 8136dce4e..7180937f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "socket", - "version": "1.1.51", + "version": "1.1.52", "description": "CLI for Socket.dev", "homepage": "https://github.com/SocketDev/socket-cli", "license": "MIT AND OFL-1.1", @@ -94,7 +94,7 @@ "@babel/preset-typescript": "7.27.1", "@babel/runtime": "7.28.4", "@biomejs/biome": "2.2.4", - "@coana-tech/cli": "14.12.138", + "@coana-tech/cli": "14.12.139", "@cyclonedx/cdxgen": "11.11.0", "@dotenvx/dotenvx": "1.49.0", "@eslint/compat": "1.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 841a3b700..22b918b25 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,8 +124,8 @@ importers: specifier: 2.2.4 version: 2.2.4 '@coana-tech/cli': - specifier: 14.12.138 - version: 14.12.138 + specifier: 14.12.139 + version: 14.12.139 '@cyclonedx/cdxgen': specifier: 11.11.0 version: 11.11.0 @@ -680,8 +680,8 @@ packages: '@bufbuild/protobuf@2.6.3': resolution: {integrity: sha512-w/gJKME9mYN7ZoUAmSMAWXk4hkVpxRKvEJCb3dV5g9wwWdxTJJ0ayOJAVcNxtdqaxDyFuC0uz4RSGVacJ030PQ==} - '@coana-tech/cli@14.12.138': - resolution: {integrity: sha512-dPkCRLnblrKLWC0rBHCILP2uvToJqA9EY6WvpvFfGYBjtf/xWu3Q92ByqFzvX8BryFt7FTfdh8t+n0Kc2kIxOw==} + '@coana-tech/cli@14.12.139': + resolution: {integrity: sha512-oI9nwrPYN1j40Li3LACTxe+PH6WxrJbcHOOY2mYGgyeW4FGgW3jjR9PRzUFetI2sRasAe7G9x5902cxOgAsAVg==} hasBin: true '@colors/colors@1.5.0': @@ -5323,7 +5323,7 @@ snapshots: '@bufbuild/protobuf@2.6.3': optional: true - '@coana-tech/cli@14.12.138': {} + '@coana-tech/cli@14.12.139': {} '@colors/colors@1.5.0': optional: true diff --git a/src/commands/ci/fetch-default-org-slug.mts b/src/commands/ci/fetch-default-org-slug.mts index 540839724..37f2e8505 100644 --- a/src/commands/ci/fetch-default-org-slug.mts +++ b/src/commands/ci/fetch-default-org-slug.mts @@ -7,7 +7,9 @@ import { fetchOrganization } from '../organization/fetch-organization-list.mts' import type { CResult } from '../../types.mts' // Use the config defaultOrg when set, otherwise discover from remote. -export async function getDefaultOrgSlug(): Promise> { +export async function getDefaultOrgSlug( + silence?: boolean, +): Promise> { const defaultOrgResult = getConfigValueOrUndef('defaultOrg') if (defaultOrgResult) { debugFn( @@ -28,7 +30,7 @@ export async function getDefaultOrgSlug(): Promise> { return { ok: true, data: envOrgSlug } } - const orgsCResult = await fetchOrganization() + const orgsCResult = await fetchOrganization({ silence }) if (!orgsCResult.ok) { return orgsCResult } diff --git a/src/commands/fix/cmd-fix.e2e.test.mts b/src/commands/fix/cmd-fix.e2e.test.mts index 18cfbdcba..5298dd297 100644 --- a/src/commands/fix/cmd-fix.e2e.test.mts +++ b/src/commands/fix/cmd-fix.e2e.test.mts @@ -402,6 +402,72 @@ describe('socket fix (E2E tests)', async () => { }, { timeout: testTimeout }, ) + + cmdit( + ['fix', '--silence', '--json', '.'], + 'should output only parseable JSON when --silence and --json flags are used', + async cmd => { + const tempFixture = await createTempFixtureCopy('e2e-test-js') + let stdout = '' + let stderr = '' + let code = -1 + + try { + const result = await spawnSocketCli(binCliPath, cmd, { + cwd: tempFixture.path, + env: getTestEnv(apiToken), + }) + stdout = result.stdout + stderr = result.stderr + code = result.code + + if (code !== 0) { + logCommandOutput(code, stdout, stderr) + } + + expect(code, 'should exit with code 0').toBe(0) + + // Verify stdout is valid JSON and nothing else. + const trimmedStdout = stdout.trim() + expect( + trimmedStdout.length, + 'stdout should not be empty', + ).toBeGreaterThan(0) + + let parsedJson: unknown + try { + parsedJson = JSON.parse(trimmedStdout) + } catch { + // Log the actual output to help debug what extra content was included. + logger.error('stdout is not valid JSON:', trimmedStdout) + throw new Error( + `Expected stdout to be valid JSON, but got: ${trimmedStdout.slice(0, 200)}...`, + ) + } + + expect(parsedJson).toBeDefined() + expect(typeof parsedJson).toBe('object') + + // Verify stderr is empty (no extra logging output). + expect( + stderr.trim(), + 'stderr should be empty when --silence is used', + ).toBe('') + + logger.info( + '\nSuccessfully verified --silence --json outputs only JSON', + ) + } catch (e) { + if (code !== 0) { + logCommandOutput(code, stdout, stderr) + } + throw e + } finally { + await tempFixture.cleanup() + } + }, + { timeout: testTimeout }, + ) }) describe('Python projects', () => { diff --git a/src/commands/fix/cmd-fix.integration.test.mts b/src/commands/fix/cmd-fix.integration.test.mts index d4cb7fc0f..270a20d9a 100644 --- a/src/commands/fix/cmd-fix.integration.test.mts +++ b/src/commands/fix/cmd-fix.integration.test.mts @@ -172,7 +172,7 @@ describe('socket fix', async () => { --fix-version Override the version of @coana-tech/cli used for fix analysis. Default: . --id Provide a list of vulnerability identifiers to compute fixes for: - GHSA IDs (https://docs.github.com/en/code-security/security-advisories/working-with-global-security-advisories-from-the-github-advisory-database/about-the-github-advisory-database#about-ghsa-ids) (e.g., GHSA-xxxx-xxxx-xxxx) - - CVE IDs (https://cve.mitre.org/cve/identifiers/) (e.g., CVE-2025-1234) - automatically converted to GHSA + - CVE IDs (https://cve.mitre.org/cve/identifiers/) (e.g., CVE-2026-1234) - automatically converted to GHSA - PURLs (https://github.com/package-url/purl-spec) (e.g., pkg:npm/package@1.0.0) - automatically converted to GHSA Can be provided as comma separated values or as multiple flags. Cannot be used with --all. --include Include workspaces matching these glob patterns. Can be provided as comma separated values or as multiple flags @@ -188,6 +188,7 @@ describe('socket fix', async () => { * pin - Use the exact version (e.g. 1.2.3) * preserve - Retain the existing version range style as-is --show-affected-direct-dependencies List the direct dependencies responsible for introducing transitive vulnerabilities and list the updates required to resolve the vulnerabilities + --silence Silence all output except the final result Environment Variables (for CI/PR mode) CI Set to enable CI mode diff --git a/src/commands/fix/cmd-fix.mts b/src/commands/fix/cmd-fix.mts index 665c8b512..5003be255 100644 --- a/src/commands/fix/cmd-fix.mts +++ b/src/commands/fix/cmd-fix.mts @@ -167,6 +167,11 @@ Available styles: description: 'List the direct dependencies responsible for introducing transitive vulnerabilities and list the updates required to resolve the vulnerabilities', }, + silence: { + type: 'boolean', + default: false, + description: 'Silence all output except the final result', + }, } const hiddenFlags: MeowFlags = { @@ -303,6 +308,7 @@ async function run( prLimit, rangeStyle, showAffectedDirectDependencies, + silence, // We patched in this feature with `npx custompatch meow` at // socket-cli/patches/meow#13.2.0.patch. unknownFlags = [], @@ -326,6 +332,7 @@ async function run( prLimit: number rangeStyle: RangeStyle showAffectedDirectDependencies: boolean + silence: boolean unknownFlags?: string[] } @@ -391,7 +398,7 @@ async function run( return } - const orgSlugCResult = await getDefaultOrgSlug() + const orgSlugCResult = await getDefaultOrgSlug(silence) if (!orgSlugCResult.ok) { process.exitCode = orgSlugCResult.code ?? 1 logger.fail( @@ -433,6 +440,7 @@ async function run( prLimit, rangeStyle, showAffectedDirectDependencies, + silence, spinner, unknownFlags, }) diff --git a/src/commands/fix/coana-fix.mts b/src/commands/fix/coana-fix.mts index 9de7f039e..23f4c0cac 100644 --- a/src/commands/fix/coana-fix.mts +++ b/src/commands/fix/coana-fix.mts @@ -57,6 +57,7 @@ type DiscoverGhsaIdsOptions = { coanaVersion?: string | undefined cwd?: string | undefined ecosystems?: PURL_Type[] | undefined + silence?: boolean | undefined spinner?: Spinner | undefined } @@ -72,6 +73,7 @@ async function discoverGhsaIds( const { cwd = process.cwd(), ecosystems, + silence = false, spinner, } = { __proto__: null, @@ -87,7 +89,11 @@ async function discoverGhsaIds( ...(ecosystems?.length ? ['--purl-types', ...ecosystems] : []), ], orgSlug, - { cwd, spinner, coanaVersion: options?.coanaVersion }, + { + cwd, + spinner: silence ? undefined : spinner, + coanaVersion: options?.coanaVersion, + }, { stdio: 'pipe' }, ) @@ -123,13 +129,16 @@ export async function coanaFix( outputFile, prLimit, showAffectedDirectDependencies, + silence, spinner, } = fixConfig const fixEnv = await getFixEnv() debugDir('inspect', { fixEnv }) - spinner?.start() + if (!silence) { + spinner?.start() + } const sockSdkCResult = await setupSdk() if (!sockSdkCResult.ok) { @@ -138,7 +147,10 @@ export async function coanaFix( const sockSdk = sockSdkCResult.data - const supportedFilesCResult = await fetchSupportedScanFileNames({ spinner }) + const supportedFilesCResult = await fetchSupportedScanFileNames({ + spinner: silence ? undefined : spinner, + silence, + }) if (!supportedFilesCResult.ok) { return supportedFilesCResult } @@ -157,6 +169,7 @@ export async function coanaFix( { description: 'upload manifests', spinner, + silence, }, ) @@ -166,7 +179,9 @@ export async function coanaFix( const tarHash: string = (uploadCResult as any).data.tarHash if (!tarHash) { - spinner?.stop() + if (!silence) { + spinner?.stop() + } return { ok: false, message: @@ -181,14 +196,14 @@ export async function coanaFix( if (!shouldOpenPrs) { // In local mode, if neither --all nor --id is provided, show deprecation warning. - if (shouldDiscoverGhsaIds && !all) { + if (!silence && shouldDiscoverGhsaIds && !all) { logger.warn( 'Implicit --all is deprecated in local mode and will be removed in a future release. Please use --all explicitly.', ) } // Inform user about local mode when fixes will be applied. - if (applyFixes && ghsas.length) { + if (!silence && applyFixes && ghsas.length) { const envCheck = checkCiEnvVars() if (envCheck.present.length) { // Some CI vars are set but not all - show what's missing. @@ -213,12 +228,15 @@ export async function coanaFix( coanaVersion, cwd, ecosystems, + silence, spinner, }) : ghsas if (ids.length === 0) { - spinner?.stop() + if (!silence) { + spinner?.stop() + } return { ok: true, data: { fixed: false } } } @@ -255,10 +273,17 @@ export async function coanaFix( ...fixConfig.unknownFlags, ], fixConfig.orgSlug, - { coanaVersion, cwd, spinner, stdio: 'inherit' }, + { + coanaVersion, + cwd, + spinner: silence ? undefined : spinner, + stdio: silence ? 'pipe' : 'inherit', + }, ) - spinner?.stop() + if (!silence) { + spinner?.stop() + } if (!fixCResult.ok) { return fixCResult @@ -269,7 +294,9 @@ export async function coanaFix( // Copy to outputFile if provided. if (outputFile) { - logger.info(`Copying fixes result to ${outputFile}`) + if (!silence) { + logger.info(`Copying fixes result to ${outputFile}`) + } const tmpContent = await fs.readFile(tmpFile, 'utf8') await fs.writeFile(outputFile, tmpContent, 'utf8') } @@ -320,6 +347,7 @@ export async function coanaFix( coanaVersion, cwd, ecosystems, + silence, spinner, }) : ghsas @@ -335,7 +363,9 @@ export async function coanaFix( } if (!ids?.length || !fixEnv.repoInfo) { - spinner?.stop() + if (!silence) { + spinner?.stop() + } return { ok: true, data: { fixed: false } } } @@ -381,11 +411,20 @@ export async function coanaFix( ...fixConfig.unknownFlags, ], fixConfig.orgSlug, - { coanaVersion, cwd, spinner, stdio: 'inherit' }, + { + coanaVersion, + cwd, + spinner: silence ? undefined : spinner, + stdio: silence ? 'pipe' : 'inherit', + }, ) if (!fixCResult.ok) { - logger.error(`Update failed for ${ghsaId}: ${getErrorCause(fixCResult)}`) + if (!silence) { + logger.error( + `Update failed for ${ghsaId}: ${getErrorCause(fixCResult)}`, + ) + } continue ghsaLoop } @@ -418,7 +457,9 @@ export async function coanaFix( if (existingOpenPrs.length > 0) { const prNum = existingOpenPrs[0]!.number - logger.info(`PR #${prNum} already exists for ${ghsaId}, skipping.`) + if (!silence) { + logger.info(`PR #${prNum} already exists for ${ghsaId}, skipping.`) + } debugFn('notice', `skip: open PR #${prNum} exists for ${ghsaId}`) continue ghsaLoop } @@ -436,10 +477,12 @@ export async function coanaFix( // Check for GitHub token before doing any git operations. if (!fixEnv.githubToken) { - logger.error( - 'Cannot create pull request: SOCKET_CLI_GITHUB_TOKEN environment variable is not set.\n' + - 'Set SOCKET_CLI_GITHUB_TOKEN or GITHUB_TOKEN to enable PR creation.', - ) + if (!silence) { + logger.error( + 'Cannot create pull request: SOCKET_CLI_GITHUB_TOKEN environment variable is not set.\n' + + 'Set SOCKET_CLI_GITHUB_TOKEN or GITHUB_TOKEN to enable PR creation.', + ) + } debugFn('error', `skip: missing GitHub token for ${ghsaId}`) continue ghsaLoop } @@ -471,7 +514,9 @@ export async function coanaFix( (await gitPushBranch(branch, cwd)) if (!pushed) { - logger.warn(`Push failed for ${ghsaId}, skipping PR creation.`) + if (!silence) { + logger.warn(`Push failed for ${ghsaId}, skipping PR creation.`) + } // eslint-disable-next-line no-await-in-loop await gitResetAndClean(fixEnv.baseBranch, cwd) // eslint-disable-next-line no-await-in-loop @@ -508,23 +553,29 @@ export async function coanaFix( const { data } = prResult.pr const prRef = `PR #${data.number}` - logger.success(`Opened ${prRef} for ${ghsaId}.`) + if (!silence) { + logger.success(`Opened ${prRef} for ${ghsaId}.`) + } if (autopilot) { - logger.indent() - spinner?.indent() + if (!silence) { + logger.indent() + spinner?.indent() + } // eslint-disable-next-line no-await-in-loop const { details, enabled } = await enablePrAutoMerge(data) - if (enabled) { - logger.info(`Auto-merge enabled for ${prRef}.`) - } else { - const message = `Failed to enable auto-merge for ${prRef}${ - details ? `:\n${details.map(d => ` - ${d}`).join('\n')}` : '.' - }` - logger.error(message) + if (!silence) { + if (enabled) { + logger.info(`Auto-merge enabled for ${prRef}.`) + } else { + const message = `Failed to enable auto-merge for ${prRef}${ + details ? `:\n${details.map(d => ` - ${d}`).join('\n')}` : '.' + }` + logger.error(message) + } + logger.dedent() + spinner?.dedent() } - logger.dedent() - spinner?.dedent() } // Clean up local branch only - keep remote branch for PR merge. @@ -533,32 +584,42 @@ export async function coanaFix( } else { // Handle PR creation failures. if (prResult.reason === 'already_exists') { - logger.info( - `PR already exists for ${ghsaId} (this should not happen due to earlier check).`, - ) + if (!silence) { + logger.info( + `PR already exists for ${ghsaId} (this should not happen due to earlier check).`, + ) + } // Don't delete branch - PR exists and needs it. } else if (prResult.reason === 'validation_error') { - logger.error( - `Failed to create PR for ${ghsaId}:\n${prResult.details}`, - ) + if (!silence) { + logger.error( + `Failed to create PR for ${ghsaId}:\n${prResult.details}`, + ) + } // eslint-disable-next-line no-await-in-loop await cleanupFailedPrBranches(branch, cwd) } else if (prResult.reason === 'permission_denied') { - logger.error( - `Failed to create PR for ${ghsaId}: Permission denied. Check SOCKET_CLI_GITHUB_TOKEN permissions.`, - ) + if (!silence) { + logger.error( + `Failed to create PR for ${ghsaId}: Permission denied. Check SOCKET_CLI_GITHUB_TOKEN permissions.`, + ) + } // eslint-disable-next-line no-await-in-loop await cleanupFailedPrBranches(branch, cwd) } else if (prResult.reason === 'network_error') { - logger.error( - `Failed to create PR for ${ghsaId}: Network error. Please try again.`, - ) + if (!silence) { + logger.error( + `Failed to create PR for ${ghsaId}: Network error. Please try again.`, + ) + } // eslint-disable-next-line no-await-in-loop await cleanupFailedPrBranches(branch, cwd) } else { - logger.error( - `Failed to create PR for ${ghsaId}: ${prResult.error.message}`, - ) + if (!silence) { + logger.error( + `Failed to create PR for ${ghsaId}: ${prResult.error.message}`, + ) + } // eslint-disable-next-line no-await-in-loop await cleanupFailedPrBranches(branch, cwd) } @@ -570,9 +631,11 @@ export async function coanaFix( // eslint-disable-next-line no-await-in-loop await gitCheckoutBranch(fixEnv.baseBranch, cwd) } catch (e) { - logger.warn( - `Unexpected condition: Push failed for ${ghsaId}, skipping PR creation.`, - ) + if (!silence) { + logger.warn( + `Unexpected condition: Push failed for ${ghsaId}, skipping PR creation.`, + ) + } debugDir('error', e) // Clean up branches (push may have succeeded before error). // eslint-disable-next-line no-await-in-loop @@ -595,7 +658,9 @@ export async function coanaFix( } } - spinner?.stop() + if (!silence) { + spinner?.stop() + } return { ok: true, diff --git a/src/commands/fix/handle-fix.mts b/src/commands/fix/handle-fix.mts index b760e8320..8d3244782 100644 --- a/src/commands/fix/handle-fix.mts +++ b/src/commands/fix/handle-fix.mts @@ -25,11 +25,22 @@ export type HandleFixConfig = Remap< } > +type ConvertIdsOptions = { + silence?: boolean | undefined +} + /** * Converts mixed CVE/GHSA/PURL IDs to GHSA IDs only. * Filters out invalid IDs and logs conversion results. */ -export async function convertIdsToGhsas(ids: string[]): Promise { +export async function convertIdsToGhsas( + ids: string[], + options?: ConvertIdsOptions | undefined, +): Promise { + const { silence = false } = { + __proto__: null, + ...options, + } as ConvertIdsOptions debugFn('notice', `Converting ${ids.length} IDs to GHSA format`) debugDir('inspect', { ids }) @@ -57,19 +68,23 @@ export async function convertIdsToGhsas(ids: string[]): Promise { const conversionResult = await convertCveToGhsa(trimmedId) if (conversionResult.ok) { validGhsas.push(conversionResult.data) - logger.info(`Converted ${trimmedId} to ${conversionResult.data}`) + if (!silence) { + logger.info(`Converted ${trimmedId} to ${conversionResult.data}`) + } } else { errors.push(`${trimmedId}: ${conversionResult.message}`) } } else if (trimmedId.startsWith('pkg:')) { - // Convert PURL to GHSAs + // Convert PURL to GHSAs. // eslint-disable-next-line no-await-in-loop const conversionResult = await convertPurlToGhsas(trimmedId) if (conversionResult.ok && conversionResult.data.length) { validGhsas.push(...conversionResult.data) - logger.info( - `Converted ${trimmedId} to ${conversionResult.data.length} GHSA(s): ${joinAnd(conversionResult.data)}`, - ) + if (!silence) { + logger.info( + `Converted ${trimmedId} to ${conversionResult.data.length} GHSA(s): ${joinAnd(conversionResult.data)}`, + ) + } } else { errors.push( `${trimmedId}: ${conversionResult.message || 'No GHSAs found'}`, @@ -84,9 +99,11 @@ export async function convertIdsToGhsas(ids: string[]): Promise { } if (errors.length) { - logger.warn( - `Skipped ${errors.length} invalid IDs:\n${errors.map(e => ` - ${e}`).join('\n')}`, - ) + if (!silence) { + logger.warn( + `Skipped ${errors.length} invalid IDs:\n${errors.map(e => ` - ${e}`).join('\n')}`, + ) + } debugDir('inspect', { errors }) } @@ -117,6 +134,7 @@ export async function handleFix({ prLimit, rangeStyle, showAffectedDirectDependencies, + silence, spinner, unknownFlags, }: HandleFixConfig) { @@ -141,6 +159,7 @@ export async function handleFix({ prLimit, rangeStyle, showAffectedDirectDependencies, + silence, unknownFlags, }) @@ -156,7 +175,7 @@ export async function handleFix({ ecosystems, exclude, // Convert mixed CVE/GHSA/PURL inputs to GHSA IDs only. - ghsas: await convertIdsToGhsas(ghsas), + ghsas: await convertIdsToGhsas(ghsas, { silence }), include, minimumReleaseAge, minSatisfying, @@ -166,6 +185,7 @@ export async function handleFix({ prLimit, rangeStyle, showAffectedDirectDependencies, + silence, spinner, unknownFlags, }), diff --git a/src/commands/fix/types.mts b/src/commands/fix/types.mts index a199e99a4..6420ea88e 100644 --- a/src/commands/fix/types.mts +++ b/src/commands/fix/types.mts @@ -22,6 +22,7 @@ export type FixConfig = { prLimit: number rangeStyle: RangeStyle showAffectedDirectDependencies: boolean + silence: boolean spinner: Spinner | undefined unknownFlags: string[] } diff --git a/src/commands/organization/fetch-organization-list.mts b/src/commands/organization/fetch-organization-list.mts index b72016b61..bf6bb544e 100644 --- a/src/commands/organization/fetch-organization-list.mts +++ b/src/commands/organization/fetch-organization-list.mts @@ -9,6 +9,7 @@ export type FetchOrganizationOptions = { description?: string | undefined sdk?: SocketSdk | undefined sdkOpts?: SetupSdkOptions | undefined + silence?: boolean | undefined } export type EnterpriseOrganization = Omit & { @@ -33,6 +34,7 @@ export async function fetchOrganization( description = 'organization list', sdk, sdkOpts, + silence = false, } = { __proto__: null, ...options, @@ -49,6 +51,7 @@ export async function fetchOrganization( const orgsCResult = await handleApiCall(sockSdk.getOrganizations(), { description, + silence, }) if (!orgsCResult.ok) { return orgsCResult diff --git a/src/commands/scan/cmd-scan-reach.e2e.test.mts b/src/commands/scan/cmd-scan-reach.e2e.test.mts index cbeac2bb7..e966e6b6a 100644 --- a/src/commands/scan/cmd-scan-reach.e2e.test.mts +++ b/src/commands/scan/cmd-scan-reach.e2e.test.mts @@ -246,7 +246,14 @@ describe('socket scan reach (E2E tests)', async () => { describe('npm-test-workspace-mono', () => { cmdit( - ['scan', 'reach', '.', '--no-interactive', '--reach-disable-analytics'], + [ + 'scan', + 'reach', + '.', + '--reach-debug', + '--no-interactive', + '--reach-disable-analytics', + ], 'should run reachability analysis on workspace mono project', async cmd => { const tempFixture = await createTempFixtureCopy( @@ -395,6 +402,7 @@ describe('socket scan reach (E2E tests)', async () => { 'scan', 'reach', '.', + '--reach-debug', '--no-interactive', '--reach-disable-analytics', '--reach-exclude-paths', @@ -493,6 +501,7 @@ describe('socket scan reach (E2E tests)', async () => { 'scan', 'reach', 'packages/package-a', + '--reach-debug', '--no-interactive', '--reach-disable-analytics', ], @@ -598,7 +607,14 @@ describe('socket scan reach (E2E tests)', async () => { ) cmdit( - ['scan', 'reach', '.', '--no-interactive', '--reach-disable-analytics'], + [ + 'scan', + 'reach', + '.', + '--reach-debug', + '--no-interactive', + '--reach-disable-analytics', + ], 'should use --cwd to set the working directory', async cmd => { const tempFixture = await createTempFixtureCopy( @@ -673,6 +689,7 @@ describe('socket scan reach (E2E tests)', async () => { 'scan', 'reach', 'packages/package-b', + '--reach-debug', '--no-interactive', '--reach-disable-analytics', ], @@ -770,6 +787,7 @@ describe('socket scan reach (E2E tests)', async () => { 'scan', 'reach', '../outside-dir', + '--reach-debug', '--no-interactive', '--reach-disable-analytics', ], @@ -822,7 +840,14 @@ describe('socket scan reach (E2E tests)', async () => { ) cmdit( - ['scan', 'reach', '.', '--no-interactive', '--reach-disable-analytics'], + [ + 'scan', + 'reach', + '.', + '--reach-debug', + '--no-interactive', + '--reach-disable-analytics', + ], 'should write output to cwd when running from subdirectory', async cmd => { const tempFixture = await createTempFixtureCopy( @@ -908,6 +933,7 @@ describe('socket scan reach (E2E tests)', async () => { 'scan', 'reach', '.', + '--reach-debug', '--no-interactive', '--reach-ecosystems', 'pypi', @@ -1009,6 +1035,7 @@ describe('socket scan reach (E2E tests)', async () => { 'scan', 'reach', '.', + '--reach-debug', '--no-interactive', '--reach-ecosystems', 'npm', diff --git a/src/commands/scan/fetch-supported-scan-file-names.mts b/src/commands/scan/fetch-supported-scan-file-names.mts index 251fd06e6..76db7773b 100644 --- a/src/commands/scan/fetch-supported-scan-file-names.mts +++ b/src/commands/scan/fetch-supported-scan-file-names.mts @@ -9,12 +9,17 @@ import type { SocketSdkSuccessResult } from '@socketsecurity/sdk' export type FetchSupportedScanFileNamesOptions = { sdkOpts?: SetupSdkOptions | undefined spinner?: Spinner | undefined + silence?: boolean | undefined } export async function fetchSupportedScanFileNames( options?: FetchSupportedScanFileNamesOptions | undefined, ): Promise['data']>> { - const { sdkOpts, spinner } = { + const { + sdkOpts, + spinner, + silence = false, + } = { __proto__: null, ...options, } as FetchSupportedScanFileNamesOptions @@ -28,5 +33,6 @@ export async function fetchSupportedScanFileNames( return await handleApiCall(sockSdk.getSupportedScanFiles(), { description: 'supported scan file types', spinner, + silence, }) } diff --git a/src/utils/api.mts b/src/utils/api.mts index 68a8d3197..3382a0dac 100644 --- a/src/utils/api.mts +++ b/src/utils/api.mts @@ -120,6 +120,7 @@ export async function getErrorMessageForHttpStatusCode(code: number) { export type HandleApiCallOptions = { description?: string | undefined spinner?: Spinner | undefined + silence?: boolean | undefined commandPath?: string | undefined } @@ -134,27 +135,39 @@ export async function handleApiCall( value: Promise>, options?: HandleApiCallOptions | undefined, ): Promise> { - const { commandPath, description, spinner } = { + const { + commandPath, + description, + spinner, + silence = false, + } = { __proto__: null, ...options, } as HandleApiCallOptions - if (description) { - spinner?.start(`Requesting ${description} from API...`) - } else { - spinner?.start() + if (!silence) { + if (description) { + spinner?.start(`Requesting ${description} from API...`) + } else { + spinner?.start() + } } let sdkResult: SocketSdkResult try { sdkResult = await value - spinner?.stop() - if (description) { + if (!silence) { + spinner?.stop() + } + // Only log the message if spinner is provided (silence mode passes undefined). + if (description && !silence) { const message = `Received Socket API response (after requesting ${description}).` - if (sdkResult.success) { - logger.success(message) - } else { - logger.info(message) + if (!silence) { + if (sdkResult.success) { + logger.success(message) + } else { + logger.info(message) + } } } } catch (e) { @@ -164,7 +177,8 @@ export async function handleApiCall( message: 'Socket API error', cause: messageWithCauses(e as Error), } - if (description) { + // Only log the message if spinner is provided (silence mode passes undefined). + if (description && !silence) { logger.fail(`An error was thrown while requesting ${description}`) } debugDir('inspect', { socketSdkErrorResult }) diff --git a/src/utils/meow-with-subcommands.mts b/src/utils/meow-with-subcommands.mts index 582ac37b5..adf625be3 100644 --- a/src/utils/meow-with-subcommands.mts +++ b/src/utils/meow-with-subcommands.mts @@ -260,7 +260,10 @@ function levenshteinDistance(a: string, b: string): number { */ function shouldSuppressBanner(flags: Record): boolean { return Boolean( - flags['json'] || flags['markdown'] || flags['banner'] === false, + flags['json'] || + flags['markdown'] || + flags['banner'] === false || + flags['silence'], ) } diff --git a/src/utils/package-environment.mts b/src/utils/package-environment.mts index a0d9643ea..deb078053 100644 --- a/src/utils/package-environment.mts +++ b/src/utils/package-environment.mts @@ -244,7 +244,7 @@ function preferWindowsCmdShim(binPath: string, binName: string): string { if (!constants.WIN32) { return binPath } - + // Relative paths might be shell commands or aliases, not file paths with potential shims if (!path.isAbsolute(binPath)) { return binPath @@ -327,7 +327,10 @@ async function getAgentVersion( shouldRunWithNode = resolved } } catch (e) { - debugFn('warn', `Failed to resolve bin path for ${agentExecPath}, falling back to direct spawn.`) + debugFn( + 'warn', + `Failed to resolve bin path for ${agentExecPath}, falling back to direct spawn.`, + ) debugDir('error', e) } }