feat(sandbox): only allow strings of Lua as params

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.
This commit is contained in:
Enrique García Cota
2021-01-05 00:40:59 +01:00
committed by Enrique García Cota
parent 9f83b8914a
commit 50bfa4abca
3 changed files with 42 additions and 47 deletions

View File

@@ -28,26 +28,10 @@ local sandbox = {
]]
}
-- Lua 5.2, 5.3 and above
-- https://leafo.net/guides/setfenv-in-lua52-and-above.html#setfenv-implementation
local setfenv = setfenv or function(fn, env)
local i = 1
while true do
local name = debug.getupvalue(fn, i)
if name == "_ENV" then
debug.upvaluejoin(fn, i, (function()
return env
end), 1)
break
elseif not name then
break
end
i = i + 1
end
return fn
end
-- PUC-Rio Lua 5.1 does not support deactivation of binary code
local mode_supported = _ENV or type(_G.jit) == "table"
sandbox.mode_supported = mode_supported
-- The base environment is merged with the given env option (or an empty table, if no env provided)
--
@@ -137,7 +121,7 @@ local function cleanup()
end
-- Public interface: sandbox.protect
function sandbox.protect(f, options)
function sandbox.protect(code, options)
options = options or {}
local quota = false
@@ -148,9 +132,17 @@ function sandbox.protect(f, options)
local env = merge(options.env or {}, BASE_ENV)
env._G = env._G or env
if type(f) == 'string' then
f = assert(load(f, nil, options.mode, env))
assert(type(code) == 'string', "expected a string")
local f
if mode_supported then
f = assert(load(code, nil, options.mode, env))
else
if options.mode then
error("options.mode is not supported on this environment (usually PUC-Rio Lua 5.1). Please unset options.mode")
end
f = assert(loadstring(code))
setfenv(f, env)
end
@@ -177,11 +169,11 @@ function sandbox.protect(f, options)
end
-- Public interface: sandbox.run
function sandbox.run(f, options, ...)
return sandbox.protect(f, options)(...)
function sandbox.run(code, options, ...)
return sandbox.protect(code, options)(...)
end
-- make sandbox(f) == sandbox.protect(f)
setmetatable(sandbox, {__call = function(_,f,o) return sandbox.protect(f,o) end})
setmetatable(sandbox, {__call = function(_,code,o) return sandbox.protect(code,o) end})
return sandbox