Search code examples
luasandboxlua-api

Lua sandbox _ENV with personal baselib (require, assert etc)


Lua 5.3.2

I have a service written in Lua/C, that executes lua files in the same Lua_State.

I need to provide all the standard libraries for the file execution environment.

The simplest thing is to execute files this way: loadfile(file_path, "bt", _G)

The problem is: code in the file is able to corrupt the service's global state, so this method is not secure.

So, I need to create a sandboxed environment loadfile(file_path, "bt", env)

The question: how to register all the standard libraries from linit.c in the env variable?

I can simply register all the libs from the linit.c, except luaopen_base, because it contains lua_pushglobaltable

I thought about this:

local env = {}
for k,v in pairs(_G) do
    if type(v)=="function" then
        env[k] = v
    end
end

But it looks like a pathetic decision. Is anyone have a better solution?


Solution

  • The easiest way is to make env inherit from _G:

    setmetatable(env,{__index=_G})
    

    Everything in _G will be seen in env, but if you write to env by creating a global variable, it won't affect _G, which seems to be what you want.

    Unfortunately, _G itself is seen in _G and so could be used to write to the original environment by doing things like _G.print=anyvalue.

    To protect _G, add:

    env._G = env
    

    Unfortunately, the original _G remains available via package.loaded._G. This is harder to protect if you want to give access to package. The easiest way is to deep copy _G.package to env.package and change env.package.loaded._G=env.