From ba0b5cfe408931de68f7a172f474e08ea77ce3f3 Mon Sep 17 00:00:00 2001 From: Pelle Wessman Date: Tue, 13 Dec 2022 21:42:42 +0100 Subject: [PATCH] Use `@socketsecurity/config` + improve errors --- cli.js | 1 + lib/commands/report/create.js | 24 ++++++++++++- lib/utils/errors.js | 14 +++++++- lib/utils/path-resolve.js | 2 +- lib/utils/socket-config.js | 66 ----------------------------------- package.json | 6 ++-- 6 files changed, 41 insertions(+), 72 deletions(-) delete mode 100644 lib/utils/socket-config.js diff --git a/cli.js b/cli.js index e01c4e035..52ea9105c 100755 --- a/cli.js +++ b/cli.js @@ -37,6 +37,7 @@ try { } else if (err instanceof InputError) { errorTitle = 'Invalid input' errorMessage = err.message + errorBody = err.body } else if (err instanceof Error) { errorTitle = 'Unexpected error' errorMessage = messageWithCauses(err) diff --git a/lib/commands/report/create.js b/lib/commands/report/create.js index 8723a8743..563b40317 100644 --- a/lib/commands/report/create.js +++ b/lib/commands/report/create.js @@ -2,16 +2,19 @@ import path from 'node:path' +import { betterAjvErrors } from '@apideck/better-ajv-errors' +import { readSocketConfig, SocketValidationError } from '@socketsecurity/config' import meow from 'meow' import ora from 'ora' +import { ErrorWithCause } from 'pony-cause' import { handleApiCall, handleUnsuccessfulApiResponse } from '../../utils/api-helpers.js' import { ChalkOrMarkdown, logSymbols } from '../../utils/chalk-markdown.js' +import { InputError } from '../../utils/errors.js' import { printFlagList } from '../../utils/formatting.js' import { createDebugLogger } from '../../utils/misc.js' import { getPackageFiles } from '../../utils/path-resolve.js' import { setupSdk } from '../../utils/sdk.js' -import { readSocketConfig } from '../../utils/socket-config.js' import { fetchReportData, formatReportDataOutput } from './view.js' /** @type {import('../../utils/meow-with-subcommands').CliSubcommand} */ @@ -166,6 +169,25 @@ async function setupCommand (name, description, argv, importMeta) { const absoluteConfigPath = path.join(cwd, 'socket.yml') const config = await readSocketConfig(absoluteConfigPath) + .catch(/** @param {unknown} cause */ cause => { + if (cause && typeof cause === 'object' && cause instanceof SocketValidationError) { + // Inspired by workbox-build: https://github.com/GoogleChrome/workbox/blob/95f97a207fd51efb3f8a653f6e3e58224183a778/packages/workbox-build/src/lib/validate-options.ts#L68-L71 + const betterErrors = betterAjvErrors({ + basePath: 'config', + data: cause.data, + errors: cause.validationErrors, + // @ts-ignore + schema: cause.schema, + }) + throw new InputError( + 'The socket.yml config is not valid', + betterErrors.map((err) => `[${err.path}] ${err.message}.${err.suggestion ? err.suggestion : ''}`).join('\n') + ) + } else { + throw new ErrorWithCause('Failed to read socket.yml config', { cause }) + } + }) + const packagePaths = await getPackageFiles(cwd, cli.input, config, debugLog) return { diff --git a/lib/utils/errors.js b/lib/utils/errors.js index 728be91f7..31972e8a1 100644 --- a/lib/utils/errors.js +++ b/lib/utils/errors.js @@ -1,2 +1,14 @@ export class AuthError extends Error {} -export class InputError extends Error {} + +export class InputError extends Error { + /** + * @param {string} message + * @param {string} [body] + */ + constructor (message, body) { + super(message) + + /** @type {string|undefined} */ + this.body = body + } +} diff --git a/lib/utils/path-resolve.js b/lib/utils/path-resolve.js index 98e4ea513..cd6d01951 100644 --- a/lib/utils/path-resolve.js +++ b/lib/utils/path-resolve.js @@ -33,7 +33,7 @@ const GLOB_IGNORE = [ * * @param {string} cwd The working directory to use when resolving paths * @param {string[]} inputPaths A list of paths to folders, package.json files and/or recognized lockfiles. Supports globs. - * @param {import('./socket-config.js').SocketYml|undefined} config + * @param {import('@socketsecurity/config').SocketYml|undefined} config * @param {typeof console.error} debugLog * @returns {Promise} * @throws {InputError} diff --git a/lib/utils/socket-config.js b/lib/utils/socket-config.js deleted file mode 100644 index aaf75c88b..000000000 --- a/lib/utils/socket-config.js +++ /dev/null @@ -1,66 +0,0 @@ -import { readFile } from 'node:fs/promises' - -import Ajv from 'ajv' -import { ErrorWithCause } from 'pony-cause' -import { parse as yamlParse } from 'yaml' - -import { isErrnoException } from './type-helpers.js' - -/** - * @typedef SocketYml - * @property {2} version - * @property {string[]} [projectIgnorePaths] - * @property {{ [issueName: string]: boolean }} [issueRules] - */ - -/** @type {import('ajv').JSONSchemaType} */ -const socketYmlSchema = { - $schema: 'http://json-schema.org/draft-07/schema#', - type: 'object', - properties: { - version: { type: 'integer' }, - projectIgnorePaths: { - type: 'array', - items: { type: 'string' }, - nullable: true, - }, - issueRules: { - type: 'object', - additionalProperties: { type: 'boolean' }, - nullable: true, - required: [], - }, - }, - required: ['version'], - additionalProperties: true, -} - -/** - * @param {string} filePath - * @returns {Promise} - */ -export async function readSocketConfig (filePath) { - /** @type {string} */ - let fileContent - - try { - fileContent = await readFile(filePath, 'utf8') - } catch (err) { - if (isErrnoException(err) && err.code === 'ENOENT') { - return - } - throw new ErrorWithCause('Error when reading socket.yml config file', { cause: err }) - } - - /** @type {unknown} */ - let parsedContent - - try { - parsedContent = yamlParse(fileContent) - } catch (err) { - throw new ErrorWithCause('Error when parsing socket.yml config', { cause: err }) - } - if ((new Ajv()).validate(socketYmlSchema, parsedContent)) { - return parsedContent - } -} diff --git a/package.json b/package.json index 82cda4664..961d439e9 100644 --- a/package.json +++ b/package.json @@ -74,8 +74,9 @@ "typescript": "~4.9.3" }, "dependencies": { + "@apideck/better-ajv-errors": "^0.3.6", + "@socketsecurity/config": "^1.1.0", "@socketsecurity/sdk": "^0.4.0", - "ajv": "^8.11.2", "chalk": "^5.1.2", "globby": "^13.1.3", "hpagent": "^1.2.0", @@ -88,7 +89,6 @@ "pony-cause": "^2.1.8", "prompts": "^2.4.2", "terminal-link": "^3.0.0", - "update-notifier": "^6.0.2", - "yaml": "^2.1.3" + "update-notifier": "^6.0.2" } }