/ bjvxlx_util / sandbox.lua
sandbox.lua
1 --[[ sandbox.lua 2 Provides a setfenv preset intended to provide the maximum amount 3 of standard library functionality that does not allow for privesc. ]] 4 5 local util = bjvxlx.util 6 7 --[[ bjvxlx.util.make_sandbox_env(append) 8 Pulls in a nearly full-featured Lua 5.1 environment for bjvxlx.util.sandbox, 9 lacking only those features which would allow the environment to be breached. 10 If a table `append` is given, it is iterated, and every key-value pair within 11 is grafted into the environment ultimately constructed, possibly creating 12 privesc routes if `append` is not chosen carefully. Otherwise, only the names 13 shown below, in the function body, are provided. ]] 14 function util.make_sandbox_env(append) 15 local env = { 16 _VERSION = _VERSION, 17 assert = assert, 18 error = error, 19 getmetatable = getmetatable, 20 ipairs = ipairs, 21 load = load, 22 loadstring = loadstring, 23 next = next, 24 pairs = pairs, 25 pcall = pcall, 26 rawequal = rawequal, 27 rawget = rawget, 28 rawset = rawset, 29 select = select, 30 setmetatable = setmetatable, 31 tonumber = tonumber, 32 tostring = tostring, 33 type = type, 34 unpack = unpack, 35 xpcall = xpcall, 36 coroutine = { 37 create = coroutine.create, 38 resume = coroutine.resume, 39 running = coroutine.running, 40 status = coroutine.status, 41 wrap = coroutine.wrap, 42 yield = coroutine.yield 43 }, 44 string = { 45 byte = string.byte, 46 char = string.char, 47 dump = string.dump, 48 find = string.find, 49 format = string.format, 50 gmatch = string.gmatch, 51 gsub = string.gsub, 52 len = string.len, 53 lower = string.lower, 54 match = string.match, 55 rep = string.rep, 56 reverse = string.reverse, 57 sub = string.sub, 58 upper = string.upper 59 }, 60 table = { 61 concat = table.concat, 62 insert = table.insert, 63 maxn = table.maxn, 64 remove = table.remove, 65 sort = table.sort 66 }, 67 math = { 68 abs = math.abs, 69 acos = math.acos, 70 asin = math.asin, 71 atan = math.atan, 72 atan2 = math.atan2, 73 ceil = math.ceil, 74 cos = math.cos, 75 cosh = math.cosh, 76 deg = math.deg, 77 exp = math.exp, 78 floor = math.floor, 79 fmod = math.fmod, 80 frexp = math.frexp, 81 huge = math.huge, 82 ldexp = math.ldexp, 83 log = math.log, 84 log10 = math.log10, 85 max = math.max, 86 min = math.min, 87 modf = math.modf, 88 pi = math.pi, 89 pow = math.pow, 90 rad = math.rad, 91 random = math.random, 92 randomseed = math.randomseed, 93 sin = math.sin, 94 sinh = math.sinh, 95 sqrt = math.sqrt, 96 tan = math.tan, 97 tanh = math.tanh 98 } 99 } 100 if append then 101 for k, v in pairs(append) do 102 env[k] = v 103 end 104 end 105 env._G = env 106 return env 107 end 108 109 --[[ bjvxlx.util.sandbox(func, env) 110 Essentially composes `coroutine.create` over `setfenv`, so the guest function 111 can't breach its environment with `coroutine.yield` or `error`. 112 A bit more plumbing is added so that the value ultimately returned 113 is a function, not a thread, in a manner similar to `coroutine.wrap`, 114 except the wrapper directly returns the results of `coroutine.resume` 115 instead of unwrapping the status boolean and rethrowing errors, 116 thus providing (or rather, simply not undoing) a `pcall`-like safety net, 117 and the function may be called even if the coroutine is already dead, 118 in which case the coroutine will be remade (so that in the expected case 119 that the untrusted function is supposed to just be a normal function 120 and never calls `coroutine.yield`, it can be called more than once 121 if needed). ]] 122 function util.sandbox(func, env) 123 local coro = coroutine.create(setfenv(func, env)) 124 return function (...) 125 if coroutine.status(coro) == 'dead' then 126 coro = coroutine.create(setfenv(func, env)) 127 end 128 return coroutine.resume(coro, ...) 129 end 130 end