Skip to content

Commit 75a8076

Browse files
committed
util: fix nested proxy inspection
Fixes: #61061
1 parent dc97b50 commit 75a8076

File tree

2 files changed

+37
-8
lines changed

2 files changed

+37
-8
lines changed

lib/internal/util/inspect.js

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ const {
173173
isModuleNamespaceObject,
174174
isNativeError,
175175
isPromise,
176+
isProxy,
176177
isSet,
177178
isSetIterator,
178179
isWeakMap,
@@ -1120,15 +1121,21 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
11201121
const context = value;
11211122
// Always check for proxies to prevent side effects and to prevent triggering
11221123
// any proxy handlers.
1123-
const proxy = getProxyDetails(value, !!ctx.showProxy);
1124+
let proxy = getProxyDetails(value, !!ctx.showProxy);
11241125
if (proxy !== undefined) {
1125-
if (proxy === null || proxy[0] === null) {
1126-
return ctx.stylize('<Revoked Proxy>', 'special');
1127-
}
11281126
if (ctx.showProxy) {
1127+
if (proxy[0] === null) {
1128+
return ctx.stylize('<Revoked Proxy>', 'special');
1129+
}
11291130
return formatProxy(ctx, proxy, recurseTimes);
11301131
}
1131-
value = proxy;
1132+
do {
1133+
if (proxy === null) {
1134+
return ctx.stylize('<Revoked Proxy>', 'special');
1135+
}
1136+
value = proxy;
1137+
proxy = getProxyDetails(value, false);
1138+
} while (proxy !== undefined);
11321139
}
11331140

11341141
// Provide a hook for user-specified inspect functions.
@@ -1144,7 +1151,7 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
11441151
// a counter internally.
11451152
const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
11461153
const isCrossContext =
1147-
proxy !== undefined || !FunctionPrototypeSymbolHasInstance(Object, context);
1154+
context !== value || !FunctionPrototypeSymbolHasInstance(Object, context);
11481155
const ret = FunctionPrototypeCall(
11491156
maybeCustom,
11501157
context,
@@ -2691,7 +2698,7 @@ function hasBuiltInToString(value) {
26912698
if (proxyTarget === null) {
26922699
return true;
26932700
}
2694-
value = proxyTarget;
2701+
return hasBuiltInToString(proxyTarget);
26952702
}
26962703

26972704
let hasOwnToString = ObjectPrototypeHasOwnProperty;

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ proxyObj = new Proxy(target, handler);
4242
util.inspect(proxyObj, opts);
4343

4444
// Make sure inspecting object does not trigger any proxy traps.
45-
util.format('%s', proxyObj);
45+
// %i%f%d use Symbol.toPrimitive to convert the value to a string.
46+
// %j uses JSON.stringify, accessing the value's toJSON and toString method.
47+
util.format('%s%o%O%c', proxyObj, proxyObj, proxyObj, proxyObj);
48+
const nestedProxy = new Proxy(new Proxy({}, handler), {});
49+
util.format('%s%o%O%c', nestedProxy, nestedProxy, nestedProxy, nestedProxy);
4650

4751
// getProxyDetails is an internal method, not intended for public use.
4852
// This is here to test that the internals are working correctly.
@@ -188,3 +192,21 @@ assert.strictEqual(
188192
')\x1B[39m'
189193
);
190194
assert.strictEqual(util.format('%s', proxy12), 'Proxy([ 1, 2, 3 ])');
195+
196+
{
197+
// Nested proxies should not trigger any proxy handlers.
198+
const nestedProxy = new Proxy(new Proxy(new Proxy({}, handler), {}), {});
199+
200+
util.inspect(nestedProxy, { showProxy: true });
201+
util.inspect(nestedProxy, { showProxy: false });
202+
}
203+
204+
{
205+
// Nested revoked proxies should work as expected as well as custom inspection functions.
206+
const revocable = Proxy.revocable({}, handler);
207+
revocable.revoke();
208+
const nestedProxy = new Proxy(revocable.proxy, {});
209+
210+
assert.strictEqual(util.inspect(nestedProxy, { showProxy: true }), 'Proxy [ <Revoked Proxy>, {} ]');
211+
assert.strictEqual(util.inspect(nestedProxy, { showProxy: false }), '<Revoked Proxy>');
212+
}

0 commit comments

Comments
 (0)