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 4a86016

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 f437204 commit 4a86016
Copy full SHA for 4a86016

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,
@@ -1720,13 +1721,19 @@ function getDuplicateErrorFrameRanges(frames) {
17201721
}
17211722

17221723
function getStackString(ctx, error) {
1723-
if (error.stack) {
1724-
if (typeof error.stack === 'string') {
1725-
return error.stack;
1724+
let stack;
1725+
try {
1726+
stack = error.stack;
1727+
} catch {
1728+
// If stack is getter that throws, we ignore the error.
1729+
}
1730+
if (stack) {
1731+
if (typeof stack === 'string') {
1732+
return stack;
17261733
}
17271734
ctx.seen.push(error);
17281735
ctx.indentationLvl += 4;
1729-
const result = formatValue(ctx, error.stack);
1736+
const result = formatValue(ctx, stack);
17301737
ctx.indentationLvl -= 4;
17311738
ctx.seen.pop();
17321739
return `${ErrorPrototypeToString(error)}\n ${result}`;
@@ -1822,18 +1829,6 @@ function improveStack(stack, constructor, name, tag) {
18221829
return stack;
18231830
}
18241831

1825-
function removeDuplicateErrorKeys(ctx, keys, err, stack) {
1826-
if (!ctx.showHidden && keys.length !== 0) {
1827-
for (const name of ['name', 'message', 'stack']) {
1828-
const index = ArrayPrototypeIndexOf(keys, name);
1829-
// Only hide the property if it's a string and if it's part of the original stack
1830-
if (index !== -1 && (typeof err[name] !== 'string' || StringPrototypeIncludes(stack, err[name]))) {
1831-
ArrayPrototypeSplice(keys, index, 1);
1832-
}
1833-
}
1834-
}
1835-
}
1836-
18371832
function markNodeModules(ctx, line) {
18381833
let tempLine = '';
18391834
let lastPos = 0;
@@ -1916,28 +1911,72 @@ function safeGetCWD() {
19161911
}
19171912

19181913
function formatError(err, constructor, tag, ctx, keys) {
1919-
const name = err.name != null ? err.name : 'Error';
1920-
let stack = getStackString(ctx, err);
1914+
let message, name, stack;
1915+
try {
1916+
stack = getStackString(ctx, err);
1917+
} catch {
1918+
return ObjectPrototypeToString(err);
1919+
}
19211920

1922-
removeDuplicateErrorKeys(ctx, keys, err, stack);
1921+
let messageIsGetterThatThrows = false;
1922+
try {
1923+
message = err.message;
1924+
} catch {
1925+
messageIsGetterThatThrows = true;
1926+
}
1927+
let nameIsGetterThatThrows = false;
1928+
try {
1929+
name = err.name;
1930+
} catch {
1931+
nameIsGetterThatThrows = true;
1932+
}
1933+
1934+
if (!ctx.showHidden && keys.length !== 0) {
1935+
const index = ArrayPrototypeIndexOf(keys, 'stack');
1936+
if (index !== -1) {
1937+
ArrayPrototypeSplice(keys, index, 1);
1938+
}
1939+
1940+
if (!messageIsGetterThatThrows) {
1941+
const index = ArrayPrototypeIndexOf(keys, 'message');
1942+
// Only hide the property if it's a string and if it's part of the original stack
1943+
if (index !== -1 && (typeof message !== 'string' || StringPrototypeIncludes(stack, message))) {
1944+
ArrayPrototypeSplice(keys, index, 1);
1945+
}
1946+
}
1947+
1948+
if (!nameIsGetterThatThrows) {
1949+
const index = ArrayPrototypeIndexOf(keys, 'name');
1950+
// Only hide the property if it's a string and if it's part of the original stack
1951+
if (index !== -1 && (typeof name !== 'string' || StringPrototypeIncludes(stack, name))) {
1952+
ArrayPrototypeSplice(keys, index, 1);
1953+
}
1954+
}
1955+
}
1956+
name ??= 'Error';
19231957

19241958
if ('cause' in err &&
19251959
(keys.length === 0 || !ArrayPrototypeIncludes(keys, 'cause'))) {
19261960
ArrayPrototypePush(keys, 'cause');
19271961
}
19281962

19291963
// Print errors aggregated into AggregateError
1930-
if (ArrayIsArray(err.errors) &&
1964+
try {
1965+
const errors = err.errors;
1966+
if (ArrayIsArray(errors) &&
19311967
(keys.length === 0 || !ArrayPrototypeIncludes(keys, 'errors'))) {
1932-
ArrayPrototypePush(keys, 'errors');
1968+
ArrayPrototypePush(keys, 'errors');
1969+
}
1970+
} catch {
1971+
// If errors is a getter that throws, we ignore the error.
19331972
}
19341973

19351974
stack = improveStack(stack, constructor, name, tag);
19361975

19371976
// Ignore the error message if it's contained in the stack.
1938-
let pos = (err.message && StringPrototypeIndexOf(stack, err.message)) || -1;
1977+
let pos = (message && StringPrototypeIndexOf(stack, message)) || -1;
19391978
if (pos !== -1)
1940-
pos += err.message.length;
1979+
pos += message.length;
19411980
// Wrap the error in brackets in case it has no stack trace.
19421981
const stackStart = StringPrototypeIndexOf(stack, '\n at', pos);
19431982
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.