Skip to content

Commit

Permalink
util: prevent leaking internal properties
Browse files Browse the repository at this point in the history
This prevents leaking of the internal `inspect()` properties when
using a custom inspect function.

It also aligns the indentation to the way it was in v8.0.0 since
that changed unintentionally. All strings returned by the custom
inspect function will now be indented appropriately to the current
depth.

PR-URL: nodejs#24971
Refs: nodejs#24765
Reviewed-By: Gus Caplan <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Rich Trott <[email protected]>
  • Loading branch information
BridgeAR committed Feb 28, 2019
1 parent be78266 commit 7b67469
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 6 deletions.
4 changes: 4 additions & 0 deletions doc/api/util.md
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,10 @@ stream.write('With ES6');
<!-- YAML
added: v0.3.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/24971
description: Internal properties no longer appear in the context argument
of a custom inspection function.
- version: v11.7.0
pr-url: https://github.com/nodejs/node/pull/25006
description: ArrayBuffers now also show their binary contents.
Expand Down
8 changes: 6 additions & 2 deletions lib/internal/util/inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -516,18 +516,22 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
maybeCustom !== inspect &&
// Also filter out any prototype objects using the circular check.
!(value.constructor && value.constructor.prototype === value)) {
// Remove some internal properties from the options before passing it
// through to the user function. This also prevents option manipulation.
// eslint-disable-next-line no-unused-vars
const { budget, seen, indentationLvl, ...plainCtx } = ctx;
// This makes sure the recurseTimes are reported as before while using
// a counter internally.
const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
const ret = maybeCustom.call(value, depth, ctx);
const ret = maybeCustom.call(value, depth, plainCtx);

// If the custom inspection method returned `this`, don't go into
// infinite recursion.
if (ret !== value) {
if (typeof ret !== 'string') {
return formatValue(ctx, ret, recurseTimes);
}
return ret;
return ret.replace(/\n/g, `\n${' '.repeat(ctx.indentationLvl)}`);
}
}
}
Expand Down
20 changes: 16 additions & 4 deletions test/parallel/test-util-inspect.js
Original file line number Diff line number Diff line change
Expand Up @@ -775,9 +775,21 @@ util.inspect({ hasOwnProperty: null });

assert.strictEqual(util.inspect(subject), "{ foo: 'bar' }");

subject[util.inspect.custom] = (depth, opts) => {
assert.strictEqual(opts.customInspectOptions, true);
};
subject[util.inspect.custom] = common.mustCall((depth, opts) => {
const clone = { ...opts };
// This might change at some point but for now we keep the stylize function.
// The function should either be documented or an alternative should be
// implemented.
assert.strictEqual(typeof opts.stylize, 'function');
assert.strictEqual(opts.seen, undefined);
assert.strictEqual(opts.budget, undefined);
assert.strictEqual(opts.indentationLvl, undefined);
assert.strictEqual(opts.showHidden, false);
opts.showHidden = true;
return { [util.inspect.custom]: common.mustCall((depth, opts2) => {
assert.deepStrictEqual(clone, opts2);
}) };
});

util.inspect(subject, { customInspectOptions: true });

Expand Down Expand Up @@ -1593,7 +1605,7 @@ util.inspect(process);
);
const longList = util.inspect(list, { depth: Infinity });
const match = longList.match(/next/g);
assert(match.length > 1000 && match.length < 10000);
assert(match.length > 500 && match.length < 10000);
assert(longList.includes('[Object: Inspection interrupted ' +
'prematurely. Maximum call stack size exceeded.]'));
}
Expand Down

0 comments on commit 7b67469

Please sign in to comment.