This commit is contained in:
kikito 2013-09-03 16:41:46 +02:00
parent 36be73e3a9
commit b1d69c89d7
2 changed files with 34 additions and 11 deletions

View File

@ -47,15 +47,26 @@ end)
local string_rep = string.rep local string_rep = string.rep
local function copy(other) local copy -- defined below
local function merge(destination, other)
if type(other) ~= 'table' then return other end if type(other) ~= 'table' then return other end
local c = {}
for k,v in pairs(other) do for k,v in pairs(other) do
c[copy(k)] = copy(v) destination[copy(k)] = copy(v)
end end
return destination
end
-- declared above
copy = function(other)
if type(other) ~= 'table' then return other end
local c = merge({}, other)
local mt = getmetatable(other)
if mt then setmetatable(c, copy(mt)) end
return c return c
end end
local function cleanup() local function cleanup()
debug.sethook() debug.sethook()
string.rep = string_rep string.rep = string_rep
@ -66,11 +77,12 @@ local function run(f, options)
options = options or {} options = options or {}
local limit = options.limit or 500000 local quota = options.quota or 500000
local env = options.env or {}
local env = copy(BASE_ENV) local sandboxed_env = merge(copy(BASE_ENV), env)
setfenv(f, env) setfenv(f, sandboxed_env)
-- I would love to be able to make step greater than 1 -- I would love to be able to make step greater than 1
-- (say, 500000) but any value > 1 seems to choke with a simple while true do end -- (say, 500000) but any value > 1 seems to choke with a simple while true do end
@ -80,9 +92,9 @@ local function run(f, options)
local instructions_count = 0 local instructions_count = 0
local timeout = function(str) local timeout = function(str)
instructions_count = instructions_count + 1 instructions_count = instructions_count + 1
if instructions_count >= limit then if instructions_count >= quota then
cleanup() cleanup()
error('Quota exceeded: ' .. tostring(instructions_count) .. '/' .. tostring(limit) .. ' instructions') error('Quota exceeded: ' .. tostring(instructions_count) .. '/' .. tostring(quota) .. ' instructions')
end end
end end
debug.sethook(timeout, "", step) debug.sethook(timeout, "", step)
@ -91,8 +103,8 @@ local function run(f, options)
local ok, result = pcall(f) local ok, result = pcall(f)
cleanup() cleanup()
if not ok then error(result) end
if not ok then error(result) end
return result return result
end end

View File

@ -22,6 +22,7 @@ describe('sandbox', function()
it('does not allow access to not-safe stuff', function() it('does not allow access to not-safe stuff', function()
assert.has_error(function() sandbox('return setmetatable({}, {})') end) assert.has_error(function() sandbox('return setmetatable({}, {})') end)
assert.has_error(function() sandbox('return string.rep("hello", 5)') end) assert.has_error(function() sandbox('return string.rep("hello", 5)') end)
assert.has_error(function() sandbox('return _G.string.upper("hello")') end)
end) end)
it('does not allow pesky string:rep', function() it('does not allow pesky string:rep', function()
@ -55,13 +56,23 @@ describe('sandbox', function()
assert.equal('hellohello', string.rep('hello', 2)) assert.equal('hellohello', string.rep('hello', 2))
end) end)
it('#focus accepts a limit param', function() it('accepts a quota param', function()
assert.no_has_error(function() sandbox("for i=1,100 do end") end) assert.no_has_error(function() sandbox("for i=1,100 do end") end)
assert.has_error(function() sandbox("for i=1,100 do end", {limit = 50}) end) assert.has_error(function() sandbox("for i=1,100 do end", {quota = 50}) end)
end) end)
end) end)
describe('when given an env', 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()
assert.equal('HELLO', sandbox("return string.upper(foo)", {env = {foo = 'hello'}}))
end)
end)
end) end)