util.inspect can be used to leak references to values from external scopes
@BridgeAR suggested this should be described in a distinct issue from #10731, where I’d mentioned it as part of the case for not expanding (and ideally limiting or removing) the exceptions already made for observing Proxy status/internals in util.inspect.
Currently, util.inspect provides avenues for violating ECMAScript invariants of Proxy objects, leaking (a) their status as Proxies and (b) actual references to the target and handler objects. The latter is more accurately violating an invariant of scopes, not Proxies.
This is possible because of the use of V8 APIs to obtain references to out-of-scope ES values, followed by passing those values into functions which may be externally provisioned or replaced (including its own context object and its children, but also global intrinsics like Array.isArray).
Here is an example. There are several ways to do this, and the results will vary depending on the avenues taken. (In this particular case, early exits for empty objects would not be captured.)
const { inspect } = require('util');
function obtainIllegalAccess(proxy) {
const seen = new class extends Array { pop() {} };
inspect(proxy, { seen, showProxy: true });
return seen;
}
const t = { foo: 1 };
const h = { bar: 2 };
const p = new Proxy(t, h);
obtainIllegalAccess(p); // [ { foo: 1 }, { bar: 2 } ]
My opinion is that this can be seen as symptomatic, where the underlying problem is the attempt to pivot on Proxy status at the ES layer at all; by design, this isn’t supposed to be possible. However even if the showProxy functionality is retained, it’d probably be possible to fix this leak by tightening how ctx works and closing over references to intrinsics at module evaluation time.