diff --git a/src/Commands/Configure.ts b/src/Commands/Configure.ts index 8a817ef2..7dc94205 100644 --- a/src/Commands/Configure.ts +++ b/src/Commands/Configure.ts @@ -1,4 +1,4 @@ -import { logger } from '../Utils'; +import { ErrorMessageFactory, logger } from '../Utils'; import { ConnectivityUrls, Platform, TestType, Options } from '../Wizard'; import container from '../container'; import { Arguments, Argv, CommandModule } from 'yargs'; @@ -98,8 +98,10 @@ export class Configure implements CommandModule { process.on('SIGTERM', stop).on('SIGINT', stop).on('SIGHUP', stop); await app.start({ ping: !!args.ping, traceroute: !!args.traceroute }); - } catch (e) { - logger.error(`Error during "configure": ${e.error || e.message}`); + } catch (error) { + logger.error( + ErrorMessageFactory.genericCommandError({ error, command: 'configure' }) + ); process.exit(1); } } diff --git a/src/Commands/GetEntryPoints.ts b/src/Commands/GetEntryPoints.ts index d54e3a7b..7548b935 100644 --- a/src/Commands/GetEntryPoints.ts +++ b/src/Commands/GetEntryPoints.ts @@ -1,5 +1,5 @@ import { EntryPoint, EntryPoints, RestProjectsOptions } from '../EntryPoint'; -import { logger } from '../Utils'; +import { ErrorMessageFactory, logger } from '../Utils'; import { Arguments, Argv, CommandModule } from 'yargs'; import { container } from 'tsyringe'; @@ -90,8 +90,13 @@ export class GetEntryPoints implements CommandModule { ); process.exitCode = 0; - } catch (e) { - logger.error(`Error during "entrypoints:list": ${e.error || e.message}`); + } catch (error) { + logger.error( + ErrorMessageFactory.genericCommandError({ + error, + command: 'entrypoints:list' + }) + ); process.exitCode = 1; } } diff --git a/src/Commands/PollingScanStatus.ts b/src/Commands/PollingScanStatus.ts index e392eae7..d8ddd72d 100644 --- a/src/Commands/PollingScanStatus.ts +++ b/src/Commands/PollingScanStatus.ts @@ -4,7 +4,7 @@ import { PollingFactory, RestScansOptions } from '../Scan'; -import { Helpers, logger } from '../Utils'; +import { ErrorMessageFactory, Helpers, logger } from '../Utils'; import { Arguments, Argv, CommandModule } from 'yargs'; import { container } from 'tsyringe'; @@ -73,14 +73,19 @@ export class PollingScanStatus implements CommandModule { await polling.start(); process.exit(0); - } catch (e) { - if (e instanceof BreakpointException) { + } catch (error) { + if (error instanceof BreakpointException) { logger.error(`The breakpoint has been hit during polling.`); - logger.error(`Breakpoint: ${e.message}`); + logger.error(`Breakpoint: ${error.message}`); process.exit(50); } - logger.error(`Error during "scan:polling": ${e.error || e.message}`); + logger.error( + ErrorMessageFactory.genericCommandError({ + error, + command: 'scan:polling' + }) + ); process.exit(1); } } diff --git a/src/Commands/RetestScan.ts b/src/Commands/RetestScan.ts index 486268d6..c759a7e4 100644 --- a/src/Commands/RetestScan.ts +++ b/src/Commands/RetestScan.ts @@ -1,5 +1,5 @@ import { RestScansOptions, Scans } from '../Scan'; -import { logger } from '../Utils'; +import { ErrorMessageFactory, logger } from '../Utils'; import { Arguments, Argv, CommandModule } from 'yargs'; import { container } from 'tsyringe'; @@ -43,8 +43,13 @@ export class RetestScan implements CommandModule { console.log(scanId); process.exit(0); - } catch (e) { - logger.error(`Error during "scan:retest": ${e.error || e.message}`); + } catch (error) { + logger.error( + ErrorMessageFactory.genericCommandError({ + error, + command: 'scan:retest' + }) + ); process.exit(1); } } diff --git a/src/Commands/RunRepeater.ts b/src/Commands/RunRepeater.ts index a31ee853..6c7aaaf7 100644 --- a/src/Commands/RunRepeater.ts +++ b/src/Commands/RunRepeater.ts @@ -1,5 +1,5 @@ import { Cert, RequestExecutorOptions } from '../RequestExecutor'; -import { Helpers, logger } from '../Utils'; +import { ErrorMessageFactory, Helpers, logger } from '../Utils'; import container from '../container'; import { DefaultRepeaterServerOptions, RepeaterLauncher } from '../Repeater'; import { Arguments, Argv, CommandModule } from 'yargs'; @@ -254,9 +254,11 @@ export class RunRepeater implements CommandModule { ); await repeaterLauncher.run(args.id as string, args.run as boolean); - } catch (e) { - captureException(e); - logger.error(e); + } catch (error) { + captureException(error); + logger.error( + ErrorMessageFactory.genericCommandError({ error, command: 'repeater' }) + ); await repeaterLauncher.close(); process.exitCode = 1; } diff --git a/src/Commands/RunScan.spec.ts b/src/Commands/RunScan.spec.ts index ab7665d0..a39fbdf4 100644 --- a/src/Commands/RunScan.spec.ts +++ b/src/Commands/RunScan.spec.ts @@ -180,7 +180,7 @@ describe('RunScan', () => { // assert verify(processSpy.exit(1)).once(); - verify(loggerSpy.error(`Error during "scan:run": ${errMessage}`)).once(); + verify(loggerSpy.error(`Error during "scan:run": ${errMessage}.`)).once(); }); it('should display warnings when present', async () => { diff --git a/src/Commands/RunScan.ts b/src/Commands/RunScan.ts index dfb91d98..f6880c56 100644 --- a/src/Commands/RunScan.ts +++ b/src/Commands/RunScan.ts @@ -7,10 +7,9 @@ import { Scans, ATTACK_PARAM_LOCATIONS_DEFAULT } from '../Scan'; -import { Helpers, logger } from '../Utils'; +import { ErrorMessageFactory, Helpers, logger } from '../Utils'; import { Arguments, Argv, CommandModule } from 'yargs'; import { container } from 'tsyringe'; -import { isAxiosError } from 'axios'; import { EOL } from 'node:os'; export class RunScan implements CommandModule { @@ -25,7 +24,11 @@ export class RunScan implements CommandModule { if (!nonEmptyPatterns.length) { logger.error( - 'Error during "scan:run": please make sure that patterns contain at least one regexp.' + ErrorMessageFactory.genericCommandError({ + command: 'scan:run', + error: + 'please make sure that patterns contain at least one regexp' + }) ); process.exit(1); } @@ -214,12 +217,10 @@ export class RunScan implements CommandModule { } process.exit(0); - } catch (e) { - const errMessage = - isAxiosError(e) && typeof e.response?.data === 'string' - ? e.response.data - : e.error || e.message; - logger.error(`Error during "scan:run": ${errMessage}`); + } catch (error) { + logger.error( + ErrorMessageFactory.genericCommandError({ error, command: 'scan:run' }) + ); process.exit(1); } } diff --git a/src/Commands/StopScan.ts b/src/Commands/StopScan.ts index 6dfb104a..16cee97b 100644 --- a/src/Commands/StopScan.ts +++ b/src/Commands/StopScan.ts @@ -1,5 +1,5 @@ import { RestScansOptions, Scans } from '../Scan'; -import { logger } from '../Utils'; +import { ErrorMessageFactory, logger } from '../Utils'; import { Arguments, Argv, CommandModule } from 'yargs'; import { container } from 'tsyringe'; @@ -41,8 +41,10 @@ export class StopScan implements CommandModule { await scanManager.stop(args.scan as string); process.exit(0); - } catch (e) { - logger.error(`Error during "scan:stop": ${e.error || e.message}`); + } catch (error) { + logger.error( + ErrorMessageFactory.genericCommandError({ error, command: 'scan:stop' }) + ); process.exit(1); } } diff --git a/src/Commands/UploadArchive.ts b/src/Commands/UploadArchive.ts index 2d0ef793..48528c9c 100644 --- a/src/Commands/UploadArchive.ts +++ b/src/Commands/UploadArchive.ts @@ -5,7 +5,7 @@ import { Spec, SpecType } from '../Archive'; -import { Helpers, logger } from '../Utils'; +import { ErrorMessageFactory, Helpers, logger } from '../Utils'; import container from '../container'; import { Arguments, Argv, CommandModule } from 'yargs'; @@ -119,8 +119,13 @@ export class UploadArchive implements CommandModule { // eslint-disable-next-line no-console console.log(await archives.upload(spec)); process.exit(0); - } catch (e) { - logger.error(`Error during "archive:upload": ${e.message}`); + } catch (error) { + logger.error( + ErrorMessageFactory.genericCommandError({ + error, + command: 'archive:upload' + }) + ); process.exit(1); } } diff --git a/src/Utils/ErrorMessageFactory.ts b/src/Utils/ErrorMessageFactory.ts new file mode 100644 index 00000000..26e9ffac --- /dev/null +++ b/src/Utils/ErrorMessageFactory.ts @@ -0,0 +1,45 @@ +import { isAxiosError } from 'axios'; + +type GenericCommandErrorParams = + | { command: string; error: any } + | { message: string; error: unknown }; + +export class ErrorMessageFactory { + public static genericCommandError(params: GenericCommandErrorParams): string { + const message = this.getTitle(params); + const details = this.extractErrorDetails(params); + + return this.formatFinalMessage(message, details); + } + + private static formatFinalMessage( + baseMessage: string, + errorDetails?: string + ): string { + return errorDetails + ? `${baseMessage}: ${errorDetails}.` + : `${baseMessage}.`; + } + + private static getTitle(params: GenericCommandErrorParams): string { + return 'message' in params + ? params.message + : `Error during "${params.command}"`; + } + + private static extractErrorDetails( + params: GenericCommandErrorParams + ): string | null { + if (typeof params.error === 'string') { + return params.error; + } + if ( + isAxiosError(params.error) && + typeof params.error.response?.data === 'string' + ) { + params.error.response.data; + } + + return (params.error.error || params.error.message) ?? null; + } +} diff --git a/src/Utils/index.ts b/src/Utils/index.ts index 3fc0f10a..fb970383 100644 --- a/src/Utils/index.ts +++ b/src/Utils/index.ts @@ -1,5 +1,6 @@ export * from './Backoff'; export * from './DefaultProxyFactory'; +export * from './ErrorMessageFactory'; export * from './Helpers'; export * from './Logger'; export * from './ProxyFactory';