Beispiel #1
0
class LuaSandbox:
    """A wrapper for LuaRuntime to use when running untrusted code.

    This has some Cellua-specific features, like scanning for unauthorized use
    of globals/closures [closures NYI] and inclusion of Cellua utility modules.
    """
    def __init__(
            self,
            *,
            allow_global_state=True,  # TODO not yet fully implemented
            allow_random=True,
            allow_time=True,
            custom_globals=None,
            **kwargs):
        """Initialize a sandboxed LuaRuntime.

        Optional keyword arguments:
        - `allow_global_state` -- bool; whether to allow setting global
          variables and setting external variables from within a closure
          [closures NYI]
        - `allow_random` -- bool; whether to give access to Lua's PRNG functions
        - `allow_time` -- bool; whether to give access to Lua's date/time
          functions
        - `custom_globals` -- dict; any extra variables to insert into the
          global namespace

        All other args and kwargs are passed to LuaRuntime(). It is not
        necessary to pass `register_eval=False` or `register_builtins=False`,
        since this is already done by LuaSandbox.
        """
        self._allow_global_state = allow_global_state
        # Prevent access to Python from Lua.
        self._lua = LuaRuntime(register_eval=False,
                               register_builtins=False,
                               **kwargs)
        # Get the behind-the-scenes global table.
        allowed_names = LUA_SAFE_NAMES
        if allow_random:
            allowed_names += LUA_RANDOM_NAMES
        if allow_time:
            allowed_names += LUA_TIME_NAMES
        new_globals = {}
        for name in allowed_names:
            # We have to handle names like `string.format` by making a new table
            # `string` containing keys like `format`.
            t = new_globals
            keys = name.split('.')
            for key in keys[:-1]:
                if key not in t:
                    t[key] = self.table()
                t = t[key]
            t[keys[-1]] = self._lua.eval(name)
        if custom_globals:
            new_globals.update(custom_globals)
        new_globals = self.table_from(new_globals)
        # Override global table access if necessary.
        self._sandboxer_code = ''
        if not allow_global_state:
            new_globals = lua_utils.make_table_readonly_recursive(
                self, new_globals,
                "Cannot set value '%s' on %s; global variables are forbidden")
        self.globals().safe_globals = new_globals
        if self.globals().setfenv:
            # Lua 5.1
            self._sandboxer_code = 'setfenv(1, safe_globals)\n'
        else:
            # Lua 5.2+
            self._sandboxer_code = '_ENV = safe_globals\n'

    def __getattr__(self, name):
        return getattr(self._lua, name)

    def _sandbox(self, lua_code):
        # TODO: Handle self._allow_global_state here by scanning AST for
        # closures. Raise an exception if one is found.
        return self._sandboxer_code + lua_code

    def compile(self, lua_code):
        return self._lua.compile(self._sandbox(lua_code))

    def eval(self, lua_code, *args):
        return self._lua.execute(self._sandbox('return ' + lua_code), *args)

    def execute(self, lua_code, *args):
        return self._lua.execute(self._sandbox(lua_code), *args)

    def require(self, lua_code, *args):
        raise NotImplemented(
            f"{self.__class__.__name__}.require() is not yet implemented.")