mirror of
https://github.com/kikito/lua-sandbox.git
synced 2024-12-18 03:04:20 +00:00
50bfa4abca
This change drops support for "protecting" raw Lua functions. There are two main reasons for this change: * More modern versions of PUC Rio Lua don't have `setfenv`. It is possible to get around this by using the debug library, but that library is not available in all environments. * Solutions based on `load` (which only allow string inputs) are objectively better since they give the user more control. For instance, you can deactivate support for binary code selectively. As a result, we are using the `load`-based sandbox in all versions of Lua that supports it, using `setfenv`-based sandboxing only when nothing else is available (PUC Rio 5.1). We are also explicitly raising an error if `options.mode` is passed but we are using `setfenv`. This is to prevent users from believing they are protected against binary code, when in fact they are not.
129 lines
4.3 KiB
Lua
129 lines
4.3 KiB
Lua
local sandbox = require 'sandbox'
|
|
|
|
describe('sandbox.run', function()
|
|
|
|
describe('when handling base cases', function()
|
|
|
|
it('can run harmless strings', function()
|
|
local r = sandbox.run("return 'hello'")
|
|
assert.equal(r, 'hello')
|
|
end)
|
|
|
|
it('can run bytecode strings by default', function()
|
|
local fn = function() end
|
|
assert.has_no.error(function() sandbox.run(string.dump(fn)) end)
|
|
end)
|
|
|
|
if sandbox.mode_supported then
|
|
it('can\'t run bytecode strings if given a \'t\' mode option', function()
|
|
local fn = function() end
|
|
assert.error(function() sandbox.run(string.dump(fn), { mode = 't' }) end)
|
|
end)
|
|
else
|
|
it('throws an error if the mode option is used', function()
|
|
assert.error(function() sandbox.run("", { mode = 't' }) end)
|
|
end)
|
|
end
|
|
|
|
it('has access to safe methods', function()
|
|
assert.equal(10, sandbox.run("return tonumber('10')"))
|
|
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(10, sandbox.run("return math.max(1,10)"))
|
|
end)
|
|
|
|
it('does not allow access to not-safe stuff', function()
|
|
assert.error(function() sandbox.run('return setmetatable({}, {})') end)
|
|
assert.error(function() sandbox.run('return string.rep("hello", 5)') end)
|
|
end)
|
|
|
|
it('does return multiple values', function()
|
|
local result = { sandbox.run("return 'hello', 'world'") }
|
|
assert.same({ 'hello', 'world' }, result)
|
|
end)
|
|
end)
|
|
|
|
describe('when handling string.rep', function()
|
|
it('does not allow pesky string:rep', function()
|
|
assert.error(function() sandbox.run('return ("hello"):rep(5)') end)
|
|
end)
|
|
|
|
it('restores the value of string.rep', function()
|
|
sandbox.run("")
|
|
assert.equal('hellohello', string.rep('hello', 2))
|
|
end)
|
|
|
|
it('restores string.rep even if there is an error', function()
|
|
assert.error(function() sandbox.run("error('foo')") end)
|
|
assert.equal('hellohello', string.rep('hello', 2))
|
|
end)
|
|
|
|
it('passes parameters to the code', function()
|
|
assert.equal(sandbox.run("local a, b = ...; return a + b", {}, 1,2), 3)
|
|
end)
|
|
end)
|
|
|
|
|
|
describe('when the sandboxed code tries to modify the base environment', function()
|
|
|
|
it('does not allow modifying the modules', function()
|
|
assert.error(function() sandbox.run("string.foo = 1") end)
|
|
assert.error(function() sandbox.run("string.char = 1") end)
|
|
end)
|
|
|
|
it('does not persist modifications of base functions', function()
|
|
sandbox.run('error = function() end')
|
|
assert.error(function() sandbox.run("error('this should be raised')") end)
|
|
end)
|
|
|
|
it('DOES persist modification to base functions when they are provided by the base env', function()
|
|
local env = {['next'] = 'hello'}
|
|
sandbox.run('next = "bye"', {env=env})
|
|
assert.equal(env['next'], 'bye')
|
|
end)
|
|
end)
|
|
|
|
|
|
describe('when given infinite loops', function()
|
|
|
|
it('throws an error with infinite loops', function()
|
|
assert.error(function() sandbox.run("while true do end") end)
|
|
end)
|
|
|
|
it('restores string.rep even after a while true', function()
|
|
assert.error(function() sandbox.run("while true do end") end)
|
|
assert.equal('hellohello', string.rep('hello', 2))
|
|
end)
|
|
|
|
it('accepts a quota param', function()
|
|
assert.has_no.errors(function() sandbox.run("for i=1,100 do end") end)
|
|
assert.error(function() sandbox.run("for i=1,100 do end", {quota = 20}) end)
|
|
end)
|
|
|
|
it('does not use quotes if the quote param is false', function()
|
|
assert.has_no.errors(function() sandbox.run("for i=1,1000000 do end", {quota = false}) end)
|
|
end)
|
|
|
|
end)
|
|
|
|
|
|
describe('when given an env option', function()
|
|
it('is available on the sandboxed env as the _G variable', function()
|
|
local env = {foo = 1}
|
|
assert.equal(1, sandbox.run("return foo", {env = env}))
|
|
assert.equal(env, sandbox.run("return _G", {env = env}))
|
|
end)
|
|
|
|
it('does not hide base env', function()
|
|
assert.equal('HELLO', sandbox.run("return string.upper(foo)", {env = {foo = 'hello'}}))
|
|
end)
|
|
|
|
it('can modify the env', function()
|
|
local env = {foo = 1}
|
|
sandbox.run("foo = 2", {env = env})
|
|
assert.equal(env.foo, 2)
|
|
end)
|
|
end)
|
|
|
|
end)
|