From e96072ad57348ce423a8dd7639dcc3d1c34e847d Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 15 Jan 2025 11:27:22 -0800 Subject: [PATCH] util: inspect: do not crash on an Error with a regex `name` See #56570 PR-URL: https://github.com/nodejs/node/pull/56574 Reviewed-By: James M Snell Reviewed-By: Matthew Aitken Reviewed-By: Marco Ippolito Reviewed-By: Luigi Pinca Reviewed-By: Ruben Bridgewater --- lib/internal/util/inspect.js | 17 ++++++++++++++--- test/parallel/test-util-inspect.js | 22 +++++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/lib/internal/util/inspect.js b/lib/internal/util/inspect.js index 743dc1fef23807..60bee498e5ea8b 100644 --- a/lib/internal/util/inspect.js +++ b/lib/internal/util/inspect.js @@ -88,6 +88,7 @@ const { StringPrototypePadEnd, StringPrototypePadStart, StringPrototypeRepeat, + StringPrototypeReplace, StringPrototypeReplaceAll, StringPrototypeSlice, StringPrototypeSplit, @@ -733,6 +734,7 @@ function addPrototypeProperties(ctx, main, obj, recurseTimes, output) { } while (++depth !== 3); } +/** @type {(constructor: string, tag: string, fallback: string, size?: string) => string} */ function getPrefix(constructor, tag, fallback, size = '') { if (constructor === null) { if (tag !== '' && fallback !== tag) { @@ -1316,11 +1318,20 @@ function getStackFrames(ctx, err, stack) { return frames; } +/** @type {(stack: string, constructor: string | null, name: unknown, tag: string) => string} */ function improveStack(stack, constructor, name, tag) { // A stack trace may contain arbitrary data. Only manipulate the output // for "regular errors" (errors that "look normal") for now. let len = name.length; + if (typeof name !== 'string') { + stack = StringPrototypeReplace( + stack, + `${name}`, + `${name} [${StringPrototypeSlice(getPrefix(constructor, tag, 'Error'), 0, -1)}]`, + ); + } + if (constructor === null || (StringPrototypeEndsWith(name, 'Error') && StringPrototypeStartsWith(stack, name) && @@ -1353,8 +1364,8 @@ function removeDuplicateErrorKeys(ctx, keys, err, stack) { if (!ctx.showHidden && keys.length !== 0) { for (const name of ['name', 'message', 'stack']) { const index = ArrayPrototypeIndexOf(keys, name); - // Only hide the property in case it's part of the original stack - if (index !== -1 && StringPrototypeIncludes(stack, err[name])) { + // Only hide the property if it's a string and if it's part of the original stack + if (index !== -1 && (typeof err[name] !== 'string' || StringPrototypeIncludes(stack, err[name]))) { ArrayPrototypeSplice(keys, index, 1); } } @@ -1414,7 +1425,7 @@ function safeGetCWD() { } function formatError(err, constructor, tag, ctx, keys) { - const name = err.name != null ? String(err.name) : 'Error'; + const name = err.name != null ? err.name : 'Error'; let stack = getStackString(err); removeDuplicateErrorKeys(ctx, keys, err, stack); diff --git a/test/parallel/test-util-inspect.js b/test/parallel/test-util-inspect.js index 9549bba2737a4f..0b04e3b8dc7179 100644 --- a/test/parallel/test-util-inspect.js +++ b/test/parallel/test-util-inspect.js @@ -770,14 +770,14 @@ assert.strictEqual(util.inspect(-5e-324), '-5e-324'); // Note: Symbols are not supported by `Error#toString()` which is called by // accessing the `stack` property. [ - [404, '404: foo', '[404]'], - [0, '0: foo', '[RangeError: foo]'], - [0n, '0: foo', '[RangeError: foo]'], + [404, '404 [RangeError]: foo', '[404]'], + [0, '0 [RangeError]: foo', '[RangeError: foo]'], + [0n, '0 [RangeError]: foo', '[RangeError: foo]'], [null, 'null: foo', '[RangeError: foo]'], [undefined, 'RangeError: foo', '[RangeError: foo]'], - [false, 'false: foo', '[RangeError: foo]'], + [false, 'false [RangeError]: foo', '[RangeError: foo]'], ['', 'foo', '[RangeError: foo]'], - [[1, 2, 3], '1,2,3: foo', '[1,2,3]'], + [[1, 2, 3], '1,2,3 [RangeError]: foo', '[1,2,3]'], ].forEach(([value, outputStart, stack]) => { let err = new RangeError('foo'); err.name = value; @@ -3436,3 +3436,15 @@ assert.strictEqual( '[Function: Symbol(f)]', ); } + +{ + const error = new EvalError(); + const re = /a/g; + error.name = re; + assert.strictEqual(error.name, re); + assert.strictEqual( + util.inspect(error), + `${re} [EvalError] +${error.stack.split('\n').slice(1).join('\n')}`, + ); +}