Skip to content

Commit

Permalink
feat: compose error catalog errors in TS custom errors
Browse files Browse the repository at this point in the history
  • Loading branch information
j-luong committed Feb 11, 2025
1 parent 2f1cc43 commit 6004297
Show file tree
Hide file tree
Showing 69 changed files with 331 additions and 113 deletions.
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"@snyk/code-client": "^4.23.5",
"@snyk/dep-graph": "^2.7.4",
"@snyk/docker-registry-v2-client": "^2.11.0",
"@snyk/error-catalog-nodejs-public": "^5.37.0",
"@snyk/error-catalog-nodejs-public": "^5.42.0",
"@snyk/fix": "file:packages/snyk-fix",
"@snyk/gemfile": "1.2.0",
"@snyk/snyk-cocoapods-plugin": "2.5.3",
Expand Down
2 changes: 2 additions & 0 deletions src/cli/commands/describe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { DCTL_EXIT_CODES, runDriftCTL } from '../../lib/iac/drift/driftctl';
import { IaCErrorCodes } from './test/iac/local-execution/types';
import { getErrorStringCode } from './test/iac/local-execution/error-utils';
import { DescribeOptions } from '../../lib/iac/types';
import { CLI } from '@snyk/error-catalog-nodejs-public';

export class FlagError extends CustomError {
constructor(flag: string) {
Expand All @@ -25,6 +26,7 @@ export class FlagError extends CustomError {
this.code = IaCErrorCodes.FlagError;
this.strCode = getErrorStringCode(this.code);
this.userMessage = msg;
this.errorCatalog = new CLI.InvalidFlagOptionError(msg);
}
}
export default async (...args: MethodArgs): Promise<any> => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from './types';
import { Options, TestOptions } from '../../../../../lib/types';
import { IacV2Name } from '../../../../../lib/iac/constants';
import { CLI } from '@snyk/error-catalog-nodejs-public';

const keys: (keyof IaCTestFlags)[] = [
'org',
Expand Down Expand Up @@ -67,6 +68,7 @@ export class FlagError extends CustomError {
this.code = IaCErrorCodes.FlagError;
this.strCode = getErrorStringCode(this.code);
this.userMessage = msg;
this.errorCatalog = new CLI.InvalidFlagOptionError(msg);
}
}

Expand All @@ -78,6 +80,7 @@ export class IntegratedFlagError extends CustomError {
this.code = IaCErrorCodes.FlagError;
this.strCode = getErrorStringCode(this.code);
this.userMessage = msg;
this.errorCatalog = new CLI.InvalidFlagOptionError(msg);
}
}

Expand All @@ -94,6 +97,7 @@ export class FeatureFlagError extends CustomError {
this.code = IaCErrorCodes.FeatureFlagError;
this.strCode = getErrorStringCode(this.code);
this.userMessage = msg;
this.errorCatalog = new CLI.InvalidFlagOptionError(msg);
}
}

Expand All @@ -105,29 +109,34 @@ export class FlagValueError extends CustomError {
this.code = IaCErrorCodes.FlagValueError;
this.strCode = getErrorStringCode(this.code);
this.userMessage = msg;
this.errorCatalog = new CLI.InvalidFlagOptionError(msg);
}
}

export class UnsupportedEntitlementFlagError extends CustomError {
constructor(key: string, entitlementName: string) {
const flag = getFlagName(key);
const msg = `Flag "${flag}" is currently not supported for this org. To enable it, please contact snyk support.`;
super(
`Unsupported flag: ${flag} - Missing the ${entitlementName} entitlement`,
);
this.code = IaCErrorCodes.UnsupportedEntitlementFlagError;
this.strCode = getErrorStringCode(this.code);
this.userMessage = `Flag "${flag}" is currently not supported for this org. To enable it, please contact snyk support.`;
this.userMessage = msg;
this.errorCatalog = new CLI.InvalidFlagOptionError(msg);
}
}

export class UnsupportedEntitlementCommandError extends CustomError {
constructor(key: string, entitlementName: string) {
const usrMsg = `Command "${key}" is currently not supported for this org. To enable it, please contact snyk support.`;
super(
`Unsupported command: ${key} - Missing the ${entitlementName} entitlement`,
);
this.code = IaCErrorCodes.UnsupportedEntitlementFlagError;
this.strCode = getErrorStringCode(this.code);
this.userMessage = `Command "${key}" is currently not supported for this org. To enable it, please contact snyk support.`;
this.userMessage = usrMsg;
this.errorCatalog = new CLI.GeneralIACFailureError(usrMsg);
}
}

