Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 8cb3b77

Browse filesBrowse files
committed
util: use more defensive code when inspecting error objects
PR-URL: #60139 Fixes: #60107 Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
1 parent 39ddd85 commit 8cb3b77
Copy full SHA for 8cb3b77

File tree

Expand file treeCollapse file tree

3 files changed

+89
-24
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+89
-24
lines changed
Open diff view settings
Collapse file

‎lib/internal/util.js‎

Copy file name to clipboardExpand all lines: lib/internal/util.js
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ const {
88
Error,
99
ErrorCaptureStackTrace,
1010
FunctionPrototypeCall,
11+
FunctionPrototypeSymbolHasInstance,
1112
NumberParseInt,
1213
ObjectDefineProperties,
1314
ObjectDefineProperty,
@@ -96,7 +97,7 @@ function isError(e) {
9697
// An error could be an instance of Error while not being a native error
9798
// or could be from a different realm and not be instance of Error but still
9899
// be a native error.
99-
return isNativeError(e) || e instanceof Error;
100+
return isNativeError(e) || FunctionPrototypeSymbolHasInstance(Error, e);
100101
}
101102

102103
// Keep a list of deprecation codes that have been warned on so we only warn on
Collapse file

‎lib/internal/util/inspect.js‎

Copy file name to clipboardExpand all lines: lib/internal/util/inspect.js
+62-23Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const {
7272
ObjectPrototype,
7373
ObjectPrototypeHasOwnProperty,
7474
ObjectPrototypePropertyIsEnumerable,
75+
ObjectPrototypeToString,
7576
ObjectSeal,
7677
ObjectSetPrototypeOf,
7778
Promise,
@@ -1468,13 +1469,19 @@ function getDuplicateErrorFrameRanges(frames) {
14681469
}
14691470

14701471
function getStackString(ctx, error) {
1471-
if (error.stack) {
1472-
if (typeof error.stack === 'string') {
1473-
return error.stack;
1472+
let stack;
1473+
try {
1474+
stack = error.stack;
1475+
} catch {
1476+
// If stack is getter that throws, we ignore the error.
1477+
}
1478+
if (stack) {
1479+
if (typeof stack === 'string') {
1480+
return stack;
14741481
}
14751482
ctx.seen.push(error);
14761483
ctx.indentationLvl += 4;
1477-
const result = formatValue(ctx, error.stack);
1484+
const result = formatValue(ctx, stack);
14781485
ctx.indentationLvl -= 4;
14791486
ctx.seen.pop();
14801487
return `${ErrorPrototypeToString(error)}\n ${result}`;
@@ -1570,18 +1577,6 @@ function improveStack(stack, constructor, name, tag) {
15701577
return stack;
15711578
}
15721579

1573-
function removeDuplicateErrorKeys(ctx, keys, err, stack) {
1574-
if (!ctx.showHidden && keys.length !== 0) {
1575-
for (const name of ['name', 'message', 'stack']) {
1576-
const index = ArrayPrototypeIndexOf(keys, name);
1577-
// Only hide the property if it's a string and if it's part of the original stack
1578-
if (index !== -1 && (typeof err[name] !== 'string' || StringPrototypeIncludes(stack, err[name]))) {
1579-
ArrayPrototypeSplice(keys, index, 1);
1580-
}
1581-
}
1582-
}
1583-
}
1584-
15851580
function markNodeModules(ctx, line) {
15861581
let tempLine = '';
15871582
let lastPos = 0;
@@ -1664,28 +1659,72 @@ function safeGetCWD() {
16641659
}
16651660

16661661
function formatError(err, constructor, tag, ctx, keys) {
1667-
const name = err.name != null ? err.name : 'Error';
1668-
let stack = getStackString(ctx, err);
1662+
let message, name, stack;
1663+
try {
1664+
stack = getStackString(ctx, err);
1665+
} catch {
1666+
return ObjectPrototypeToString(err);
1667+
}
16691668

1670-
removeDuplicateErrorKeys(ctx, keys, err, stack);
1669+
let messageIsGetterThatThrows = false;
1670+
try {
1671+
message = err.message;
1672+
} catch {
1673+
messageIsGetterThatThrows = true;
1674+
}
1675+
let nameIsGetterThatThrows = false;
1676+
try {
1677+
name = err.name;
1678+
} catch {
1679+
nameIsGetterThatThrows = true;
1680+
}
1681+
1682+
if (!ctx.showHidden && keys.length !== 0) {
1683+
const index = ArrayPrototypeIndexOf(keys, 'stack');
1684+
if (index !== -1) {
1685+
ArrayPrototypeSplice(keys, index, 1);
1686+
}
1687+
1688+
if (!messageIsGetterThatThrows) {
1689+
const index = ArrayPrototypeIndexOf(keys, 'message');
1690+
// Only hide the property if it's a string and if it's part of the original stack
1691+
if (index !== -1 && (typeof message !== 'string' || StringPrototypeIncludes(stack, message))) {
1692+
ArrayPrototypeSplice(keys, index, 1);
1693+
}
1694+
}
1695+
1696+
if (!nameIsGetterThatThrows) {
1697+
const index = ArrayPrototypeIndexOf(keys, 'name');
1698+
// Only hide the property if it's a string and if it's part of the original stack
1699+
if (index !== -1 && (typeof name !== 'string' || StringPrototypeIncludes(stack, name))) {
1700+
ArrayPrototypeSplice(keys, index, 1);
1701+
}
1702+
}
1703+
}
1704+
name ??= 'Error';
16711705

16721706
if ('cause' in err &&
16731707
(keys.length === 0 || !ArrayPrototypeIncludes(keys, 'cause'))) {
16741708
ArrayPrototypePush(keys, 'cause');
16751709
}
16761710

16771711
// Print errors aggregated into AggregateError
1678-
if (ArrayIsArray(err.errors) &&
1712+
try {
1713+
const errors = err.errors;
1714+
if (ArrayIsArray(errors) &&
16791715
(keys.length === 0 || !ArrayPrototypeIncludes(keys, 'errors'))) {
1680-
ArrayPrototypePush(keys, 'errors');
1716+
ArrayPrototypePush(keys, 'errors');
1717+
}
1718+
} catch {
1719+
// If errors is a getter that throws, we ignore the error.
16811720
}
16821721

16831722
stack = improveStack(stack, constructor, name, tag);
16841723

16851724
// Ignore the error message if it's contained in the stack.
1686-
let pos = (err.message && StringPrototypeIndexOf(stack, err.message)) || -1;
1725+
let pos = (message && StringPrototypeIndexOf(stack, message)) || -1;
16871726
if (pos !== -1)
1688-
pos += err.message.length;
1727+
pos += message.length;
16891728
// Wrap the error in brackets in case it has no stack trace.
16901729
const stackStart = StringPrototypeIndexOf(stack, '\n at', pos);
16911730
if (stackStart === -1) {
Collapse file

‎test/parallel/test-util-inspect.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-util-inspect.js
+25Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3716,3 +3716,28 @@ ${error.stack.split('\n').slice(1).join('\n')}`,
37163716

37173717
assert.strictEqual(inspect(error), '[Error: foo\n [Error: bar\n [Circular *1]]]');
37183718
}
3719+
3720+
{
3721+
Object.defineProperty(Error, Symbol.hasInstance,
3722+
{ __proto__: null, value: common.mustNotCall(), configurable: true });
3723+
const error = new Error();
3724+
3725+
const throwingGetter = {
3726+
__proto__: null,
3727+
get() {
3728+
throw error;
3729+
},
3730+
configurable: true,
3731+
enumerable: true,
3732+
};
3733+
3734+
Object.defineProperties(error, {
3735+
name: throwingGetter,
3736+
stack: throwingGetter,
3737+
cause: throwingGetter,
3738+
});
3739+
3740+
assert.strictEqual(inspect(error), `[object Error] {\n stack: [Getter/Setter],\n name: [Getter],\n cause: [Getter]\n}`);
3741+
assert.match(inspect(DOMException.prototype), /^\[object DOMException\] \{/);
3742+
delete Error[Symbol.hasInstance];
3743+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.