diff --git a/src/cli/commands/test/iac/scan.ts b/src/cli/commands/test/iac/scan.ts index e652eb560a..371b6a2e5d 100644 --- a/src/cli/commands/test/iac/scan.ts +++ b/src/cli/commands/test/iac/scan.ts @@ -1,5 +1,6 @@ import * as cloneDeep from 'lodash.clonedeep'; import * as assign from 'lodash.assign'; +import * as debugLib from 'debug'; import { IacFileInDirectory, @@ -30,6 +31,8 @@ import { getRepositoryRootForPath } from '../../../../lib/iac/git'; import { getInfo } from '../../../../lib/project-metadata/target-builders/git'; import { buildMeta, GitRepository, GitRepositoryFinder } from './meta'; +const debug = debugLib('snyk-iac'); + export async function scan( iacOrgSettings: IacOrgSettings, options: any, @@ -109,6 +112,7 @@ export async function scan( iacScanFailures = [...iacScanFailures, ...(failures || [])]; iacIgnoredIssuesCount += ignoreCount; } catch (error) { + debug(`Scan error for path ${path}, details below`); res = formatTestError(error); } @@ -155,12 +159,17 @@ export async function scan( function formatTestError(error) { let errorResponse; if (error instanceof Error) { + debug(`Error: ${error.name} ${error.message}`); + debug(`Stack trace: ${error.stack}`); errorResponse = error; } else if (Array.isArray(error)) { return error.map(formatTestError); } else if (typeof error !== 'object') { + debug(`Error value: ${error}`); errorResponse = new Error(error); } else { + // we should not get here, but if we do, we want to log the thrown object + debug('Unexpected error object:', safeStringify(error)); try { errorResponse = JSON.parse(error.message); } catch (unused) { @@ -170,6 +179,17 @@ function formatTestError(error) { return errorResponse; } +function safeStringify(obj: unknown): string { + try { + return JSON.stringify(obj); + } catch (e) { + if (e instanceof Error) { + return `Error stringifying object: ${e.message}`; + } + return `Error stringifying object`; + } +} + class CurrentWorkingDirectoryTraversalError extends CustomError { public filename: string; public projectRoot: string; diff --git a/src/lib/snyk-test/iac-test-result.ts b/src/lib/snyk-test/iac-test-result.ts index 244d5ba366..33b0c50166 100644 --- a/src/lib/snyk-test/iac-test-result.ts +++ b/src/lib/snyk-test/iac-test-result.ts @@ -1,6 +1,9 @@ import pick = require('lodash.pick'); import { CustomError } from '../errors'; import { BasicResultData, SEVERITY, TestDepGraphMeta } from './legacy'; +import * as debugLib from 'debug'; + +const debug = debugLib('snyk-iac'); export interface AnnotatedIacIssue { id: string; @@ -58,10 +61,24 @@ const IAC_ISSUES_KEY = 'infrastructureAsCodeIssues'; export function mapIacTestResult( iacTest: IacTestResponse, ): MappedIacTestResponse | IacTestError { - if (iacTest instanceof CustomError) { + if (iacTest instanceof Error) { return mapIacTestError(iacTest); } + if (!iacTest.result) { + // This is an unexpected scenario, we should always have a result object, + // but if we don't, we should handle it gracefully. + debug(`invalid scan result: ${iacTest}`); + const errorMessage = iacTest.path + ? `Invalid result for ${iacTest.path}` + : 'Invalid result'; + return mapIacTestError( + new CustomError( + `${errorMessage}. Please run the command again with the \`-d\` flag and contact support@snyk.io with the contents of the output`, + ), + ); + } + const infrastructureAsCodeIssues = iacTest?.result?.cloudConfigResults.map(mapIacIssue) || []; const { @@ -76,10 +93,10 @@ export function mapIacTestResult( }; } -export function mapIacTestError(error: CustomError) { +export function mapIacTestError(error: Error) { return { ok: false, - code: error.code, + code: error instanceof CustomError ? error.code : undefined, error: error.message, path: (error as any).path, };