diff --git a/index.js b/index.js index bdc5177..f384b3c 100644 --- a/index.js +++ b/index.js @@ -1,23 +1,30 @@ var vm = require('vm') +function clearContext () { + // eslint-disable-next-line no-global-assign + Function = undefined + const keys = Object.getOwnPropertyNames(this).concat(['constructor']) + keys.forEach((key) => { + const item = this[key] + if (!item) return + if (typeof Object.getPrototypeOf(item).constructor === 'function') { + Object.getPrototypeOf(item).constructor = undefined + } + if (typeof item.constructor === 'function') { + this[key].constructor = undefined + } + }) +} + module.exports = function safeEval (code, context, opts) { var sandbox = {} var resultKey = 'SAFE_EVAL_' + Math.floor(Math.random() * 1000000) sandbox[resultKey] = {} - var clearContext = ` - (function() { - Function = undefined; - const keys = Object.getOwnPropertyNames(this).concat(['constructor']); - keys.forEach((key) => { - const item = this[key]; - if (!item || typeof item.constructor !== 'function') return; - this[key].constructor = undefined; - }); - })(); - ` - code = clearContext + resultKey + '=' + code + var clearContextCall = `(${clearContext.toString()})();` + code = `${clearContextCall}${resultKey}=${code}` if (context) { Object.keys(context).forEach(function (key) { + if (context[key] === Function) return sandbox[key] = context[key] }) } diff --git a/package.json b/package.json index 55327b4..aaacf93 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "safe-eval", - "version": "0.4.1", + "version": "0.4.2", "description": "Safer version of eval()", "main": "index.js", "scripts": { diff --git a/test/test.js b/test/test.js index c79027b..43670da 100644 --- a/test/test.js +++ b/test/test.js @@ -44,7 +44,33 @@ describe('safe-eval', function () { }) }) - it('should not have access to Node.js objects (CWE-265)', function () { + it('should not have access to Node.js objects using context (CWE-265)', function () { + var code = 'test(\'return process\')()' + assert.throws(function () { + safeEval(code, { + // eslint-disable-next-line no-new-func + test: new Function().constructor + }) + }) + }) + + it('should not have access to Node.js objects using Object.getPrototypeOf (CWE-265)', function () { + var code = `Object.getPrototypeOf(Object).constructor('return process')();` + assert.throws(function () { + safeEval(code) + }) + }) + + it('should not have access to Node.js objects using Object.getPrototypeOf with context (CWE-265)', function () { + var code = `Object.getPrototypeOf(obj).constructor.constructor("return process")();` + assert.throws(function () { + safeEval(code, { + obj: Object + }) + }) + }) + + it('should not have access to Node.js objects using this.constructor (CWE-265)', function () { var code = 'this.constructor.constructor(\'return process\')()' assert.throws(function () { safeEval(code)