diff --git a/README.md b/README.md index a33dad4..990005f 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,17 @@ Usage -- It is also possible to use the env option to add additional variables to the environment sandbox('return foo', {env = {foo = 'This was on the environment'}}) + -- The variables defined on the env are deep-copied and changes on them will not be persisted + local env = {foo = "can't touch this"} + sandbox('foo = "bar"', {env = env}) + assert(env.foo = "can't touch this") + + -- If you want to modify variables from inside the sandbox, use the refs option: + local refs = {foo = "kindof insecure"} + sandbox('foo = "changed"', {refs = refs}) + assert(refs.foo = "changed") + + Installation ============ diff --git a/sandbox.lua b/sandbox.lua index 034f4eb..696acc1 100644 --- a/sandbox.lua +++ b/sandbox.lua @@ -67,9 +67,10 @@ copy = function(other) end -local function cleanup() +local function cleanup(sandboxed_env, refs) debug.sethook() string.rep = string_rep + for k,_ in pairs(refs) do refs[k] = sandboxed_env[k] end end local function run(f, options) @@ -79,8 +80,10 @@ local function run(f, options) local quota = options.quota or 500000 local env = options.env or {} + local refs = options.refs or {} local sandboxed_env = merge(copy(BASE_ENV), env) + for k,v in pairs(refs) do sandboxed_env[k] = v end setfenv(f, sandboxed_env) @@ -93,7 +96,7 @@ local function run(f, options) local timeout = function(str) instructions_count = instructions_count + 1 if instructions_count >= quota then - cleanup() + cleanup(sandboxed_env, refs) error('Quota exceeded: ' .. tostring(instructions_count) .. '/' .. tostring(quota) .. ' instructions') end end @@ -102,7 +105,7 @@ local function run(f, options) local ok, result = pcall(f) - cleanup() + cleanup(sandboxed_env, refs) if not ok then error(result) end return result diff --git a/spec/sandbox_spec.lua b/spec/sandbox_spec.lua index da406d5..df3e539 100644 --- a/spec/sandbox_spec.lua +++ b/spec/sandbox_spec.lua @@ -58,19 +58,49 @@ describe('sandbox', function() it('accepts a quota param', function() assert.no_has_error(function() sandbox("for i=1,100 do end") end) - assert.has_error(function() sandbox("for i=1,100 do end", {quota = 50}) end) + assert.has_error(function() sandbox("for i=1,100 do end", {quota = 20}) end) end) end) - describe('when given an env', function() + describe('when given an env option', function() it('is available on the sandboxed env', function() assert.equal(1, sandbox("return foo", {env = {foo = 1}})) end) - it('does not hide previous env', function() + it('does not hide base env', function() assert.equal('HELLO', sandbox("return string.upper(foo)", {env = {foo = 'hello'}})) end) + + it('can not modify the env', function() + local env = {foo = 1} + sandbox("foo = 2", {env = env}) + assert.equal(env.foo, 1) + end) + end) + + describe('when given a refs option', function() + it('is available on the sandboxed env', function() + assert.equal(1, sandbox("return foo", {refs = {foo = 1}})) + end) + + it('does not hide base env', function() + assert.equal('HELLO', sandbox("return string.upper(foo)", {refs = {foo = 'hello'}})) + end) + + it('can modify the refs', function() + local refs = {foo = 1} + sandbox("foo = 2", {refs = refs}) + assert.equal(refs.foo, 2) + end) + + it('can modify the ref tables keys', function() + local refs = {items = {quantity = 1}} + sandbox("items.quantity = 2", {refs = refs}) + assert.equal(refs.items.quantity, 2) + end) + + end)