沙盒Lua功能

时间:2019-01-15 12:43:31

标签: lua

在开始之前,我先澄清一下,我已经浏览了有关沙箱的现有问题,但是我发现的任何解决方案似乎都不适合我。

我要实现的不一定是运行不安全代码的完全安全的沙箱,而是要提供一个可以模拟游戏功能的环境(准确地说是《星球大战》,战争),我可以扩展使用Lua脚本。该游戏提供了一些我希望在此环境中实现的全局函数,以便能够在不启动游戏本身的情况下测试我的游戏扩展,因为它的调试功能非常差。游戏也有一些怪癖。在这种情况下,相关的问题是使用升值会导致损坏的保存游戏,因此这就是我们的Lua模块必须是全局变量的原因。由于我的游戏脚本未包含在单个文件中,并且可能需要其他文件,因此使用setfenv()调用并声明自己的函数来设置环境对我而言实际上并不起作用,因为这仅适用于您指定和而不是它可能会调用的其他任何功能。

现在输入我的代码本身。到目前为止,我已经尝试过通过简单地深度克隆_G并将其恢复到以前的状态来实现一个简单的沙箱(这仍然是WIP,我意识到您仍然可以对表和环境进行修改,或者可能脱离该方法未涵盖的沙箱;正如我所说,这并不是要运行不安全的代码)。

这在sandbox.lua中:

local function run(func)
    -- tested deep_clone with luassert's assert.are_same(), so this should be fine
    local g_clone = deep_clone(_G)
    -- coroutine is needed because one of the game functions needs
    -- to be able to interrupt the script
    coroutine.wrap(function()
        func()
    end)()

    for k, v in pairs(_G) do
        if not g_clone[k] then
            _G[k] = nil
        else
            _G[k] = g_clone[k]
        end
    end
end

这是我的测试脚本:

sandbox.run(function()
    require "src/declare_global" -- this declares A_GLOBAL = "TEST"
    print(A_GLOBAL) -- prints "TEST", everything is fine
end)

print(A_GLOBAL) -- prints nil, as intended

-- as I've restored _G to its former state I should be able to require 
-- the script again!
require "src/declare_global" 

print("package.loaded: "..tostring(package.loaded["src/declare_global"])) -- prints nil

print(A_GLOBAL) -- prints nil

正如您所看到的,我的问题是,在沙盒中执行此操作后,只需要声明一个全局变量的文件不再起作用。因此,恢复_G的状态可能存在问题,但是我不知道它可能是什么。不过,require调用本身似乎可以工作。如果我需要的脚本返回一个函数,我仍然可以将其存储在变量中并执行它。

对于解决此问题的任何建议,我将非常感谢。

1 个答案:

答案 0 :(得分:0)

我找到了解决问题的方法。但是,我觉得这是实现我想要的目标的一种低效方式,因此,如果有人有更好的建议,我很想听听。

@EgorSkriptunoff对我的有关“ _G”的“浅”还原的原始问题的评论为我指明了正确的方向,因此我最终编写了“深度还原”功能,该功能似乎可以与到目前为止所测试的功能一起使用

local known_tables = {}

local function deep_restore(tab, backup)
    known_tables[tab] = true
    for k, v in pairs(tab) do
        if backup[k] == nil then
            tab[k] = nil
        elseif type(v) == "table" and not known_tables[v] then
            deep_restore(v, backup[k])
        elseif not type(v) == "table" then
            tab[k] = backup[k]
        end
    end
end


local function run(func)
    local g_clone = deep_clone(_G)

    coroutine.wrap(function()
        func()
    end)()

    deep_restore(_G, g_clone)
    known_tables = {}

end

结果:

sandbox.run(function()
    require "src/declare_global"
    print("Printing inside sandbox: "..A_GLOBAL) -- prints TEST
end)

print("This should be nil: "..tostring(A_GLOBAL)) -- nil
print("package.loaded should be nil: "..tostring(package.loaded["src/declare_global"])) -- nil

print("\nRequiring again...")
require "src/declare_global"
print("package.loaded: "..tostring(package.loaded["src/declare_global"])) -- true
print(A_GLOBAL) -- TEST