Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom inspector for objects, fix #314 #315

Closed
wants to merge 17 commits into from
6 changes: 6 additions & 0 deletions lib/contextify.js
Expand Up @@ -199,6 +199,11 @@ Decontextify.instance = (instance, klass, deepTraps, flags, toStringTag) => {
if (key === '__lookupGetter__') return host.Object.prototype.__lookupGetter__;
if (key === '__lookupSetter__') return host.Object.prototype.__lookupSetter__;
if (key === host.Symbol.toStringTag && toStringTag) return toStringTag;
if (key === host.inspect.custom) {
return (depth, options) => {
return host.inspect(instance, options.showHidden, depth, options.colors);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is unsafe. The instance object is from the sandbox and is passed into the host function inspect. This can be used to escape the sandbox. Here custom implementations inside the sandbox are required.

Copy link
Contributor Author

@shigma shigma Aug 10, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry. Can you please provide an example to escape?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you go:

const vm = new VM();
const code = '(' + function() {
    const customInspect = Symbol.for('nodejs.util.inspect.custom');
    Date.prototype[customInspect] = (depth, options) => {throw options.constructor.constructor('return process')()};
    return new Date();
}+')();';
const bad_object = vm.run(code);
console.log("Still good");
console.log(bad_object);
console.log("Logging is bad");

};
}

try {
return Decontextify.value(instance[key], null, deepTraps, flags);
Expand Down Expand Up @@ -979,6 +984,7 @@ BufferOverride.inspect = function inspect(recurseTimes, ctx) {
const LocalBuffer = global.Buffer = Contextify.readonly(host.Buffer, BufferMock);
Contextify.connect(host.Buffer.prototype.inspect, BufferOverride.inspect);

Proxy[host.inspect.custom] = () => '[Function: Proxy]';

const exportsMap = host.Object.create(null);
exportsMap.Contextify = Contextify;
Expand Down
2 changes: 2 additions & 0 deletions lib/main.js
Expand Up @@ -23,6 +23,7 @@
const fs = require('fs');
const vm = require('vm');
const pa = require('path');
const {inspect} = require('util');
const {EventEmitter} = require('events');
const {INSPECT_MAX_BYTES} = require('buffer');
const helpers = require('./helpers.js');
Expand Down Expand Up @@ -1286,6 +1287,7 @@ const HOST = {
Set,
WeakSet,
Promise,
inspect,
Symbol,
INSPECT_MAX_BYTES,
VM,
Expand Down
57 changes: 57 additions & 0 deletions test/vm.js
Expand Up @@ -280,6 +280,63 @@ describe('contextify', () => {
});
});

describe('inspect', () => {
let vm;

before(() => {
const sandbox = { inspect };
vm = new VM({ sandbox });
});

it('boxed primitives', () => {
assert.equal(inspect(vm.run('new Number(1)')), inspect(new Number(1)));
assert.equal(vm.run('inspect(new Number(1))'), inspect(new Number(1)));
assert.equal(inspect(vm.run('new String(1)')), inspect(new String(1)));
assert.equal(vm.run('inspect(new String(1))'), inspect(new String(1)));
assert.equal(inspect(vm.run('new Boolean(1)')), inspect(new Boolean(1)));
assert.equal(vm.run('inspect(new Boolean(1))'), inspect(new Boolean(1)));
});

it('other built-in objects', () => {
assert.equal(inspect(vm.run('Object')), inspect(Object));
assert.equal(vm.run('inspect(Object)'), inspect(Object));
assert.equal(inspect(vm.run('Function')), inspect(Function));
assert.equal(vm.run('inspect(Function)'), inspect(Function));
assert.equal(inspect(vm.run('Symbol')), inspect(Symbol));
assert.equal(vm.run('inspect(Symbol)'), inspect(Symbol));
assert.equal(inspect(vm.run('Reflect')), inspect(Reflect));
assert.equal(vm.run('inspect(Reflect)'), inspect(Reflect));
assert.equal(inspect(vm.run('Proxy')), inspect(Proxy));
assert.equal(vm.run('inspect(Proxy)'), inspect(Proxy));
assert.equal(inspect(vm.run('JSON')), inspect(JSON));
assert.equal(vm.run('inspect(JSON)'), inspect(JSON));
});

it('built-in instances', () => {
assert.equal(inspect(vm.run('new Date')).slice(0, -3), inspect(new Date).slice(0, -3));
assert.equal(vm.run('inspect(new Date)').slice(0, -3), inspect(new Date).slice(0, -3));
assert.equal(inspect(vm.run('new RegExp("^", "g")')), inspect(new RegExp('^', 'g')));
assert.equal(vm.run('inspect(new RegExp("^", "g"))'), inspect(new RegExp('^', 'g')));
assert.equal(inspect(vm.run('new Array(50)')), inspect(new Array(50)));
assert.equal(vm.run('inspect(new Array(50))'), inspect(new Array(50)));
assert.equal(inspect(vm.run('new Set([1])')), inspect(new Set([1])));
assert.equal(vm.run('inspect(new Set([1]))'), inspect(new Set([1])));
assert.equal(inspect(vm.run('new Map([[1, 2]])')), inspect(new Map([[1, 2]])));
assert.equal(vm.run('inspect(new Map([[1, 2]]))'), inspect(new Map([[1, 2]])));
assert.equal(inspect(vm.run('Promise.resolve(1)')), inspect(Promise.resolve(1)));
assert.equal(vm.run('inspect(Promise.resolve(1))'), inspect(Promise.resolve(1)));
});

it('other objects', () => {
const AsyncFunction = Object.getPrototypeOf(async () => {}).constructor;
const GeneratorFunction = Object.getPrototypeOf(function* f() {}).constructor;
assert.equal(inspect(vm.run('Object.getPrototypeOf(async () => {}).constructor')), inspect(AsyncFunction));
assert.equal(vm.run('inspect(Object.getPrototypeOf(async () => {}).constructor)'), inspect(AsyncFunction));
assert.equal(inspect(vm.run('Object.getPrototypeOf(function* f() {}).constructor')), inspect(GeneratorFunction));
assert.equal(vm.run('inspect(Object.getPrototypeOf(function* f() {}).constructor)'), inspect(GeneratorFunction));
});
});

describe('VM', () => {
let vm;

Expand Down