From a5ccb2500840c04470d9be9ed0da98136ade6b9b Mon Sep 17 00:00:00 2001 From: Ahmed Agabani <70949530+ahmed-agabani-snyk@users.noreply.github.com> Date: Wed, 4 Nov 2020 11:10:14 +0000 Subject: [PATCH] fix: Improve error message for scanning an image that doesn't exist --- src/lib/ecosystems/monitor.ts | 29 +++++++++++++++---- .../errors/docker-image-not-found-error.ts | 12 ++++++++ src/lib/errors/index.ts | 1 + src/lib/snyk-test/run-test.ts | 8 +++++ .../cli-monitor.acceptance.test.ts | 17 +++++++++++ .../cli-test/cli-test.docker.spec.ts | 16 ++++++++++ 6 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 src/lib/errors/docker-image-not-found-error.ts diff --git a/src/lib/ecosystems/monitor.ts b/src/lib/ecosystems/monitor.ts index 77f1d7fef2..4c55362059 100644 --- a/src/lib/ecosystems/monitor.ts +++ b/src/lib/ecosystems/monitor.ts @@ -11,7 +11,11 @@ import { getPlugin } from './plugins'; import { BadResult, GoodResult } from '../../cli/commands/monitor/types'; import { formatMonitorOutput } from '../../cli/commands/monitor/formatters/format-monitor-response'; import { getExtraProjectCount } from '../plugins/get-extra-project-count'; -import { AuthFailedError, MonitorError } from '../errors'; +import { + AuthFailedError, + DockerImageNotFoundError, + MonitorError, +} from '../errors'; import { Ecosystem, ScanResult, @@ -32,11 +36,24 @@ export async function monitorEcosystem( const plugin = getPlugin(ecosystem); const scanResultsByPath: { [dir: string]: ScanResult[] } = {}; for (const path of paths) { - await spinner(`Analyzing dependencies in ${path}`); - options.path = path; - const pluginResponse = await plugin.scan(options); - scanResultsByPath[path] = pluginResponse.scanResults; - spinner.clearAll(); + try { + await spinner(`Analyzing dependencies in ${path}`); + options.path = path; + const pluginResponse = await plugin.scan(options); + scanResultsByPath[path] = pluginResponse.scanResults; + } catch (error) { + if ( + ecosystem === 'docker' && + error.statusCode === 401 && + error.message === 'authentication required' + ) { + throw new DockerImageNotFoundError(path); + } + + throw error; + } finally { + spinner.clearAll(); + } } const [monitorResults, errors] = await monitorDependencies( scanResultsByPath, diff --git a/src/lib/errors/docker-image-not-found-error.ts b/src/lib/errors/docker-image-not-found-error.ts new file mode 100644 index 0000000000..25b86d5ef3 --- /dev/null +++ b/src/lib/errors/docker-image-not-found-error.ts @@ -0,0 +1,12 @@ +import { CustomError } from './custom-error'; + +export class DockerImageNotFoundError extends CustomError { + private static ERROR_CODE = 404; + + constructor(image: string) { + const message = `Failed to scan image "${image}". Please make sure the image and/or repository exist.`; + super(message); + this.code = DockerImageNotFoundError.ERROR_CODE; + this.userMessage = message; + } +} diff --git a/src/lib/errors/index.ts b/src/lib/errors/index.ts index fce0684e15..8fbf2bc3b1 100644 --- a/src/lib/errors/index.ts +++ b/src/lib/errors/index.ts @@ -19,6 +19,7 @@ export { OptionMissingErrorError } from './option-missing-error'; export { ExcludeFlagBadInputError } from './exclude-flag-bad-input'; export { UnsupportedOptionCombinationError } from './unsupported-option-combination-error'; export { FeatureNotSupportedByPackageManagerError } from './feature-not-supported-by-package-manager-error'; +export { DockerImageNotFoundError } from './docker-image-not-found-error'; export { NotSupportedIacFileError, NotSupportedIacFileErrorMsg, diff --git a/src/lib/snyk-test/run-test.ts b/src/lib/snyk-test/run-test.ts index a16f652e3a..fc794f2462 100644 --- a/src/lib/snyk-test/run-test.ts +++ b/src/lib/snyk-test/run-test.ts @@ -27,6 +27,7 @@ import { FailedToGetVulnsFromUnavailableResource, FailedToRunTestError, UnsupportedFeatureFlagError, + DockerImageNotFoundError, } from '../errors'; import * as snyk from '../'; import { isCI } from '../is-ci'; @@ -318,6 +319,13 @@ export async function runTest( if (hasFailedToGetVulnerabilities) { throw FailedToGetVulnsFromUnavailableResource(root, error.code); } + if ( + getEcosystem(options) === 'docker' && + error.statusCode === 401 && + error.message === 'authentication required' + ) { + throw new DockerImageNotFoundError(root); + } throw new FailedToRunTestError( error.userMessage || diff --git a/test/acceptance/cli-monitor/cli-monitor.acceptance.test.ts b/test/acceptance/cli-monitor/cli-monitor.acceptance.test.ts index c5aab985c7..dfbcf862ec 100644 --- a/test/acceptance/cli-monitor/cli-monitor.acceptance.test.ts +++ b/test/acceptance/cli-monitor/cli-monitor.acceptance.test.ts @@ -1873,6 +1873,23 @@ if (!isWindows) { ); }); + test('`monitor doesnotexist --docker`', async (t) => { + try { + await cli.monitor('doesnotexist', { + docker: true, + org: 'explicit-org', + }); + t.fail('should have failed'); + } catch (err) { + t.match( + err.message, + 'Failed to scan image "doesnotexist". Please make sure the image and/or repository exist.', + 'show err message', + ); + t.pass('throws err'); + } + }); + test('monitor --json multiple folders', async (t) => { chdirWorkspaces('fail-on'); diff --git a/test/acceptance/cli-test/cli-test.docker.spec.ts b/test/acceptance/cli-test/cli-test.docker.spec.ts index 48b859c0fd..8dd0ec39cb 100644 --- a/test/acceptance/cli-test/cli-test.docker.spec.ts +++ b/test/acceptance/cli-test/cli-test.docker.spec.ts @@ -671,6 +671,22 @@ export const DockerTests: AcceptanceTests = { ); t.end(); }, + + '`test --docker doesnotexist`': (params) => async (t) => { + try { + await params.cli.test('doesnotexist', { + docker: true, + org: 'explicit-org', + }); + t.fail('should have thrown'); + } catch (err) { + const msg = err.message; + t.match( + msg, + 'Failed to scan image "doesnotexist". Please make sure the image and/or repository exist.', + ); + } + }, }, };