Comparing version 3.3.1 to 3.4.0
@@ -0,4 +1,8 @@ | ||
v3.4.0 (2017-03-28) | ||
------------------- | ||
[new] Added experimental VM.protect method | ||
v3.3.1 (2017-03-27) | ||
------------------- | ||
[new] Added support for freezing objects | ||
[new] Added VM.freeze method | ||
@@ -5,0 +9,0 @@ v3.2.0 (2017-02-10) |
@@ -34,8 +34,8 @@ const fs = require('fs'); | ||
get: (target, key) => _freeze(object[key]), | ||
set: (target, key) => { throw new Error('Object is read-only.') }, | ||
setPrototypeOf: (target, key) => { throw new Error('Object is read-only.') }, | ||
defineProperty: (target, key) => { throw new Error('Object is read-only.') }, | ||
deleteProperty: (target, key) => { throw new Error('Object is read-only.') }, | ||
set: (target, key) => { throw new VMError('Object is read-only.') }, | ||
setPrototypeOf: (target, key) => { throw new VMError('Object is read-only.') }, | ||
defineProperty: (target, key) => { throw new VMError('Object is read-only.') }, | ||
deleteProperty: (target, key) => { throw new VMError('Object is read-only.') }, | ||
isExtensible: (target, key) => false, | ||
preventExtensions: (target) => { throw new Error('Object is read-only.') } | ||
preventExtensions: (target) => { throw new VMError('Object is read-only.') } | ||
}); | ||
@@ -47,2 +47,21 @@ } | ||
const _protect = function protect(object) { | ||
if (typeof object === 'object' || typeof object === 'function') { | ||
return new Proxy(object, { | ||
get: (target, key) => _protect(object[key]), | ||
set: (target, key, value) => { | ||
if (typeof value === 'function') throw new VMError('Assigning a function to protected object is prohibited.'); | ||
object[key] = value | ||
}, | ||
setPrototypeOf: (target, key) => { throw new VMError('Changing prototype on protected object is prohibited.') }, | ||
defineProperty: (target, key) => { throw new VMError('Defining property on protected object is prohibited.') }, | ||
deleteProperty: (target, key) => delete object[key], | ||
isExtensible: (target, key) => false, | ||
preventExtensions: (target) => { throw new VMError('Method is prohibited on protected object.') } | ||
}); | ||
} | ||
return object; | ||
} | ||
/** | ||
@@ -107,7 +126,7 @@ * Class Script | ||
/** | ||
* Makes the value read only. | ||
* Makes the object read only. | ||
* | ||
* @static | ||
* @param {*} value Value to freeze. | ||
* @return {*} Frozen value. | ||
* @param {*} object Object to freeze. | ||
* @return {*} Frozen object. | ||
*/ | ||
@@ -120,2 +139,14 @@ | ||
/** | ||
* Protects the object. | ||
* | ||
* @static | ||
* @param {*} object Object to protect. | ||
* @return {*} Protected object. | ||
*/ | ||
static protect(object) { | ||
return _protect(object); | ||
} | ||
/** | ||
* Create VM instance. | ||
@@ -220,7 +251,7 @@ * | ||
/** | ||
* Makes the value read only. | ||
* Makes the object read only. | ||
* | ||
* @static | ||
* @param {*} value Value to freeze. | ||
* @return {*} Frozen value. | ||
* @param {*} object Object to freeze. | ||
* @return {*} Frozen object. | ||
*/ | ||
@@ -233,2 +264,14 @@ | ||
/** | ||
* Protects the object. | ||
* | ||
* @static | ||
* @param {*} object Object to protect. | ||
* @return {*} Protected object. | ||
*/ | ||
static protect(object) { | ||
return _protect(object); | ||
} | ||
/** | ||
* Create NodeVM instance. | ||
@@ -235,0 +278,0 @@ * |
@@ -16,3 +16,3 @@ { | ||
], | ||
"version": "3.3.1", | ||
"version": "3.4.0", | ||
"main": "index.js", | ||
@@ -19,0 +19,0 @@ "repository": { |
@@ -50,8 +50,7 @@ const assert = require("assert"); | ||
before(done => { | ||
before(() => { | ||
vm = new VM({sandbox}); | ||
done(); | ||
}) | ||
it('common', done => { | ||
it('common', () => { | ||
assert.ok(vm.run(`global.__proto__ === Object.prototype`)); | ||
@@ -103,7 +102,5 @@ assert.ok(vm.run(`global.__proto__.constructor === Object`)); | ||
assert.strictEqual(o.a === o.b, true); | ||
done(); | ||
}) | ||
it('class', done => { | ||
it('class', () => { | ||
assert.strictEqual(vm.run("new test.klass()").isVMProxy, undefined); | ||
@@ -114,40 +111,28 @@ assert.strictEqual(vm.run("new test.klass()").greet('friend'), 'hello friend'); | ||
//vm.run("class LocalClass extends test.klass {}"); | ||
done(); | ||
}) | ||
it('string', done => { | ||
it('string', () => { | ||
assert.strictEqual(vm.run("(test.string).constructor === String"), true); | ||
assert.strictEqual(vm.run("typeof(test.stringO) === 'string' && test.string.valueOf instanceof Object"), true); | ||
done(); | ||
}) | ||
it('number', done => { | ||
it('number', () => { | ||
assert.strictEqual(vm.run("typeof(test.numberO) === 'number' && test.number.valueOf instanceof Object"), true); | ||
done(); | ||
}) | ||
it('boolean', done => { | ||
it('boolean', () => { | ||
assert.strictEqual(vm.run("typeof(test.booleanO) === 'boolean' && test.boolean.valueOf instanceof Object"), true); | ||
done(); | ||
}) | ||
it('date', done => { | ||
it('date', () => { | ||
assert.strictEqual(vm.run("test.date instanceof Date"), true); | ||
assert.strictEqual(vm.run("test.date") instanceof Date, true); | ||
assert.strictEqual(vm.run("test.date"), sandbox.test.date); | ||
done(); | ||
}) | ||
it('regexp', done => { | ||
it('regexp', () => { | ||
assert.strictEqual(vm.run("test.regexp instanceof RegExp"), true); | ||
done(); | ||
}) | ||
it('buffer', done => { | ||
it('buffer', () => { | ||
assert.strictEqual(vm.run("test.buffer.inspect()"), '<Buffer 00 01>', '#1'); | ||
@@ -193,15 +178,11 @@ assert.strictEqual(vm.run("test.buffer instanceof Buffer"), true, '#2'); | ||
assert.ok(d.constructor.constructor === Function); | ||
done(); | ||
}) | ||
it('function', done => { | ||
it('function', () => { | ||
assert.strictEqual(vm.run("test.function instanceof Function"), true, '#1'); | ||
assert.strictEqual(vm.run("test.function() instanceof Function"), true, '#2'); | ||
assert.strictEqual(vm.run("test.function()() instanceof Object"), true, '#3'); | ||
done(); | ||
}) | ||
it('object', done => { | ||
it('object', () => { | ||
assert.strictEqual(vm.run("test.object instanceof Object && test.object.x === 1"), true, '#1'); | ||
@@ -214,19 +195,13 @@ assert.strictEqual(vm.run("test.object.y instanceof Function"), true, '#2'); | ||
assert.strictEqual(vm.run("Object.getOwnPropertyDescriptor(test.object, 'y').hasOwnProperty.constructor('return (function(){return this})().isVM')()"), true, '#7'); | ||
done(); | ||
}) | ||
it('null', done => { | ||
it('null', () => { | ||
assert.strictEqual(vm.run("test.nil === null"), true); | ||
done(); | ||
}) | ||
it('undefined', done => { | ||
it('undefined', () => { | ||
assert.strictEqual(vm.run("test.undef === undefined"), true); | ||
done(); | ||
}) | ||
it('symbol', done => { | ||
it('symbol', () => { | ||
assert.strictEqual(vm.run("Symbol.for('foo') === test.symbol2"), true); | ||
@@ -239,16 +214,10 @@ assert.strictEqual(vm.run("test.symbol1.constructor.constructor === Function"), true); | ||
assert.strictEqual(vm.run("Symbol.keyFor(test.symbol2)"), 'foo'); | ||
done(); | ||
}) | ||
it('error', done => { | ||
it('error', () => { | ||
assert.strictEqual(vm.run("Object.getOwnPropertyDescriptor(test.error, 'stack').get.constructor === Function;"), true); | ||
done(); | ||
}) | ||
after(done => { | ||
after(() => { | ||
vm = null; | ||
done(); | ||
}) | ||
@@ -260,3 +229,3 @@ }) | ||
before(done => { | ||
before(() => { | ||
let sandbox = { | ||
@@ -282,13 +251,9 @@ round(number) { return Math.round(number); }, | ||
}) | ||
done(); | ||
}) | ||
it('globals', done => { | ||
it('globals', () => { | ||
assert.equal(vm.run("round(1.5)"), 2); | ||
done(); | ||
}) | ||
it('errors', done => { | ||
it('errors', () => { | ||
assert.throws(() => vm.run("notdefined"), /notdefined is not defined/); | ||
@@ -300,7 +265,5 @@ assert.throws(() => vm.run("Object.setPrototypeOf(sub, {})"), err => { | ||
}) | ||
done(); | ||
}) | ||
it('timeout', done => { | ||
it('timeout', () => { | ||
assert.throws(() => new VM({ | ||
@@ -310,15 +273,11 @@ timeout: 10 | ||
assert.throws(() => vm.run("sub.getter"), /Script execution timed out\./); | ||
done(); | ||
}) | ||
it('timers', done => { | ||
it('timers', () => { | ||
assert.equal(vm.run("global.setTimeout"), void 0); | ||
assert.equal(vm.run("global.setInterval"), void 0); | ||
assert.equal(vm.run("global.setImmediate"), void 0); | ||
done(); | ||
}) | ||
it('various attacks #1', done => { | ||
it('various attacks #1', () => { | ||
let vm2 = new VM({sandbox: {log: console.log, boom: function() { throw new Error(); }}}); | ||
@@ -375,7 +334,5 @@ | ||
`)(new Buffer(1)), '#7'); | ||
done(); | ||
}) | ||
it('various attacks #2', done => { | ||
it('various attacks #2', () => { | ||
let vm2 = new VM({ | ||
@@ -450,10 +407,6 @@ sandbox: { | ||
`)); | ||
done(); | ||
}) | ||
after(done => { | ||
after(() => { | ||
vm = null; | ||
done(); | ||
}) | ||
@@ -465,53 +418,37 @@ }) | ||
before(done => { | ||
before(() => { | ||
vm = new NodeVM; | ||
done(); | ||
}) | ||
it('globals', done => { | ||
it('globals', () => { | ||
let ex; | ||
ex = vm.run("module.exports = global"); | ||
assert.equal(ex.isVM, true); | ||
done(); | ||
}) | ||
it('errors', done => { | ||
it('errors', () => { | ||
assert.throws(() => vm.run("notdefined"), /notdefined is not defined/); | ||
done(); | ||
}) | ||
it('prevent global access', done => { | ||
it('prevent global access', () => { | ||
assert.throws(() => vm.run("process.exit()"), /(undefined is not a function|process\.exit is not a function)/); | ||
done(); | ||
}) | ||
it('arguments attack', done => { | ||
it('arguments attack', () => { | ||
assert.strictEqual(vm.run("module.exports = (function() { return arguments.callee.caller.constructor === Function; })()"), true); | ||
assert.throws(() => vm.run("module.exports = (function() { return arguments.callee.caller.caller.toString(); })()"), /Cannot read property 'toString' of null/); | ||
done(); | ||
}) | ||
it('global attack', done => { | ||
it('global attack', () => { | ||
assert.equal(vm.run("module.exports = console.log.constructor('return (function(){return this})().isVM')()"), true); | ||
done(); | ||
}) | ||
it.skip('timeout (not supported by Node\'s VM)', done => { | ||
it.skip('timeout (not supported by Node\'s VM)', () => { | ||
assert.throws(() => new NodeVM({ | ||
timeout: 10 | ||
}).run("while (true) {}"), /Script execution timed out\./); | ||
done(); | ||
}) | ||
after(done => { | ||
after(() => { | ||
vm = null; | ||
done(); | ||
}) | ||
@@ -521,3 +458,3 @@ }) | ||
describe('modules', () => { | ||
it('require json', done => { | ||
it('require json', () => { | ||
let vm = new NodeVM({ | ||
@@ -530,7 +467,5 @@ require: { | ||
assert.equal(vm.run(`module.exports = require('${__dirname}/data/json.json')`).working, true); | ||
done(); | ||
}) | ||
it.skip('run coffee-script', done => { | ||
it.skip('run coffee-script', () => { | ||
let vm = new NodeVM({ | ||
@@ -544,15 +479,11 @@ require: { | ||
assert.equal(vm.run("module.exports = working: true").working, true); | ||
done(); | ||
}) | ||
it('disabled require', done => { | ||
it('disabled require', () => { | ||
let vm = new NodeVM; | ||
assert.throws(() => vm.run("require('fs')"), /Access denied to require 'fs'/); | ||
done(); | ||
}) | ||
it('disable setters on builtin modules', done => { | ||
it('disable setters on builtin modules', () => { | ||
let vm = new NodeVM({ | ||
@@ -583,7 +514,5 @@ require: { | ||
}) | ||
done(); | ||
}) | ||
it('enabled require for certain modules', done => { | ||
it('enabled require for certain modules', () => { | ||
let vm = new NodeVM({ | ||
@@ -596,7 +525,5 @@ require: { | ||
assert.doesNotThrow(() => vm.run("require('fs')")); | ||
done(); | ||
}) | ||
it('require relative', done => { | ||
it('require relative', () => { | ||
let vm = new NodeVM({ | ||
@@ -609,7 +536,5 @@ require: { | ||
vm.run("require('foobar')", __filename); | ||
done(); | ||
}) | ||
it('arguments attack', done => { | ||
it('arguments attack', () => { | ||
let vm = new NodeVM; | ||
@@ -622,4 +547,2 @@ | ||
assert.throws(() => vm.run("module.exports = function fce(msg) { return fce.caller.toString(); }")(), /Cannot read property 'toString' of null/); | ||
done(); | ||
}) | ||
@@ -641,3 +564,3 @@ | ||
it('path attack', done => { | ||
it('path attack', () => { | ||
let vm = new NodeVM({ | ||
@@ -651,7 +574,5 @@ require: { | ||
assert.throws(() => vm.run("var test = require('../package.json')", __filename), /Module '\.\.\/package.json' is not allowed to be required\. The path is outside the border!/); | ||
done(); | ||
}) | ||
it('process events', done => { | ||
it('process events', () => { | ||
let vm = new NodeVM({ | ||
@@ -670,4 +591,2 @@ sandbox: { | ||
assert.strictEqual(sandbox.VM2_COUNTER, 1); | ||
done(); | ||
}) | ||
@@ -690,3 +609,3 @@ | ||
it('mock', done => { | ||
it('mock', () => { | ||
let vm = new NodeVM({ | ||
@@ -704,4 +623,2 @@ require: { | ||
assert.strictEqual(vm.run("module.exports = require('fs').readFileSync()"), 'Nice try!'); | ||
done(); | ||
}) | ||
@@ -711,3 +628,3 @@ }) | ||
describe('nesting', () => { | ||
it('NodeVM', done => { | ||
it('NodeVM', () => { | ||
let vm = new NodeVM({ | ||
@@ -725,4 +642,2 @@ nesting: true | ||
assert.strictEqual(nestedObject.constructor.constructor === Function, true); | ||
done(); | ||
}) | ||
@@ -732,3 +647,3 @@ }) | ||
describe('wrappers', () => { | ||
it('none', done => { | ||
it('none', () => { | ||
let vm = new NodeVM({ | ||
@@ -739,4 +654,2 @@ wrapper: 'none' | ||
assert.strictEqual(vm.run('return 2 + 2'), 4) | ||
done() | ||
}) | ||
@@ -746,3 +659,3 @@ }) | ||
describe('precompiled scripts', () => { | ||
it('VM', done => { | ||
it('VM', () => { | ||
let vm = new VM(); | ||
@@ -754,7 +667,5 @@ let script = new VMScript("Math.random()"); | ||
assert.ok( val1 != val2); | ||
done() | ||
}) | ||
it('NodeVM', done => { | ||
it('NodeVM', () => { | ||
let vm = new NodeVM(); | ||
@@ -766,9 +677,7 @@ let script = new VMScript("module.exports = Math.random()"); | ||
assert.ok( val1 != val2); | ||
done() | ||
}) | ||
}) | ||
describe('read-only contextified items', () => { | ||
it('without read-only', done => { | ||
describe('freeze, protect', () => { | ||
it('without freeze', () => { | ||
let x = { | ||
@@ -790,7 +699,5 @@ a: () => 'a', | ||
assert.strictEqual(x.c.d(), '---'); | ||
done() | ||
}) | ||
it('with read-only', done => { | ||
it('with freeze', () => { | ||
let x = VM.freeze({ | ||
@@ -819,5 +726,22 @@ a: () => 'a', | ||
}, /Object is read-only\./); | ||
}) | ||
done() | ||
it('without protect', () => { | ||
let vm = new VM(), obj = {}; | ||
vm.run('(i) => { i.text = "test" }')(obj); | ||
vm.run('(i) => { i.func = () => {} }')(obj); | ||
vm.run('(i) => { delete i.func }')(obj); | ||
}) | ||
it('with protect', () => { | ||
let vm = new VM(), obj = {}; | ||
vm.run('(i) => { i.text = "test" }')(obj); | ||
assert.throws(() => { | ||
vm.run('(i) => { i.func = () => {} }')(VM.protect(obj)); | ||
}, /Assigning a function to protected object is prohibited\./); | ||
vm.run('(i) => { delete i.func }')(VM.protect(obj)); | ||
}) | ||
}) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
73964
1847