mirror of https://github.com/nodejs/node.git
util: harden more built-in classes against prototype pollution
PR-URL: https://github.com/nodejs/node/pull/56225 Reviewed-By: Jordan Harband <ljharb@gmail.com> Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>pull/56283/head
parent
b171afefb6
commit
80e3ef38ee
|
@ -35,6 +35,7 @@ const {
|
|||
NumberMIN_SAFE_INTEGER,
|
||||
ObjectDefineProperties,
|
||||
ObjectDefineProperty,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
ObjectSetPrototypeOf,
|
||||
RegExpPrototypeSymbolReplace,
|
||||
StringPrototypeCharCodeAt,
|
||||
|
@ -911,7 +912,14 @@ Buffer.prototype[customInspectSymbol] = function inspect(recurseTimes, ctx) {
|
|||
}), 27, -2);
|
||||
}
|
||||
}
|
||||
return `<${this.constructor.name} ${str}>`;
|
||||
let constructorName = 'Buffer';
|
||||
try {
|
||||
const { constructor } = this;
|
||||
if (typeof constructor === 'function' && ObjectPrototypeHasOwnProperty(constructor, 'name')) {
|
||||
constructorName = constructor.name;
|
||||
}
|
||||
} catch { /* Ignore error and use default name */ }
|
||||
return `<${constructorName} ${str}>`;
|
||||
};
|
||||
Buffer.prototype.inspect = Buffer.prototype[customInspectSymbol];
|
||||
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
const {
|
||||
Array,
|
||||
ArrayBuffer,
|
||||
ArrayBufferPrototype,
|
||||
ArrayIsArray,
|
||||
ArrayPrototype,
|
||||
ArrayPrototypeFilter,
|
||||
ArrayPrototypeForEach,
|
||||
ArrayPrototypeIncludes,
|
||||
|
@ -29,6 +32,8 @@ const {
|
|||
FunctionPrototypeSymbolHasInstance,
|
||||
FunctionPrototypeToString,
|
||||
JSONStringify,
|
||||
Map,
|
||||
MapPrototype,
|
||||
MapPrototypeEntries,
|
||||
MapPrototypeGetSize,
|
||||
MathFloor,
|
||||
|
@ -68,6 +73,8 @@ const {
|
|||
SafeMap,
|
||||
SafeSet,
|
||||
SafeStringIterator,
|
||||
Set,
|
||||
SetPrototype,
|
||||
SetPrototypeGetSize,
|
||||
SetPrototypeValues,
|
||||
String,
|
||||
|
@ -93,6 +100,8 @@ const {
|
|||
SymbolPrototypeValueOf,
|
||||
SymbolToPrimitive,
|
||||
SymbolToStringTag,
|
||||
TypedArray,
|
||||
TypedArrayPrototype,
|
||||
TypedArrayPrototypeGetLength,
|
||||
TypedArrayPrototypeGetSymbolToStringTag,
|
||||
Uint8Array,
|
||||
|
@ -599,8 +608,13 @@ function isInstanceof(object, proto) {
|
|||
|
||||
// Special-case for some builtin prototypes in case their `constructor` property has been tampered.
|
||||
const wellKnownPrototypes = new SafeMap();
|
||||
wellKnownPrototypes.set(ObjectPrototype, { name: 'Object', constructor: Object });
|
||||
wellKnownPrototypes.set(ArrayPrototype, { name: 'Array', constructor: Array });
|
||||
wellKnownPrototypes.set(ArrayBufferPrototype, { name: 'ArrayBuffer', constructor: ArrayBuffer });
|
||||
wellKnownPrototypes.set(FunctionPrototype, { name: 'Function', constructor: Function });
|
||||
wellKnownPrototypes.set(MapPrototype, { name: 'Map', constructor: Map });
|
||||
wellKnownPrototypes.set(ObjectPrototype, { name: 'Object', constructor: Object });
|
||||
wellKnownPrototypes.set(SetPrototype, { name: 'Set', constructor: Set });
|
||||
wellKnownPrototypes.set(TypedArrayPrototype, { name: 'TypedArray', constructor: TypedArray });
|
||||
|
||||
function getConstructorName(obj, ctx, recurseTimes, protoProps) {
|
||||
let firstProto;
|
||||
|
@ -825,12 +839,12 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
|
|||
// Filter out the util module, its inspect function is special.
|
||||
maybeCustom !== inspect &&
|
||||
// Also filter out any prototype objects using the circular check.
|
||||
!(value.constructor && value.constructor.prototype === value)) {
|
||||
ObjectGetOwnPropertyDescriptor(value, 'constructor')?.value?.prototype !== value) {
|
||||
// This makes sure the recurseTimes are reported as before while using
|
||||
// a counter internally.
|
||||
const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
|
||||
const isCrossContext =
|
||||
proxy !== undefined || !(context instanceof Object);
|
||||
proxy !== undefined || !FunctionPrototypeSymbolHasInstance(Object, context);
|
||||
const ret = FunctionPrototypeCall(
|
||||
maybeCustom,
|
||||
context,
|
||||
|
|
|
@ -3385,3 +3385,44 @@ assert.strictEqual(
|
|||
);
|
||||
Object.defineProperty(BuiltinPrototype, 'constructor', desc);
|
||||
}
|
||||
{
|
||||
const prototypes = [
|
||||
Array.prototype,
|
||||
ArrayBuffer.prototype,
|
||||
Buffer.prototype,
|
||||
Function.prototype,
|
||||
Map.prototype,
|
||||
Object.prototype,
|
||||
Reflect.getPrototypeOf(Uint8Array.prototype),
|
||||
Set.prototype,
|
||||
Uint8Array.prototype,
|
||||
];
|
||||
const descriptors = new Map();
|
||||
const buffer = Buffer.from('Hello');
|
||||
const o = {
|
||||
arrayBuffer: new ArrayBuffer(), buffer, typedArray: Uint8Array.from(buffer),
|
||||
array: [], func() {}, set: new Set([1]), map: new Map(),
|
||||
};
|
||||
for (const BuiltinPrototype of prototypes) {
|
||||
descriptors.set(BuiltinPrototype, Reflect.getOwnPropertyDescriptor(BuiltinPrototype, 'constructor'));
|
||||
Object.defineProperty(BuiltinPrototype, 'constructor', {
|
||||
get: () => BuiltinPrototype,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
assert.strictEqual(
|
||||
util.inspect(o),
|
||||
'{\n' +
|
||||
' arrayBuffer: ArrayBuffer { [Uint8Contents]: <>, byteLength: 0 },\n' +
|
||||
' buffer: <Buffer 48 65 6c 6c 6f>,\n' +
|
||||
' typedArray: TypedArray(5) [Uint8Array] [ 72, 101, 108, 108, 111 ],\n' +
|
||||
' array: [],\n' +
|
||||
' func: [Function: func],\n' +
|
||||
' set: Set(1) { 1 },\n' +
|
||||
' map: Map(0) {}\n' +
|
||||
'}',
|
||||
);
|
||||
for (const [BuiltinPrototype, desc] of descriptors) {
|
||||
Object.defineProperty(BuiltinPrototype, 'constructor', desc);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue