Skip to content

Commit 4a91507

Browse files
authored
util: do not rely on mutable Object and Function' constructor prop
PR-URL: nodejs#56188 Fixes: nodejs#55924 Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Jordan Harband <[email protected]>
1 parent fd9a79d commit 4a91507

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

lib/internal/util/inspect.js

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ const {
2222
DatePrototypeToISOString,
2323
DatePrototypeToString,
2424
ErrorPrototypeToString,
25+
Function,
26+
FunctionPrototype,
2527
FunctionPrototypeBind,
2628
FunctionPrototypeCall,
29+
FunctionPrototypeSymbolHasInstance,
2730
FunctionPrototypeToString,
2831
JSONStringify,
2932
MapPrototypeEntries,
@@ -50,6 +53,7 @@ const {
5053
ObjectGetPrototypeOf,
5154
ObjectIs,
5255
ObjectKeys,
56+
ObjectPrototype,
5357
ObjectPrototypeHasOwnProperty,
5458
ObjectPrototypePropertyIsEnumerable,
5559
ObjectSeal,
@@ -593,10 +597,26 @@ function isInstanceof(object, proto) {
593597
}
594598
}
595599

600+
// Special-case for some builtin prototypes in case their `constructor` property has been tampered.
601+
const wellKnownPrototypes = new SafeMap();
602+
wellKnownPrototypes.set(ObjectPrototype, { name: 'Object', constructor: Object });
603+
wellKnownPrototypes.set(FunctionPrototype, { name: 'Function', constructor: Function });
604+
596605
function getConstructorName(obj, ctx, recurseTimes, protoProps) {
597606
let firstProto;
598607
const tmp = obj;
599608
while (obj || isUndetectableObject(obj)) {
609+
const wellKnownPrototypeNameAndConstructor = wellKnownPrototypes.get(obj);
610+
if (wellKnownPrototypeNameAndConstructor != null) {
611+
const { name, constructor } = wellKnownPrototypeNameAndConstructor;
612+
if (FunctionPrototypeSymbolHasInstance(constructor, tmp)) {
613+
if (protoProps !== undefined && firstProto !== obj) {
614+
addPrototypeProperties(
615+
ctx, tmp, firstProto || tmp, recurseTimes, protoProps);
616+
}
617+
return name;
618+
}
619+
}
600620
const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
601621
if (descriptor !== undefined &&
602622
typeof descriptor.value === 'function' &&
@@ -954,7 +974,11 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
954974
if (noIterator) {
955975
keys = getKeys(value, ctx.showHidden);
956976
braces = ['{', '}'];
957-
if (constructor === 'Object') {
977+
if (typeof value === 'function') {
978+
base = getFunctionBase(value, constructor, tag);
979+
if (keys.length === 0 && protoProps === undefined)
980+
return ctx.stylize(base, 'special');
981+
} else if (constructor === 'Object') {
958982
if (isArgumentsObject(value)) {
959983
braces[0] = '[Arguments] {';
960984
} else if (tag !== '') {
@@ -963,10 +987,6 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
963987
if (keys.length === 0 && protoProps === undefined) {
964988
return `${braces[0]}}`;
965989
}
966-
} else if (typeof value === 'function') {
967-
base = getFunctionBase(value, constructor, tag);
968-
if (keys.length === 0 && protoProps === undefined)
969-
return ctx.stylize(base, 'special');
970990
} else if (isRegExp(value)) {
971991
// Make RegExps say that they are RegExps
972992
base = RegExpPrototypeToString(

test/parallel/test-util-inspect.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3355,3 +3355,33 @@ assert.strictEqual(
33553355
'}',
33563356
);
33573357
}
3358+
3359+
{
3360+
const o = {};
3361+
const { prototype: BuiltinPrototype } = Object;
3362+
const desc = Reflect.getOwnPropertyDescriptor(BuiltinPrototype, 'constructor');
3363+
Object.defineProperty(BuiltinPrototype, 'constructor', {
3364+
get: () => BuiltinPrototype,
3365+
configurable: true,
3366+
});
3367+
assert.strictEqual(
3368+
util.inspect(o),
3369+
'{}',
3370+
);
3371+
Object.defineProperty(BuiltinPrototype, 'constructor', desc);
3372+
}
3373+
3374+
{
3375+
const o = { f() {} };
3376+
const { prototype: BuiltinPrototype } = Function;
3377+
const desc = Reflect.getOwnPropertyDescriptor(BuiltinPrototype, 'constructor');
3378+
Object.defineProperty(BuiltinPrototype, 'constructor', {
3379+
get: () => BuiltinPrototype,
3380+
configurable: true,
3381+
});
3382+
assert.strictEqual(
3383+
util.inspect(o),
3384+
'{ f: [Function: f] }',
3385+
);
3386+
Object.defineProperty(BuiltinPrototype, 'constructor', desc);
3387+
}

0 commit comments

Comments
 (0)