moved specs from busted to telescope in order to fix issue with debug.sethook-based quota errors

This commit is contained in:
kikito 2013-09-13 11:35:35 +02:00
parent e9ef4bb57c
commit 83a5a2b1e2
3 changed files with 55 additions and 66 deletions

View File

@ -71,12 +71,9 @@ This library is released under the MIT license. See MIT-LICENSE.txt for details
Specs Specs
===== =====
This project uses [busted](http://olivinelabs.com/busted/) for its specs. In order to run them, install `busted` and then: This project uses [telescope](https://github.com/norman/telescope) for its specs. In order to run them, install it and then:
cd /path/to/where/the/spec/folder/is cd /path/to/where/the/spec/folder/is
busted tsc spec/*
I would love to use [busted](http://olivinelabs.com/busted/), but it has some incompatibility with `debug.sethook(f, "", quota)` and the tests just hanged up.

View File

@ -1,17 +1,17 @@
local BASE_ENV = {} local BASE_ENV = {}
-- Non-safe : -- Non-safe :
-- string.rep: can be used to allocate millions of bytes in 1 operation -- * string.rep: can be used to allocate millions of bytes in 1 operation
-- {set|get}metatable: can be used to modify the metatable of global objects (strings, integers) -- * {set|get}metatable: can be used to modify the metatable of global objects (strings, integers)
-- collectgarbage: can affect performance of other systems -- * collectgarbage: can affect performance of other systems
-- dofile: can access the server filesystem -- * dofile: can access the server filesystem
-- _G: Unsafe. It can be mocked though -- * _G: Unsafe. It can be mocked though
-- load{file|string}: All unsafe because they can grant acces to global env -- * load{file|string}: All unsafe because they can grant acces to global env
-- raw{get|set|equal}: Potentially unsafe -- * raw{get|set|equal}: Potentially unsafe
-- module|require|module: Can modify the host settings -- * module|require|module: Can modify the host settings
-- string.dump: Can display confidential server info (implementation of functions) -- * string.dump: Can display confidential server info (implementation of functions)
-- string.rep: Can allocate millions of bytes in one go -- * string.rep: Can allocate millions of bytes in one go
-- math.randomseed: Can affect the host sytem -- * math.randomseed: Can affect the host sytem
-- io.*, os.*: Most stuff there is non-save -- * io.*, os.*: Most stuff there is non-save
([[ ([[
@ -74,7 +74,6 @@ local function cleanup()
end end
local function protect(f, options) local function protect(f, options)
return function(...)
if type(f) == 'string' then f = assert(loadstring(f)) end if type(f) == 'string' then f = assert(loadstring(f)) end
options = options or {} options = options or {}
@ -84,20 +83,13 @@ local function protect(f, options)
setfenv(f, env) setfenv(f, env)
-- I would love to be able to make step greater than 1 return function(...)
-- (say, 500000) but any value > 1 seems to choke with a simple while true do end local timeout = function()
-- After ~100 iterations, they stop calling timeout. So I need to use step = 1 and
-- instructions_count the steps separatedly
local step = 1
local instructions_count = 0
local timeout = function(str)
instructions_count = instructions_count + 1
if instructions_count >= quota then
cleanup() cleanup()
error('Quota exceeded: ' .. tostring(instructions_count) .. '/' .. tostring(quota) .. ' instructions') error('Quota exceeded: ' .. tostring(quota))
end end
end
debug.sethook(timeout, "", step) debug.sethook(timeout, "", quota)
string.rep = nil string.rep = nil
local ok, result = pcall(f, ...) local ok, result = pcall(f, ...)

View File

@ -5,45 +5,45 @@ describe('sandbox.run', function()
describe('when handling base cases', function() describe('when handling base cases', function()
it('can run harmless functions', function() it('can run harmless functions', function()
local r = sandbox.run(function() return 'hello' end) local r = sandbox.run(function() return 'hello' end)
assert.equal(r, 'hello') assert_equal(r, 'hello')
end) end)
it('can run harmless strings', function() it('can run harmless strings', function()
local r = sandbox.run("return 'hello'") local r = sandbox.run("return 'hello'")
assert.equal(r, 'hello') assert_equal(r, 'hello')
end) end)
it('has access to safe methods', function() it('has access to safe methods', function()
assert.equal(10, sandbox.run("return tonumber('10')")) assert_equal(10, sandbox.run("return tonumber('10')"))
assert.equal('HELLO', sandbox.run("return string.upper('hello')")) assert_equal('HELLO', sandbox.run("return string.upper('hello')"))
assert.equal(1, sandbox.run("local a = {3,2,1}; table.sort(a); return a[1]")) assert_equal(1, sandbox.run("local a = {3,2,1}; table.sort(a); return a[1]"))
assert.equal(10, sandbox.run("return math.max(1,10)")) assert_equal(10, sandbox.run("return math.max(1,10)"))
end) end)
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.run('return setmetatable({}, {})') end) assert_error(function() sandbox.run('return setmetatable({}, {})') end)
assert.has_error(function() sandbox.run('return string.rep("hello", 5)') end) assert_error(function() sandbox.run('return string.rep("hello", 5)') end)
assert.has_error(function() sandbox.run('return _G.string.upper("hello")') end) assert_error(function() sandbox.run('return _G.string.upper("hello")') end)
end) end)
end) end)
describe('when handling string.rep', function() describe('when handling string.rep', function()
it('does not allow pesky string:rep', function() it('does not allow pesky string:rep', function()
assert.has_error(function() sandbox.run('return ("hello"):rep(5)') end) assert_error(function() sandbox.run('return ("hello"):rep(5)') end)
end) end)
it('restores the value of string.rep', function() it('restores the value of string.rep', function()
sandbox.run("") sandbox.run("")
assert.equal('hellohello', string.rep('hello', 2)) assert_equal('hellohello', string.rep('hello', 2))
end) end)
it('restores string.rep even if there is an error', function() it('restores string.rep even if there is an error', function()
assert.has_error(function() sandbox.run("error('foo')") end) assert_error(function() sandbox.run("error('foo')") end)
assert.equal('hellohello', string.rep('hello', 2)) assert_equal('hellohello', string.rep('hello', 2))
end) end)
it('passes parameters to the function', function() it('passes parameters to the function', function()
assert.equal(sandbox.run(function(a,b) return a + b end, {}, 1,2), 3) assert_equal(sandbox.run(function(a,b) return a + b end, {}, 1,2), 3)
end) end)
end) end)
@ -51,19 +51,19 @@ describe('sandbox.run', function()
describe('when the sandboxed function tries to modify the base environment', function() describe('when the sandboxed function tries to modify the base environment', function()
it('does not allow modifying the modules', function() it('does not allow modifying the modules', function()
assert.has_error(function() sandbox.run("string.foo = 1") end) assert_error(function() sandbox.run("string.foo = 1") end)
assert.has_error(function() sandbox.run("string.char = 1") end) assert_error(function() sandbox.run("string.char = 1") end)
end) end)
it('does not persist modifications of base functions', function() it('does not persist modifications of base functions', function()
sandbox.run('error = function() end') sandbox.run('error = function() end')
assert.has_error(function() sandbox.run("error('this should be raised')") end) assert_error(function() sandbox.run("error('this should be raised')") end)
end) end)
it('DOES persist modification to base functions when they are provided by the base env', function() it('DOES persist modification to base functions when they are provided by the base env', function()
local env = {['next'] = 'hello'} local env = {['next'] = 'hello'}
sandbox.run('next = "bye"', {env=env}) sandbox.run('next = "bye"', {env=env})
assert.equal(env['next'], 'bye') assert_equal(env['next'], 'bye')
end) end)
end) end)
@ -71,17 +71,17 @@ describe('sandbox.run', function()
describe('when given infinite loops', function() describe('when given infinite loops', function()
it('throws an error with infinite loops', function() it('throws an error with infinite loops', function()
assert.has_error(function() sandbox.run("while true do end") end) assert_error(function() sandbox.run("while true do end") end)
end) end)
it('restores string.rep even after a while true', function() it('restores string.rep even after a while true', function()
assert.has_error(function() sandbox.run("while true do end") end) assert_error(function() sandbox.run("while true do end") end)
assert.equal('hellohello', string.rep('hello', 2)) assert_equal('hellohello', string.rep('hello', 2))
end) end)
it('accepts a quota param', function() it('accepts a quota param', function()
assert.no_has_error(function() sandbox.run("for i=1,100 do end") end) assert_not_error(function() sandbox.run("for i=1,100 do end") end)
assert.has_error(function() sandbox.run("for i=1,100 do end", {quota = 20}) end) assert_error(function() sandbox.run("for i=1,100 do end", {quota = 20}) end)
end) end)
end) end)
@ -89,17 +89,17 @@ describe('sandbox.run', function()
describe('when given an env option', function() describe('when given an env option', function()
it('is available on the sandboxed env', function() it('is available on the sandboxed env', function()
assert.equal(1, sandbox.run("return foo", {env = {foo = 1}})) assert_equal(1, sandbox.run("return foo", {env = {foo = 1}}))
end) end)
it('does not hide base env', function() it('does not hide base env', function()
assert.equal('HELLO', sandbox.run("return string.upper(foo)", {env = {foo = 'hello'}})) assert_equal('HELLO', sandbox.run("return string.upper(foo)", {env = {foo = 'hello'}}))
end) end)
it('can modify the env', function() it('can modify the env', function()
local env = {foo = 1} local env = {foo = 1}
sandbox.run("foo = 2", {env = env}) sandbox.run("foo = 2", {env = env})
assert.equal(env.foo, 2) assert_equal(env.foo, 2)
end) end)
end) end)