Skip to content

Commit 3e17a8e

Browse files
committed
util: harden more built-in classes against prototype pollution
PR-URL: #56225 Reviewed-By: Jordan Harband <ljharb@gmail.com> Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
1 parent 25bb462 commit 3e17a8e

File tree

3 files changed

+67
-4
lines changed

3 files changed

+67
-4
lines changed

lib/buffer.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const {
3535
NumberMIN_SAFE_INTEGER,
3636
ObjectDefineProperties,
3737
ObjectDefineProperty,
38+
ObjectPrototypeHasOwnProperty,
3839
ObjectSetPrototypeOf,
3940
RegExpPrototypeSymbolReplace,
4041
StringPrototypeCharCodeAt,
@@ -910,7 +911,14 @@ Buffer.prototype[customInspectSymbol] = function inspect(recurseTimes, ctx) {
910911
}), 27, -2);
911912
}
912913
}
913-
return `<${this.constructor.name} ${str}>`;
914+
let constructorName = 'Buffer';
915+
try {
916+
const { constructor } = this;
917+
if (typeof constructor === 'function' && ObjectPrototypeHasOwnProperty(constructor, 'name')) {
918+
constructorName = constructor.name;
919+
}
920+
} catch { /* Ignore error and use default name */ }
921+
return `<${constructorName} ${str}>`;
914922
};
915923
Buffer.prototype.inspect = Buffer.prototype[customInspectSymbol];
916924

lib/internal/util/inspect.js

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
const {
44
Array,
5+
ArrayBuffer,
6+
ArrayBufferPrototype,
57
ArrayIsArray,
8+
ArrayPrototype,
69
ArrayPrototypeFilter,
710
ArrayPrototypeForEach,
811
ArrayPrototypeIncludes,
@@ -29,6 +32,8 @@ const {
2932
FunctionPrototypeSymbolHasInstance,
3033
FunctionPrototypeToString,
3134
JSONStringify,
35+
Map,
36+
MapPrototype,
3237
MapPrototypeEntries,
3338
MapPrototypeGetSize,
3439
MathFloor,
@@ -68,6 +73,8 @@ const {
6873
SafeMap,
6974
SafeSet,
7075
SafeStringIterator,
76+
Set,
77+
SetPrototype,
7178
SetPrototypeGetSize,
7279
SetPrototypeValues,
7380
String,
@@ -93,6 +100,8 @@ const {
93100
SymbolPrototypeValueOf,
94101
SymbolToPrimitive,
95102
SymbolToStringTag,
103+
TypedArray,
104+
TypedArrayPrototype,
96105
TypedArrayPrototypeGetLength,
97106
TypedArrayPrototypeGetSymbolToStringTag,
98107
Uint8Array,
@@ -599,8 +608,13 @@ function isInstanceof(object, proto) {
599608

600609
// Special-case for some builtin prototypes in case their `constructor` property has been tampered.
601610
const wellKnownPrototypes = new SafeMap();
602-
wellKnownPrototypes.set(ObjectPrototype, { name: 'Object', constructor: Object });
611+
wellKnownPrototypes.set(ArrayPrototype, { name: 'Array', constructor: Array });
612+
wellKnownPrototypes.set(ArrayBufferPrototype, { name: 'ArrayBuffer', constructor: ArrayBuffer });
603613
wellKnownPrototypes.set(FunctionPrototype, { name: 'Function', constructor: Function });
614+
wellKnownPrototypes.set(MapPrototype, { name: 'Map', constructor: Map });
615+
wellKnownPrototypes.set(ObjectPrototype, { name: 'Object', constructor: Object });
616+
wellKnownPrototypes.set(SetPrototype, { name: 'Set', constructor: Set });
617+
wellKnownPrototypes.set(TypedArrayPrototype, { name: 'TypedArray', constructor: TypedArray });
604618

605619
function getConstructorName(obj, ctx, recurseTimes, protoProps) {
606620
let firstProto;
@@ -825,12 +839,12 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
825839
// Filter out the util module, its inspect function is special.
826840
maybeCustom !== inspect &&
827841
// Also filter out any prototype objects using the circular check.
828-
!(value.constructor && value.constructor.prototype === value)) {
842+
ObjectGetOwnPropertyDescriptor(value, 'constructor')?.value?.prototype !== value) {
829843
// This makes sure the recurseTimes are reported as before while using
830844
// a counter internally.
831845
const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
832846
const isCrossContext =
833-
proxy !== undefined || !(context instanceof Object);
847+
proxy !== undefined || !FunctionPrototypeSymbolHasInstance(Object, context);
834848
const ret = FunctionPrototypeCall(
835849
maybeCustom,
836850
context,

test/parallel/test-util-inspect.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3353,3 +3353,44 @@ assert.strictEqual(
33533353
);
33543354
Object.defineProperty(BuiltinPrototype, 'constructor', desc);
33553355
}
3356+
{
3357+
const prototypes = [
3358+
Array.prototype,
3359+
ArrayBuffer.prototype,
3360+
Buffer.prototype,
3361+
Function.prototype,
3362+
Map.prototype,
3363+
Object.prototype,
3364+
Reflect.getPrototypeOf(Uint8Array.prototype),
3365+
Set.prototype,
3366+
Uint8Array.prototype,
3367+
];
3368+
const descriptors = new Map();
3369+
const buffer = Buffer.from('Hello');
3370+
const o = {
3371+
arrayBuffer: new ArrayBuffer(), buffer, typedArray: Uint8Array.from(buffer),
3372+
array: [], func() {}, set: new Set([1]), map: new Map(),
3373+
};
3374+
for (const BuiltinPrototype of prototypes) {
3375+
descriptors.set(BuiltinPrototype, Reflect.getOwnPropertyDescriptor(BuiltinPrototype, 'constructor'));
3376+
Object.defineProperty(BuiltinPrototype, 'constructor', {
3377+
get: () => BuiltinPrototype,
3378+
configurable: true,
3379+
});
3380+
}
3381+
assert.strictEqual(
3382+
util.inspect(o),
3383+
'{\n' +
3384+
' arrayBuffer: ArrayBuffer { [Uint8Contents]: <>, byteLength: 0 },\n' +
3385+
' buffer: <Buffer 48 65 6c 6c 6f>,\n' +
3386+
' typedArray: TypedArray(5) [Uint8Array] [ 72, 101, 108, 108, 111 ],\n' +
3387+
' array: [],\n' +
3388+
' func: [Function: func],\n' +
3389+
' set: Set(1) { 1 },\n' +
3390+
' map: Map(0) {}\n' +
3391+
'}',
3392+
);
3393+
for (const [BuiltinPrototype, desc] of descriptors) {
3394+
Object.defineProperty(BuiltinPrototype, 'constructor', desc);
3395+
}
3396+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy