From cd344baac41a48eb28363f554b1ddbc78f7b0ca3 Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 31 May 2023 20:40:32 +0800 Subject: [PATCH] Fix parsing of Error objects when redacting (#18777) * Fix parsing of Error objects when redacting * Create perfect-ducks-add.md * Move errorReplacer into redact util * Use unknown instead of any Co-authored-by: Pascal Jufer --------- Co-authored-by: Pascal Jufer --- .changeset/perfect-ducks-add.md | 5 +++ api/src/utils/redact.test.ts | 70 ++++++++++++++++++++++++++++++++- api/src/utils/redact.ts | 18 ++++++++- 3 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 .changeset/perfect-ducks-add.md diff --git a/.changeset/perfect-ducks-add.md b/.changeset/perfect-ducks-add.md new file mode 100644 index 0000000000..60f972e31c --- /dev/null +++ b/.changeset/perfect-ducks-add.md @@ -0,0 +1,5 @@ +--- +"@directus/api": patch +--- + +Fixed parsing of Error objects when redacting diff --git a/api/src/utils/redact.test.ts b/api/src/utils/redact.test.ts index fd9e30ebb4..fac81589f6 100644 --- a/api/src/utils/redact.test.ts +++ b/api/src/utils/redact.test.ts @@ -1,7 +1,7 @@ import { REDACTED_TEXT } from '@directus/constants'; import { merge } from 'lodash-es'; -import { expect, test } from 'vitest'; -import { redact } from './redact.js'; +import { describe, expect, test } from 'vitest'; +import { errorReplacer, redact } from './redact.js'; const input = { $trigger: { @@ -142,3 +142,69 @@ test('should support multiple paths', () => { }) ); }); + +describe('errorReplacer tests', () => { + test('Returns parsed error object', () => { + const errorMessage = 'Error Message'; + const errorCause = 'Error Cause'; + const result = errorReplacer('', new Error(errorMessage, { cause: errorCause })); + expect(result.name).toBe('Error'); + expect(result.message).toBe(errorMessage); + expect(result.stack).toBeDefined(); + expect(result.cause).toBe(errorCause); + }); + + test('Returns other types as is', () => { + const values = [ + undefined, + null, + 0, + 1, + true, + false, + '', + 'abc', + [], + ['123'], + {}, + { abc: '123' }, + () => { + // empty + }, + ]; + + for (const value of values) { + expect(errorReplacer('', value)).toBe(value); + } + }); + + test('Correctly parses error object when used with JSON.stringify()', () => { + const errorMessage = 'Error Message'; + const errorCause = 'Error Cause'; + + const baseValue = { + string: 'abc', + num: 123, + bool: true, + null: null, + }; + + const objWithError = { + ...baseValue, + error: new Error(errorMessage, { cause: errorCause }), + }; + + const expectedResult = { + ...baseValue, + error: { name: 'Error', message: errorMessage, cause: errorCause }, + }; + + const result = JSON.parse(JSON.stringify(objWithError, errorReplacer)); + + // Stack changes depending on env + expect(result.error.stack).toBeDefined(); + delete result.error.stack; + + expect(result).toStrictEqual(expectedResult); + }); +}); diff --git a/api/src/utils/redact.ts b/api/src/utils/redact.ts index dd9568a07a..eb4529b15f 100644 --- a/api/src/utils/redact.ts +++ b/api/src/utils/redact.ts @@ -13,7 +13,7 @@ type Paths = string[][]; export function redact(input: UnknownObject, paths: Paths, replacement: string): UnknownObject { const wildcardChars = ['*', '**']; - const clone = structuredClone(input); + const clone = JSON.parse(JSON.stringify(input, errorReplacer)); const visited = new WeakSet(); traverse(clone, paths); @@ -86,3 +86,19 @@ export function redact(input: UnknownObject, paths: Paths, replacement: string): } } } + +/** + * Extract values from Error objects for use with JSON.stringify() + */ +export function errorReplacer(_key: string, value: unknown) { + if (value instanceof Error) { + return { + name: value.name, + message: value.message, + stack: value.stack, + cause: value.cause, + }; + } + + return value; +}