util: prevent tampering with internals in inspect()
This makes sure user options passed to `util.inspect()` will not override any internal properties (besides `stylize`). PR-URL: https://github.com/nodejs/node/pull/26577 Fixes: https://github.com/nodejs/node/issues/24765 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
This commit is contained in:
parent
32853c0a13
commit
5672ab7668
@ -150,6 +150,16 @@ const meta = [
|
|||||||
'', '', '', '', '', '', '', '\\\\'
|
'', '', '', '', '', '', '', '\\\\'
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function getUserOptions(ctx) {
|
||||||
|
const obj = { stylize: ctx.stylize };
|
||||||
|
for (const key of Object.keys(inspectDefaultOptions)) {
|
||||||
|
obj[key] = ctx[key];
|
||||||
|
}
|
||||||
|
if (ctx.userOptions === undefined)
|
||||||
|
return obj;
|
||||||
|
return { ...obj, ...ctx.userOptions };
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Echos the value of any input. Tries to print the value out
|
* Echos the value of any input. Tries to print the value out
|
||||||
* in the best way possible given the different types.
|
* in the best way possible given the different types.
|
||||||
@ -192,8 +202,16 @@ function inspect(value, opts) {
|
|||||||
ctx.showHidden = opts;
|
ctx.showHidden = opts;
|
||||||
} else if (opts) {
|
} else if (opts) {
|
||||||
const optKeys = Object.keys(opts);
|
const optKeys = Object.keys(opts);
|
||||||
for (var i = 0; i < optKeys.length; i++) {
|
for (const key of optKeys) {
|
||||||
ctx[optKeys[i]] = opts[optKeys[i]];
|
// TODO(BridgeAR): Find a solution what to do about stylize. Either make
|
||||||
|
// this function public or add a new API with a similar or better
|
||||||
|
// functionality.
|
||||||
|
if (hasOwnProperty(inspectDefaultOptions, key) || key === 'stylize') {
|
||||||
|
ctx[key] = opts[key];
|
||||||
|
} else if (ctx.userOptions === undefined) {
|
||||||
|
// This is required to pass through the actual user input.
|
||||||
|
ctx.userOptions = opts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -522,14 +540,10 @@ function formatValue(ctx, value, recurseTimes, typedArray) {
|
|||||||
maybeCustom !== inspect &&
|
maybeCustom !== inspect &&
|
||||||
// Also filter out any prototype objects using the circular check.
|
// Also filter out any prototype objects using the circular check.
|
||||||
!(value.constructor && value.constructor.prototype === value)) {
|
!(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
|
// This makes sure the recurseTimes are reported as before while using
|
||||||
// a counter internally.
|
// a counter internally.
|
||||||
const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
|
const depth = ctx.depth === null ? null : ctx.depth - recurseTimes;
|
||||||
const ret = maybeCustom.call(context, depth, plainCtx);
|
const ret = maybeCustom.call(context, depth, getUserOptions(ctx));
|
||||||
// If the custom inspection method returned `this`, don't go into
|
// If the custom inspection method returned `this`, don't go into
|
||||||
// infinite recursion.
|
// infinite recursion.
|
||||||
if (ret !== context) {
|
if (ret !== context) {
|
||||||
|
@ -791,7 +791,7 @@ util.inspect({ hasOwnProperty: null });
|
|||||||
}) };
|
}) };
|
||||||
});
|
});
|
||||||
|
|
||||||
util.inspect(subject, { customInspectOptions: true });
|
util.inspect(subject);
|
||||||
|
|
||||||
// util.inspect.custom is a shared symbol which can be accessed as
|
// util.inspect.custom is a shared symbol which can be accessed as
|
||||||
// Symbol.for("nodejs.util.inspect.custom").
|
// Symbol.for("nodejs.util.inspect.custom").
|
||||||
@ -803,9 +803,11 @@ util.inspect({ hasOwnProperty: null });
|
|||||||
|
|
||||||
subject[inspect] = (depth, opts) => {
|
subject[inspect] = (depth, opts) => {
|
||||||
assert.strictEqual(opts.customInspectOptions, true);
|
assert.strictEqual(opts.customInspectOptions, true);
|
||||||
|
assert.strictEqual(opts.seen, null);
|
||||||
|
return {};
|
||||||
};
|
};
|
||||||
|
|
||||||
util.inspect(subject, { customInspectOptions: true });
|
util.inspect(subject, { customInspectOptions: true, seen: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -816,6 +818,12 @@ util.inspect({ hasOwnProperty: null });
|
|||||||
`{ a: 123,\n [Symbol(${UIC})]: [Function: [${UIC}]] }`);
|
`{ a: 123,\n [Symbol(${UIC})]: [Function: [${UIC}]] }`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify that it's possible to use the stylize function to manipulate input.
|
||||||
|
assert.strictEqual(
|
||||||
|
util.inspect([1, 2, 3], { stylize() { return 'x'; } }),
|
||||||
|
'[ x, x, x ]'
|
||||||
|
);
|
||||||
|
|
||||||
// Using `util.inspect` with "colors" option should produce as many lines as
|
// Using `util.inspect` with "colors" option should produce as many lines as
|
||||||
// without it.
|
// without it.
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user