Skip to content

Commit 9e98e86

Browse files
aduh95ruyadorno
authored andcommitted
util: do not rely on mutable Object and Function' constructor prop
PR-URL: #56188 Fixes: #55924 Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Jordan Harband <[email protected]>
1 parent 5e5b4b0 commit 9e98e86

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

lib/internal/util/inspect.js

+25-5
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

+30
Original file line numberDiff line numberDiff line change
@@ -3323,3 +3323,33 @@ assert.strictEqual(
33233323
}
33243324
}), '{ [Symbol(Symbol.iterator)]: [Getter] }');
33253325
}
3326+
3327+
{
3328+
const o = {};
3329+
const { prototype: BuiltinPrototype } = Object;
3330+
const desc = Reflect.getOwnPropertyDescriptor(BuiltinPrototype, 'constructor');
3331+
Object.defineProperty(BuiltinPrototype, 'constructor', {
3332+
get: () => BuiltinPrototype,
3333+
configurable: true,
3334+
});
3335+
assert.strictEqual(
3336+
util.inspect(o),
3337+
'{}',
3338+
);
3339+
Object.defineProperty(BuiltinPrototype, 'constructor', desc);
3340+
}
3341+
3342+
{
3343+
const o = { f() {} };
3344+
const { prototype: BuiltinPrototype } = Function;
3345+
const desc = Reflect.getOwnPropertyDescriptor(BuiltinPrototype, 'constructor');
3346+
Object.defineProperty(BuiltinPrototype, 'constructor', {
3347+
get: () => BuiltinPrototype,
3348+
configurable: true,
3349+
});
3350+
assert.strictEqual(
3351+
util.inspect(o),
3352+
'{ f: [Function: f] }',
3353+
);
3354+
Object.defineProperty(BuiltinPrototype, 'constructor', desc);
3355+
}

0 commit comments

Comments
 (0)