Skip to content

Commit 7ba6848

Browse files
committed
util: protect against monkeypatched Object prototype for inspect()
Prevent affects of monkeypatching (for example) Object.keys() when calling util.inspect().
1 parent 679c23f commit 7ba6848

File tree

2 files changed

+35
-23
lines changed

2 files changed

+35
-23
lines changed

lib/internal/util/inspect.js

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ const {
6363
isBigUint64Array
6464
} = require('internal/util/types');
6565

66+
const assert = require('internal/assert');
67+
6668
const ReflectApply = Reflect.apply;
6769

6870
// This function is borrowed from the function with the same name on V8 Extras'
@@ -92,7 +94,7 @@ const dateGetTime = uncurryThis(Date.prototype.getTime);
9294
const hasOwnProperty = uncurryThis(Object.prototype.hasOwnProperty);
9395
let hexSlice;
9496

95-
const inspectDefaultOptions = Object.seal({
97+
const inspectDefaultOptions = primordials.Object.seal({
9698
showHidden: false,
9799
depth: 2,
98100
colors: false,
@@ -185,7 +187,7 @@ function inspect(value, opts) {
185187
if (typeof opts === 'boolean') {
186188
ctx.showHidden = opts;
187189
} else if (opts) {
188-
const optKeys = Object.keys(opts);
190+
const optKeys = primordials.Object.keys(opts);
189191
for (var i = 0; i < optKeys.length; i++) {
190192
ctx[optKeys[i]] = opts[optKeys[i]];
191193
}
@@ -197,20 +199,20 @@ function inspect(value, opts) {
197199
}
198200
inspect.custom = customInspectSymbol;
199201

200-
Object.defineProperty(inspect, 'defaultOptions', {
202+
primordials.Object.defineProperty(inspect, 'defaultOptions', {
201203
get() {
202204
return inspectDefaultOptions;
203205
},
204206
set(options) {
205207
if (options === null || typeof options !== 'object') {
206208
throw new ERR_INVALID_ARG_TYPE('options', 'Object', options);
207209
}
208-
return Object.assign(inspectDefaultOptions, options);
210+
return primordials.Object.assign(inspectDefaultOptions, options);
209211
}
210212
});
211213

212214
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
213-
inspect.colors = Object.assign(Object.create(null), {
215+
inspect.colors = primordials.Object.assign(primordials.Object.create(null), {
214216
bold: [1, 22],
215217
italic: [3, 23],
216218
underline: [4, 24],
@@ -227,7 +229,7 @@ inspect.colors = Object.assign(Object.create(null), {
227229
});
228230

229231
// Don't use 'blue' not visible on cmd.exe
230-
inspect.styles = Object.assign(Object.create(null), {
232+
inspect.styles = primordials.Object.assign(Object.create(null), {
231233
special: 'cyan',
232234
number: 'yellow',
233235
bigint: 'yellow',
@@ -327,14 +329,15 @@ function getEmptyFormatArray() {
327329
function getConstructorName(obj, ctx) {
328330
let firstProto;
329331
while (obj) {
330-
const descriptor = Object.getOwnPropertyDescriptor(obj, 'constructor');
332+
const descriptor =
333+
primordials.Object.getOwnPropertyDescriptor(obj, 'constructor');
331334
if (descriptor !== undefined &&
332335
typeof descriptor.value === 'function' &&
333336
descriptor.value.name !== '') {
334337
return descriptor.value.name;
335338
}
336339

337-
obj = Object.getPrototypeOf(obj);
340+
obj = primordials.Object.getPrototypeOf(obj);
338341
if (firstProto === undefined) {
339342
firstProto = obj;
340343
}
@@ -369,9 +372,9 @@ const getBoxedValue = formatPrimitive.bind(null, stylizeNoColor);
369372
// Look up the keys of the object.
370373
function getKeys(value, showHidden) {
371374
let keys;
372-
const symbols = Object.getOwnPropertySymbols(value);
375+
const symbols = primordials.Object.getOwnPropertySymbols(value);
373376
if (showHidden) {
374-
keys = Object.getOwnPropertyNames(value);
377+
keys = primordials.Object.getOwnPropertyNames(value);
375378
if (symbols.length !== 0)
376379
keys.push(...symbols);
377380
} else {
@@ -381,15 +384,11 @@ function getKeys(value, showHidden) {
381384
// TODO(devsnek): track https://github.com/tc39/ecma262/issues/1209
382385
// and modify this logic as needed.
383386
try {
384-
keys = Object.keys(value);
387+
keys = primordials.Object.keys(value);
385388
} catch (err) {
386-
if (isNativeError(err) &&
387-
err.name === 'ReferenceError' &&
388-
isModuleNamespaceObject(value)) {
389-
keys = Object.getOwnPropertyNames(value);
390-
} else {
391-
throw err;
392-
}
389+
assert(isNativeError(err) && err.name === 'ReferenceError' &&
390+
isModuleNamespaceObject(value));
391+
keys = primordials.Object.getOwnPropertyNames(value);
393392
}
394393
if (symbols.length !== 0) {
395394
keys.push(...symbols.filter((key) => propertyIsEnumerable(value, key)));
@@ -454,8 +453,8 @@ function clazzWithNullPrototype(clazz, name) {
454453
return '';
455454
}
456455
}
457-
Object.defineProperty(NullPrototype.prototype.constructor, 'name',
458-
{ value: `[${name}: null prototype]` });
456+
primordials.Object.defineProperty(NullPrototype.prototype.constructor, 'name',
457+
{ value: `[${name}: null prototype]` });
459458
lazyNullPrototypeCache.set(clazz, NullPrototype);
460459
return NullPrototype;
461460
}
@@ -477,7 +476,9 @@ function noPrototypeIterator(ctx, value, recurseTimes) {
477476
newVal = new clazz(value);
478477
}
479478
if (newVal !== undefined) {
480-
Object.defineProperties(newVal, Object.getOwnPropertyDescriptors(value));
479+
primordials.Object.defineProperties(
480+
newVal, primordials.Object.getOwnPropertyDescriptors(value)
481+
);
481482
return formatRaw(ctx, newVal, recurseTimes);
482483
}
483484
}
@@ -894,7 +895,7 @@ function formatNamespaceObject(ctx, value, recurseTimes, keys) {
894895

895896
// The array is sparse and/or has extra keys
896897
function formatSpecialArray(ctx, value, recurseTimes, maxLength, output, i) {
897-
const keys = Object.keys(value);
898+
const keys = primordials.Object.keys(value);
898899
let index = i;
899900
for (; i < keys.length && output.length < maxLength; i++) {
900901
const key = keys[i];
@@ -1125,7 +1126,7 @@ function formatPromise(ctx, value, recurseTimes) {
11251126
function formatProperty(ctx, value, recurseTimes, key, type) {
11261127
let name, str;
11271128
let extra = ' ';
1128-
const desc = Object.getOwnPropertyDescriptor(value, key) ||
1129+
const desc = primordials.Object.getOwnPropertyDescriptor(value, key) ||
11291130
{ value: value[key], enumerable: true };
11301131
if (desc.value !== undefined) {
11311132
const diff = (type !== kObjectType || ctx.compact === false) ? 2 : 3;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
'use strict';
2+
3+
// Monkeypatch Object.keys() so that it throws an unexpected error. This tests
4+
// that `util.inspect()` is unaffected by monkey-patching `Object`.
5+
6+
require('../common');
7+
const assert = require('assert');
8+
const util = require('util');
9+
10+
Object.keys = () => { throw new Error('fhqwhgads'); };
11+
assert.strictEqual(util.inspect({}), '{}');

0 commit comments

Comments
 (0)