Repository: nodejs/node
Pull request: util: inspect: do not crash on an Error with a regex `name`

See #56570 - this avoids a crash with a regex set as the `name` property of an Error

Changed files:
filename: lib/internal/util/inspect.js
patch: @@ -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);

filename: test/parallel/test-util-inspect.js
patch: @@ -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')}`,
+  );
+}