util: use more defensive code when inspecting error objects · nodejs/node@63d5242 · GitHub
Skip to content

Commit 63d5242

Browse 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 b9277a0 commit 63d5242

3 files changed

Lines changed: 89 additions & 24 deletions

File tree

lib/internal/util.js

Lines changed: 2 additions & 1 deletion

lib/internal/util/inspect.js

Lines 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) {

test/parallel/test-util-inspect.js

Lines changed: 25 additions & 0 deletions

0 commit comments

Comments
 (0)