From 68c2cb3b98e919cbea89c475b380655ea17bfdda Mon Sep 17 00:00:00 2001 From: Gnome Ann <> Date: Sat, 11 Dec 2021 01:21:18 -0500 Subject: [PATCH] Fix a few problems in bridge.lua * Use `python.iter` instead of `pairs` to iterate through `_bridged` * Use `old_loadfile` instead of `safe_require_with_env` to load scripts in order to handle unusual file names * Prevent modules imported by scripts from accessing bridge.lua's environment * Fix behaviour of `KoboldWorldInfoEntry_mt._kobold_next(t, k)` * New `next` implementation now has more safety checks --- bridge.lua | 91 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/bridge.lua b/bridge.lua index 3e3299f0..6c63eb37 100644 --- a/bridge.lua +++ b/bridge.lua @@ -17,7 +17,7 @@ return function(_python, _bridged) ---@return K|nil, V|nil function next(t, k) local meta = getmetatable(t) - return ((meta ~= nil and string.match(rawget(t, "_name"), "^Kobold") and type(meta._kobold_next) == "function") and meta._kobold_next or old_next)(t,k) + return ((meta ~= nil and type(rawget(t, "_name")) == "string" and string.match(rawget(t, "_name"), "^Kobold") and type(meta._kobold_next) == "function") and meta._kobold_next or old_next)(t, k) end @@ -53,13 +53,21 @@ return function(_python, _bridged) package.cpath = "" end + ---@param path string + ---@param filename string + ---@return string + function join_folder_and_filename(path, filename) + return path .. string.match(package.config, "[^\n]+") .. filename + end + --========================================================================== -- _bridged preprocessing --========================================================================== local bridged = {} - for k, v in pairs(_bridged) do + for k in _python.iter(_bridged) do + v = _bridged[k] bridged[k] = type(v) == "userdata" and _python.as_attrgetter(v) or v end set_require_path(bridged.lib_path) @@ -264,7 +272,11 @@ return function(_python, _bridged) ---@param k K ---@return K, any function KoboldWorldInfoEntry_mt._kobold_next(t, k) - return next(fields[rawget(t, "_name")], k) + local _t = fields[rawget(t, "_name")] + if _t == nil then + return + end + return next(_t, k) end ---@param t KoboldWorldInfoEntry|KoboldWorldInfoFolder|KoboldWorldInfo|KoboldWorldInfoFolderSelector @@ -811,28 +823,34 @@ return function(_python, _bridged) local envs = {} local old_load = load - local function safe_load(chunk, chunkname, mode, env) - if mode == nil then - mode = "t" - elseif mode ~= "t" then - error("Calling `load` with a `mode` other than 't' is disabled for security reasons") - return + local function _safe_load(_g) + return function(chunk, chunkname, mode, env) + if mode == nil then + mode = "t" + elseif mode ~= "t" then + error("Calling `load` with a `mode` other than 't' is disabled for security reasons") + return + end + if env == nil then + env = _g + end + return old_load(chunk, chunkname, mode, env) end - if env == nil then - env = _G - end - return old_load(chunk, chunkname, mode, env) end local old_loadfile = loadfile local old_package_loaded = package.loaded local old_package_searchers = package.searchers ---@param modname string - ---@param env table + ---@param env? table + ---@param search_path? string ---@return any, string|nil - local function safe_require_with_env(modname, env) + local function requirex(modname, env, search_path) + if search_path == nil then + search_path = bridged.lib_path + end if modname == "bridge" then - return function() return _G.kobold, _G.koboldcore end + return function() return env.kobold, env.koboldcore end end if type(modname) == "number" then modname = tostring(modname) @@ -840,13 +858,14 @@ return function(_python, _bridged) error("bad argument #1 to 'require' (string expected, got "..type(modname)..")") return end - local allowsearch = type(modname) == "string" and string.match(modname, "[^%w._]") == nil and string.match(modname, "%.%.") == nil + local allowsearch = type(modname) == "string" and string.match(modname, "[^%w._-]") == nil and string.match(modname, "%.%.") == nil if allowsearch and old_package_loaded[modname] then return old_package_loaded[modname] end local loader, path local errors = {} local n_errors = 0 + set_require_path(search_path) for k, v in ipairs(old_package_searchers) do loader, path = v(modname) if allowsearch and type(loader) == "function" then @@ -856,6 +875,7 @@ return function(_python, _bridged) errors[n_errors] = "\n\t" .. loader end end + set_require_path(bridged.lib_path) if not allowsearch or type(loader) ~= "function" then error("module '" .. modname .. "' not found:" .. table.concat(errors)) return @@ -864,10 +884,12 @@ return function(_python, _bridged) old_package_loaded[modname] = retval == nil or retval return old_package_loaded[modname], path end - ---@param modname string - ---@return any, string|nil - local function safe_require(modname) - return safe_require_with_env(modname, _G) + local function _safe_require(_g) + ---@param modname string + ---@return any, string|nil + return function(modname) + return requirex(modname, _g) + end end local sandbox_template_env = { @@ -876,7 +898,7 @@ return function(_python, _bridged) error = error, getmetatable = getmetatable, ipairs = ipairs, - load = safe_load, + load = nil, ---@type function next = next, pairs = pairs, pcall = pcall, @@ -903,7 +925,7 @@ return function(_python, _bridged) wrap = coroutine.wrap, yield = coroutine.yield, }, - require = safe_require, + require = nil, ---@type function package = { config = package.config, }, @@ -1017,6 +1039,8 @@ return function(_python, _bridged) if universe == 0 then envs[universe].koboldcore = deepcopy(koboldcore) end + envs[universe].load = _safe_load(env) + envs[universe].require = _safe_require(env) env._G = env end return env @@ -1028,17 +1052,17 @@ return function(_python, _bridged) --========================================================================== - -- Connection to aiserver.py + -- API for aiserver.py --========================================================================== ---@return nil function koboldbridge.load_userscripts(filenames, modulenames, descriptions) - local old_path = set_require_path(bridged.userscript_path) + set_require_path(bridged.userscript_path) koboldbridge.userscripts = {} koboldbridge.num_userscripts = 0 for i, filename in _python.enumerate(filenames) do bridged.load_callback(filename) - local _userscript = safe_require_with_env(filename, koboldbridge.get_universe(filename)) + local _userscript = old_loadfile(join_folder_and_filename(bridged.userscript_path, filename), "t", koboldbridge.get_universe(filename))() local userscript = deepcopy(KoboldUserScriptModule) rawset(userscript, "_inmod", _userscript.inmod) rawset(userscript, "_genmod", _userscript.genmod) @@ -1049,20 +1073,17 @@ return function(_python, _bridged) koboldbridge.userscripts[i+1] = userscript koboldbridge.num_userscripts = i + 1 end - set_require_path(old_path) end ---@return nil function koboldbridge.load_corescript(filename) - set_require_path(bridged.corescript_path) - local corescript = safe_require_with_env(filename, koboldbridge.get_universe(0)) + local corescript = old_loadfile(join_folder_and_filename(bridged.corescript_path, filename), "t", koboldbridge.get_universe(0))() koboldbridge.inmod = corescript.inmod koboldbridge.genmod = corescript.genmod koboldbridge.outmod = corescript.outmod - set_require_path(bridged.lib_path) end - ---@return any, any, any + ---@return integer, any, any, any function koboldbridge.execute() local i, g, o local num_generated = 0 @@ -1077,7 +1098,7 @@ return function(_python, _bridged) while generating and num_generated < bridged.get_gen_len() do bridged.generation_step() if koboldbridge.genmod ~= nil then - table.insert(g, koboldbridge.genmod()) + g[num_generated + 1] = koboldbridge.genmod() if genmod_comparison_context ~= kobold.worldinfo:compute_context() then bridged.register_context_change() genmod_comparison_context = nil @@ -1092,11 +1113,13 @@ return function(_python, _bridged) end generating = true userstate = "inmod" - return i, g, o + return num_generated, i, g, o end - ---------------------------------------------------------------------------- + --========================================================================== + -- Footer + --========================================================================== metawrapper.__newindex = nil setmetatable(KoboldWorldInfoEntry, KoboldWorldInfoEntry_mt)