在开始之前,我先澄清一下,我已经浏览了有关沙箱的现有问题,但是我发现的任何解决方案似乎都不适合我。
我要实现的不一定是运行不安全代码的完全安全的沙箱,而是要提供一个可以模拟游戏功能的环境(准确地说是《星球大战》,战争),我可以扩展使用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调用本身似乎可以工作。如果我需要的脚本返回一个函数,我仍然可以将其存储在变量中并执行它。
对于解决此问题的任何建议,我将非常感谢。
答案 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