Expand Down Expand Up @@ -213,5 +222,6 @@ export class InvalidArgumentError extends CustomError {
this.code = IaCErrorCodes.InvalidArgumentError;
this.strCode = getErrorStringCode(this.code);
this.userMessage = msg;
this.errorCatalog = new CLI.InvalidFlagOptionError(msg);
}
}
9 changes: 7 additions & 2 deletions src/cli/commands/test/iac/local-execution/file-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { IacFileTypes } from '../../../../../lib/iac/constants';
import { CustomError } from '../../../../../lib/errors';
import { getErrorStringCode } from './error-utils';
import { getFileType } from './directory-loader';
import { CLI } from '@snyk/error-catalog-nodejs-public';

const DEFAULT_ENCODING = 'utf-8';

Expand Down Expand Up @@ -47,21 +48,25 @@ function removeBom(s: string): string {

export class NoFilesToScanError extends CustomError {
constructor(message?: string) {
super(message || 'Could not find any valid IaC files');
const msg = message || 'Could not find any valid IaC files';
super(msg);
this.code = IaCErrorCodes.NoFilesToScanError;
this.strCode = getErrorStringCode(this.code);
this.userMessage =
'Could not find any valid infrastructure as code files. Supported file extensions are tf, yml, yaml & json.\nMore information can be found by running `snyk iac test --help` or through our documentation:\nhttps://support.snyk.io/hc/en-us/articles/360012429477-Test-your-Kubernetes-files-with-our-CLI-tool\nhttps://support.snyk.io/hc/en-us/articles/360013723877-Test-your-Terraform-files-with-our-CLI-tool';
this.errorCatalog = new CLI.GeneralIACFailureError(msg);
}
}

export class FailedToLoadFileError extends CustomError {
public filename: string;
constructor(filename: string) {
const usrMsg = `We were unable to read file "${filename}" for scanning. Please ensure that it is readable.`;
super('Failed to load file content');
this.code = IaCErrorCodes.FailedToLoadFileError;
this.strCode = getErrorStringCode(this.code);
this.filename = filename;
this.userMessage = `We were unable to read file "${filename}" for scanning. Please ensure that it is readable.`;
this.userMessage = usrMsg;
this.errorCatalog = new CLI.GeneralIACFailureError(usrMsg);
}
}
5 changes: 4 additions & 1 deletion src/cli/commands/test/iac/local-execution/file-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import hclToJsonV2 from './parsers/hcl-to-json-v2';
import { IacProjectType } from '../../../../../lib/iac/constants';

import * as Debug from 'debug';
import { CLI } from '@snyk/error-catalog-nodejs-public';

const debug = Debug('snyk-test');

Expand Down Expand Up @@ -164,9 +165,11 @@ export function tryParseIacFile(

export class UnsupportedFileTypeError extends CustomError {
constructor(fileType: string) {
super('Unsupported file extension');
const msg = 'Unsupported file extension';
super(msg);
this.code = IaCErrorCodes.UnsupportedFileTypeError;
this.strCode = getErrorStringCode(this.code);
this.userMessage = `Unable to process the file with extension ${fileType}. Supported file extensions are tf, yml, yaml & json.\nMore information can be found by running \`snyk iac test --help\` or through our documentation:\nhttps://support.snyk.io/hc/en-us/articles/360012429477-Test-your-Kubernetes-files-with-our-CLI-tool\nhttps://support.snyk.io/hc/en-us/articles/360013723877-Test-your-Terraform-files-with-our-CLI-tool`;
this.errorCatalog = new CLI.GeneralIACFailureError(msg);
}
}
9 changes: 7 additions & 2 deletions src/cli/commands/test/iac/local-execution/file-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { CustomError } from '../../../../../lib/errors';
import { getErrorStringCode } from './error-utils';
import { IacFileInDirectory } from '../../../../../lib/types';
import { SEVERITIES } from '../../../../../lib/snyk-test/common';
import { CLI } from '@snyk/error-catalog-nodejs-public';

export async function scanFiles(parsedFiles: Array<IacFileParsed>): Promise<{
scannedFiles: IacFileScanResult[];
Expand Down Expand Up @@ -165,19 +166,23 @@ class PolicyEngine {

export class FailedToBuildPolicyEngine extends CustomError {
constructor(message?: string) {
super(message || 'Failed to build policy engine');
const msg = message || 'Failed to build policy engine';
super(msg);
this.code = IaCErrorCodes.FailedToBuildPolicyEngine;
this.strCode = getErrorStringCode(this.code);
this.userMessage =
'We were unable to run the test. Please run the command again with the `-d` flag and contact [email protected] with the contents of the output';
this.errorCatalog = new CLI.GeneralIACFailureError(msg);
}
}
export class FailedToExecutePolicyEngine extends CustomError {
constructor(message?: string) {
super(message || 'Failed to execute policy engine');
const msg = message || 'Failed to execute policy engine';
super(msg);
this.code = IaCErrorCodes.FailedToExecutePolicyEngine;
this.strCode = getErrorStringCode(this.code);
this.userMessage =
'We were unable to run the test. Please run the command again with the `-d` flag and contact [email protected] with the contents of the output';
this.errorCatalog = new CLI.GeneralIACFailureError(msg);
}
}
5 changes: 4 additions & 1 deletion src/cli/commands/test/iac/local-execution/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { CustomError } from '../../../../../lib/errors';
import { getErrorStringCode } from './error-utils';
import { NoFilesToScanError } from './file-loader';
import { Tag } from '../../../../../lib/types';
import { CLI } from '@snyk/error-catalog-nodejs-public';

// this method executes the local processing engine and then formats the results to adapt with the CLI output.
// this flow is the default GA flow for IAC scanning.
Expand Down Expand Up @@ -180,9 +181,11 @@ function shouldLoadVarDefinitionsFile(

export class InvalidVarFilePath extends CustomError {
constructor(path: string, message?: string) {
super(message || 'Invalid path to variable definitions file');
const msg = message || 'Invalid path to variable definitions file';
super(msg);
this.code = IaCErrorCodes.InvalidVarFilePath;
this.strCode = getErrorStringCode(this.code);
this.userMessage = `We were unable to locate a variable definitions file at: "${path}". The file at the provided path does not exist`;
this.errorCatalog = new CLI.GeneralIACFailureError(msg);
}
}
25 changes: 18 additions & 7 deletions src/cli/commands/test/iac/local-execution/local-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getErrorStringCode } from './error-utils';
import config from '../../../../../lib/config';
import { streamRequest } from '../../../../../lib/request/request';
import envPaths from 'env-paths';
import { CLI } from '@snyk/error-catalog-nodejs-public';

const debug = Debug('iac-local-cache');

Expand Down Expand Up @@ -171,48 +172,58 @@ export function cleanLocalCache() {

export class FailedToInitLocalCacheError extends CustomError {
constructor(message?: string) {
const usrMsg =
'We were unable to create a local directory to store the test assets, please ensure that the current working directory is writable';
super(message || 'Failed to initialize local cache');
this.code = IaCErrorCodes.FailedToInitLocalCacheError;
this.strCode = getErrorStringCode(this.code);
this.userMessage =
'We were unable to create a local directory to store the test assets, please ensure that the current working directory is writable';
this.userMessage = usrMsg;
this.errorCatalog = new CLI.GeneralIACFailureError(usrMsg);
}
}

export class FailedToDownloadRulesError extends CustomError {
constructor(message?: string) {
const usrMsg =
'We were unable to download the security rules, please ensure the network can access https://downloads.snyk.io';
super(message || 'Failed to download policies');
this.code = IaCErrorCodes.FailedToDownloadRulesError;
this.strCode = getErrorStringCode(this.code);
this.userMessage =
'We were unable to download the security rules, please ensure the network can access https://downloads.snyk.io';
this.userMessage = usrMsg;
this.errorCatalog = new CLI.GeneralIACFailureError(usrMsg);
}
}

export class FailedToExtractCustomRulesError extends CustomError {
constructor(path: string, message?: string) {
const usrMsg = `We were unable to extract the rules provided at: ${path}. The provided bundle may be corrupted or invalid. Please ensure it was generated using the 'snyk-iac-rules' SDK`;
super(message || 'Failed to download policies');
this.code = IaCErrorCodes.FailedToExtractCustomRulesError;
this.strCode = getErrorStringCode(this.code);
this.userMessage = `We were unable to extract the rules provided at: ${path}. The provided bundle may be corrupted or invalid. Please ensure it was generated using the 'snyk-iac-rules' SDK`;
this.userMessage = usrMsg;
this.errorCatalog = new CLI.GeneralIACFailureError(usrMsg);
}
}

export class InvalidCustomRules extends CustomError {
constructor(path: string, message?: string) {
const usrMsg = `We were unable to extract the rules provided at: ${path}. The provided bundle does not match the required structure. Please ensure it was generated using the 'snyk-iac-rules' SDK`;
super(message || 'Invalid custom rules bundle');
this.code = IaCErrorCodes.InvalidCustomRules;
this.strCode = getErrorStringCode(this.code);
this.userMessage = `We were unable to extract the rules provided at: ${path}. The provided bundle does not match the required structure. Please ensure it was generated using the 'snyk-iac-rules' SDK`;
this.userMessage = usrMsg;
this.errorCatalog = new CLI.GeneralIACFailureError(usrMsg);
}
}

export class InvalidCustomRulesPath extends CustomError {
constructor(path: string, message?: string) {
const usrMsg = `We were unable to extract the rules provided at: ${path}. The bundle at the provided path does not exist`;
super(message || 'Invalid path to custom rules bundle');
this.code = IaCErrorCodes.InvalidCustomRulesPath;
this.strCode = getErrorStringCode(this.code);
this.userMessage = `We were unable to extract the rules provided at: ${path}. The bundle at the provided path does not exist`;
this.userMessage = usrMsg;
this.errorCatalog = new CLI.GeneralIACFailureError(usrMsg);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getAuthHeader } from '../../../../../../lib/api-token';
import { makeRequest } from '../../../../../../lib/request';
import { CustomError } from '../../../../../../lib/errors';
import { getErrorStringCode } from '../error-utils';
import { CLI } from '@snyk/error-catalog-nodejs-public';

export function getIacOrgSettings(
publicOrgId?: string,
Expand Down Expand Up @@ -36,10 +37,12 @@ export function getIacOrgSettings(

export class FailedToGetIacOrgSettingsError extends CustomError {
constructor(message?: string) {
const usrMsg =
'We failed to fetch your organization settings, including custom severity overrides for infrastructure-as-code policies. Please run the command again with the `-d` flag and contact [email protected] with the contents of the output.';
super(message || 'Failed to fetch IaC organization settings');
this.code = IaCErrorCodes.FailedToGetIacOrgSettingsError;
this.strCode = getErrorStringCode(this.code);
this.userMessage =
'We failed to fetch your organization settings, including custom severity overrides for infrastructure-as-code policies. Please run the command again with the `-d` flag and contact [email protected] with the contents of the output.';
this.userMessage = usrMsg;
this.errorCatalog = new CLI.GeneralIACFailureError(usrMsg);
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { IaCErrorCodes } from '../types';
import { CustomError } from '../../../../../../lib/errors';
import { getErrorStringCode } from '../error-utils';
import { CLI } from '@snyk/error-catalog-nodejs-public';

export class FailedToParseTerraformFileError extends CustomError {
public filename: string;
constructor(filename: string) {
const usrMsg = `We were unable to parse the Terraform file "${filename}", please ensure it is valid HCL2. This can be done by running it through the 'terraform validate' command.`;
super('Failed to parse Terraform file');
this.code = IaCErrorCodes.FailedToParseTerraformFileError;
this.strCode = getErrorStringCode(this.code);
this.filename = filename;
this.userMessage = `We were unable to parse the Terraform file "${filename}", please ensure it is valid HCL2. This can be done by running it through the 'terraform validate' command.`;
this.userMessage = usrMsg;
this.errorCatalog = new CLI.GeneralIACFailureError(usrMsg);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import { CustomError } from '../../../../../../lib/errors';
import { getErrorStringCode } from '../error-utils';
import { IacProjectType } from '../../../../../../lib/iac/constants';
import { CLI } from '@snyk/error-catalog-nodejs-public';

function terraformPlanReducer(
scanInput: TerraformScanInput,
Expand Down Expand Up @@ -183,12 +184,14 @@ export function tryParsingTerraformPlan(
// This error is due to the complex reduction logic, so it catches scenarios we might have not covered.
export class FailedToExtractResourcesInTerraformPlanError extends CustomError {
constructor(message?: string) {
const usrMsg =
'We failed to extract resource changes from the Terraform plan file, please contact [email protected], if possible with a redacted version of the file';
super(
message || 'Failed to extract resources from Terraform plan JSON file',
);
this.code = IaCErrorCodes.FailedToExtractResourcesInTerraformPlanError;
this.strCode = getErrorStringCode(this.code);
this.userMessage =
'We failed to extract resource changes from the Terraform plan file, please contact [email protected], if possible with a redacted version of the file';
this.userMessage = usrMsg;
this.errorCatalog = new CLI.GeneralIACFailureError(usrMsg);
}
}
Loading

0 comments on commit 6004297

Please sign in to comment.