Skip to content
This repository was archived by the owner on Dec 28, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,30 @@ var hoist = require('hoister')
var InfiniteChecker = require('./lib/infinite-checker')
var Primitives = require('./lib/primitives')

var self = this

module.exports = safeEval
module.exports.eval = safeEval
module.exports.compile = compile
module.exports.FunctionFactory = FunctionFactory
module.exports.Function = FunctionFactory()

var maxIterations = 1000000
var MAX_ITERATIONS_DEFAULT = 1000000

// 'eval' with a controlled environment
function safeEval(src, parentContext){
function safeEval(src, parentContext, _options={}){
const options = Object.assign({
maxIterations: _options.maxIteration ? _options.maxIteration : MAX_ITERATIONS_DEFAULT,
timeout: _options.timeout ? _options.timeout : 0
}, _options)
var tree = prepareAst(src)
var context = Object.create(parentContext || {})
return finalValue(evaluateAst(tree, context))
return finalValue(evaluateAst(tree, context, options))
}

function compile(src) {
var tree = prepareAst(src)
return tree
}

// create a 'Function' constructor for a controlled environment
Expand All @@ -42,7 +54,9 @@ function prepareAst(src){
}

// evaluate an AST in the given context
function evaluateAst(tree, context){
function evaluateAst(tree, context, options){
var startTime = new Date().getTime()
var isTimeout = options.timeout ? options.timeout > 0 : false

var safeFunction = FunctionFactory(context)
var primitives = Primitives(context)
Expand All @@ -68,6 +82,11 @@ function evaluateAst(tree, context){

// recursively evalutate the node of an AST
function walk(node, traceNode){
if (isTimeout){
if (new Date().getTime() - startTime > options.timeout){
throw new Error('Execution time')
}
}
try {
if (!node) return
switch (node.type) {
Expand Down Expand Up @@ -171,7 +190,7 @@ function evaluateAst(tree, context){
}

case 'ForStatement':
var infinite = InfiniteChecker(maxIterations)
var infinite = InfiniteChecker(options.maxIterations)
var result = undefined

enterBlock() // allow lets on delarations
Expand All @@ -192,7 +211,7 @@ function evaluateAst(tree, context){
return result

case 'ForInStatement':
var infinite = InfiniteChecker(maxIterations)
var infinite = InfiniteChecker(options.maxIterations)
var result = undefined

var value = walk(node.right)
Expand Down Expand Up @@ -228,7 +247,7 @@ function evaluateAst(tree, context){
return result

case 'WhileStatement':
var infinite = InfiniteChecker(maxIterations)
var infinite = InfiniteChecker(options.maxIterations)
while (walk(node.test)){
walk(node.body)
infinite.check()
Expand Down Expand Up @@ -509,7 +528,7 @@ function getFunction(body, params, parentContext, traceNode){
context[param] = arg
}
})
var result = evaluateAst(body, context)
var result = evaluateAst(body, context, {timeout: 0, maxIterations: MAX_ITERATIONS_DEFAULT})

if (result instanceof ReturnValue){
return result.value
Expand Down
6 changes: 5 additions & 1 deletion lib/infinite-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ module.exports = InfiniteChecker

function InfiniteChecker(maxIterations){
if (this instanceof InfiniteChecker){
this.maxIterations = maxIterations
if (maxIterations == 0) {
this.maxIterations = Number.POSITIVE_INFINITY
} else {
this.maxIterations = maxIterations
}
this.count = 0
} else {
return new InfiniteChecker(maxIterations)
Expand Down
7 changes: 7 additions & 0 deletions test/sandbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ test('infinite while loop', function(t){
t.end()
})

test('infinite while loop timeout', function(t){
t.throws(function(){
safeEval('while (true){}', {}, {timeout: 100, maxIterations: 0})
})
t.end()
})

test('set wrapped string prototype', function(t){
var code = 'String.prototype.makeLouder = function() { return this + "!" }; "test".makeLouder()'
t.equal(safeEval(code), 'test!')
Expand Down