diff --git a/package-lock.json b/package-lock.json index 33cfbf7331..fc0e316b86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@sentry/node": "^7.34.0", "@snyk/cli-interface": "2.12.0", "@snyk/cloud-config-parser": "^1.14.5", - "@snyk/code-client": "^4.22.3", + "@snyk/code-client": "^4.23.0", "@snyk/dep-graph": "^2.7.4", "@snyk/docker-registry-v2-client": "^2.10.0", "@snyk/fix": "file:packages/snyk-fix", @@ -2667,16 +2667,16 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/@snyk/code-client": { - "version": "4.22.3", - "resolved": "https://registry.npmjs.org/@snyk/code-client/-/code-client-4.22.3.tgz", - "integrity": "sha512-rOa51ol5sMfm5eDtN/p0LhpnRWXrHqIF6vmRZsxN88p5B3d1YpZJOiZ/SYSv7yYB4Iubt1Cky7LD53Tj4ohCvg==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@snyk/code-client/-/code-client-4.23.0.tgz", + "integrity": "sha512-m7dbryJGdVqsTtU/T+59W6q9MN0gF/9akH0a2/Vu7Eq20dlrVWa94ZnI/3IPyuQ/X73TRgYx2yTRb68kANdcXw==", "dependencies": { "@deepcode/dcignore": "^1.0.4", "@types/flat-cache": "^2.0.0", "@types/lodash.omit": "^4.5.7", "@types/lodash.pick": "^4.4.7", "@types/lodash.union": "^4.6.7", - "@types/sarif": "^2.1.4", + "@types/sarif": "^2.1.5", "@types/uuid": "^8.3.4", "fast-glob": "3.3.1", "ignore": "^5.2.4", @@ -4812,9 +4812,9 @@ } }, "node_modules/@types/sarif": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.4.tgz", - "integrity": "sha512-4xKHMdg3foh3Va1fxTzY1qt8QVqmaJpGWsVvtjQrJBn+/bkig2pWFKJ4FPI2yLI4PAj0SUKiPO4Vd7ggYIMZjQ==" + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==" }, "node_modules/@types/semver": { "version": "7.3.8", @@ -26065,16 +26065,16 @@ } }, "@snyk/code-client": { - "version": "4.22.3", - "resolved": "https://registry.npmjs.org/@snyk/code-client/-/code-client-4.22.3.tgz", - "integrity": "sha512-rOa51ol5sMfm5eDtN/p0LhpnRWXrHqIF6vmRZsxN88p5B3d1YpZJOiZ/SYSv7yYB4Iubt1Cky7LD53Tj4ohCvg==", + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/@snyk/code-client/-/code-client-4.23.0.tgz", + "integrity": "sha512-m7dbryJGdVqsTtU/T+59W6q9MN0gF/9akH0a2/Vu7Eq20dlrVWa94ZnI/3IPyuQ/X73TRgYx2yTRb68kANdcXw==", "requires": { "@deepcode/dcignore": "^1.0.4", "@types/flat-cache": "^2.0.0", "@types/lodash.omit": "^4.5.7", "@types/lodash.pick": "^4.4.7", "@types/lodash.union": "^4.6.7", - "@types/sarif": "^2.1.4", + "@types/sarif": "^2.1.5", "@types/uuid": "^8.3.4", "fast-glob": "3.3.1", "ignore": "^5.2.4", @@ -27776,9 +27776,9 @@ } }, "@types/sarif": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.4.tgz", - "integrity": "sha512-4xKHMdg3foh3Va1fxTzY1qt8QVqmaJpGWsVvtjQrJBn+/bkig2pWFKJ4FPI2yLI4PAj0SUKiPO4Vd7ggYIMZjQ==" + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==" }, "@types/semver": { "version": "7.3.8", diff --git a/package.json b/package.json index 6ce5d999b8..c3cbf698e1 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@sentry/node": "^7.34.0", "@snyk/cli-interface": "2.12.0", "@snyk/cloud-config-parser": "^1.14.5", - "@snyk/code-client": "^4.22.3", + "@snyk/code-client": "^4.23.0", "@snyk/dep-graph": "^2.7.4", "@snyk/docker-registry-v2-client": "^2.10.0", "@snyk/fix": "file:packages/snyk-fix", diff --git a/src/lib/plugins/sast/errors/code-client-error.ts b/src/lib/plugins/sast/errors/code-client-error.ts index 88b6e7db05..940363e350 100644 --- a/src/lib/plugins/sast/errors/code-client-error.ts +++ b/src/lib/plugins/sast/errors/code-client-error.ts @@ -1,10 +1,19 @@ import { CustomError } from '../../../errors/custom-error'; +const messagePrefix = 'There was a problem running Code analysis'; + export class CodeClientError extends CustomError { constructor(statusCode: number, statusText: string, additionalUserHelp = '') { super(statusText); this.code = statusCode; + this.userMessage = `${messagePrefix}. ${additionalUserHelp}\nContact support if the problem persists.`; + } +} - this.userMessage = `There was a problem running Code analysis. ${additionalUserHelp}\nContact support if the problem persists.`; +export class CodeClientErrorWithDetail extends CustomError { + constructor(message: string, statusCode: number, detail: string) { + super(message); + this.code = statusCode; + this.userMessage = `${messagePrefix}. ${detail}.`; } } diff --git a/src/lib/plugins/sast/errors/index.ts b/src/lib/plugins/sast/errors/index.ts index 80906e5057..dfc0ed1bef 100644 --- a/src/lib/plugins/sast/errors/index.ts +++ b/src/lib/plugins/sast/errors/index.ts @@ -1,3 +1,6 @@ export { MissingConfigurationError } from './missing-configuration-error'; export { FeatureNotSupportedBySnykCodeError } from './unsupported-feature-snyk-code-error'; -export { CodeClientError } from './code-client-error'; +export { + CodeClientError, + CodeClientErrorWithDetail, +} from './code-client-error'; diff --git a/src/lib/plugins/sast/index.ts b/src/lib/plugins/sast/index.ts index fd5d134921..5207e2ddbd 100644 --- a/src/lib/plugins/sast/index.ts +++ b/src/lib/plugins/sast/index.ts @@ -11,7 +11,7 @@ import { } from './format/output-format'; import { EcosystemPlugin } from '../../ecosystems/types'; import { FailedToRunTestError, NoSupportedSastFiles } from '../../errors'; -import { CodeClientError } from './errors'; +import { CodeClientErrorWithDetail, CodeClientError } from './errors'; import { filterIgnoredIssues } from './utils'; import { jsonStringifyLargeObject } from '../../json'; import * as analytics from '../../analytics'; @@ -111,7 +111,9 @@ export const codePlugin: EcosystemPlugin = { return sarifResult ? { readableResult, sarifResult } : { readableResult }; } catch (error) { let err: Error; - if (isCodeClientError(error)) { + if (isCodeClientErrorWithDetail(error)) { + err = resolveCodeClientErrorWithDetail(error); + } else if (isCodeClientError(error)) { err = resolveCodeClientError(error); } else if (error instanceof Error) { err = error; @@ -180,6 +182,23 @@ function resolveCodeClientError(error: { ); } +function resolveCodeClientErrorWithDetail(error: any) { + return new CodeClientErrorWithDetail( + error.statusText, + error.statusCode, + error.detail, + ); +} + +function isCodeClientErrorWithDetail(error: any): boolean { + return ( + error.hasOwnProperty('statusCode') && + error.hasOwnProperty('statusText') && + error.hasOwnProperty('detail') && + error.hasOwnProperty('apiName') + ); +} + function isUnauthorizedError(error: any): boolean { return ( error.statusCode === 401 || diff --git a/test/jest/unit/snyk-code/snyk-code-test.spec.ts b/test/jest/unit/snyk-code/snyk-code-test.spec.ts index 274deb747e..445d1a9db5 100644 --- a/test/jest/unit/snyk-code/snyk-code-test.spec.ts +++ b/test/jest/unit/snyk-code/snyk-code-test.spec.ts @@ -734,6 +734,37 @@ describe('Test snyk code', () => { ).rejects.toHaveProperty('message', "Failed to run 'code test'"); }); + it('When code-client fails with details, show message with failure details', async () => { + const codeClientErrorWithDetail = { + apiName: 'getAnalysis', + statusCode: 422, + statusText: 'Analysis failed', + detail: 'Analysis failed, more info: https://snyk.io', + }; + + jest + .spyOn(analysis, 'getCodeTestResults') + .mockRejectedValue(codeClientErrorWithDetail); + + isSastEnabledForOrgSpy.mockResolvedValueOnce({ + sastEnabled: true, + localCodeEngine: { + enabled: false, + }, + }); + trackUsageSpy.mockResolvedValue({}); + + await expect( + ecosystems.testEcosystem('code', ['.'], { + path: '', + code: true, + }), + ).rejects.toHaveProperty( + 'userMessage', + 'There was a problem running Code analysis. Analysis failed, more info: https://snyk.io.', + ); + }); + it('analyzeFolders should be called with the right arguments', async () => { const baseURL = expect.any(String); const sessionToken = `token ${fakeApiKey}`;