Skip to content

Commit 524a76c

Browse files
aduh95marco-ippolito
authored andcommitted
util: do not rely on mutable Object and Function' constructor prop
PR-URL: #56188 Fixes: #55924 Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Jordan Harband <ljharb@gmail.com>
1 parent 3e763fe commit 524a76c

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
MapPrototypeGetSize,
@@ -50,6 +53,7 @@ const {
5053
ObjectGetPrototypeOf,
5154
ObjectIs,
5255
ObjectKeys,
56+
ObjectPrototype,
5357
ObjectPrototypeHasOwnProperty,
5458
ObjectPrototypePropertyIsEnumerable,
5559
ObjectSeal,
@@ -588,10 +592,26 @@ function isInstanceof(object, proto) {
588592
}
589593
}
590594

595+
// Special-case for some builtin prototypes in case their `constructor` property has been tampered.
596+
const wellKnownPrototypes = new SafeMap();
597+
wellKnownPrototypes.set(ObjectPrototype, { name: 'Object', constructor: Object });
598+
wellKnownPrototypes.set(FunctionPrototype, { name: 'Function', constructor: Function });
599+
591600
function getConstructorName(obj, ctx, recurseTimes, protoProps) {
592601
let firstProto;
593602
const tmp = obj;
594603
while (obj || isUndetectableObject(obj)) {
604+
const wellKnownPrototypeNameAndConstructor = wellKnownPrototypes.get(obj);
605+
if (wellKnownPrototypeNameAndConstructor != null) {
606+
const { name, constructor } = wellKnownPrototypeNameAndConstructor;
607+
if (FunctionPrototypeSymbolHasInstance(constructor, tmp)) {
608+
if (protoProps !== undefined && firstProto !== obj) {
609+
addPrototypeProperties(
610+
ctx, tmp, firstProto || tmp, recurseTimes, protoProps);
611+
}
612+
return name;
613+
}
614+
}
595615
const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor');
596616
if (descriptor !== undefined &&
597617
typeof descriptor.value === 'function' &&
@@ -949,7 +969,11 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
949969
if (noIterator) {
950970
keys = getKeys(value, ctx.showHidden);
951971
braces = ['{', '}'];
952-
if (constructor === 'Object') {
972+
if (typeof value === 'function') {
973+
base = getFunctionBase(value, constructor, tag);
974+
if (keys.length === 0 && protoProps === undefined)
975+
return ctx.stylize(base, 'special');
976+
} else if (constructor === 'Object') {
953977
if (isArgumentsObject(value)) {
954978
braces[0] = '[Arguments] {';
955979
} else if (tag !== '') {
@@ -958,10 +982,6 @@ function formatRaw(ctx, value, recurseTimes, typedArray) {
958982
if (keys.length === 0 && protoProps === undefined) {
959983
return `${braces[0]}}`;
960984
}
961-
} else if (typeof value === 'function') {
962-
base = getFunctionBase(value, constructor, tag);
963-
if (keys.length === 0 && protoProps === undefined)
964-
return ctx.stylize(base, 'special');
965985
} else if (isRegExp(value)) {
966986
// Make RegExps say that they are RegExps
967987
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)