– Lua parser extensions for MediaWiki - Wrapper for Lua interpreter – © 2008 Fran Rogers - see 'COPYING' for license
– Creates a new sandbox environment for scripts to safely run in. function make_sandbox()
-- Dummy function that returns nil, to quietly replace unsafe functions
local function dummy(...)
return nil
end
-- Deep-copy an object; optionally replace all its leaf members with the
-- value 'override' if it's non-nil
local function deepcopy(object, override)
local lookup_table = {}
local function _copy(object, override)
if type(object) ~= "table" then
return object
elseif lookup_table[object] then
return lookup_table[object]
end
local new_table = {}
lookup_table[object] = new_table
for index, value in pairs(object) do
if override ~= nil then
value = override
end
new_table[_copy(index)] = _copy(value, override)
end
return setmetatable(new_table, _copy(getmetatable(object), override))
end
return _copy(object, override)
end
-- Our new environment
local env = {}
-- "_OUTPUT" will accumulate the results of print() and friends
env._OUTPUT = ""
-- _OUTPUT wrapper for io.write()
local function writewrapper(...)
local out = ""
for n = 1, select("#", ...) do
if out == "" then
out = tostring(select(n, ...))
else
out = out .. tostring(select(n, ...))
end
end
env._OUTPUT = env._OUTPUT .. out
end
-- _OUTPUT wrapper for io.stdout:output()
local function outputwrapper(file)
if file == nil then
local file = {}
file.close = dummy
file.lines = dummy
file.read = dummy
file.flush = dummy
file.seek = dummy
file.setvbuf = dummy
function file:write(...) writewrapper(...); end
return file
else
return nil
end
end
-- _OUTPUT wrapper for print()
local function printwrapper(...)
local out = ""
for n = 1, select("#", ...) do
if out == "" then
out = tostring(select(n, ...))
else
out = out .. '\t' .. tostring(select(n, ...))
end
end
env._OUTPUT =env._OUTPUT .. out .. "\n"
end
-- Safe wrapper for loadstring()
local oldloadstring = loadstring
local function safeloadstring(s, chunkname)
local f, message = oldloadstring(s, chunkname)
if not f then
return f, message
end
setfenv(f, getfenv(2))
return f
end
-- Populate the sandbox environment
env.assert = _G.assert
env.error = _G.error
env._G = env
env.ipairs = _G.ipairs
env.loadstring = safeloadstring
env.next = _G.next
env.pairs = _G.pairs
env.pcall = _G.pcall
env.print = printwrapper
env.write = writewrapper
env.select = _G.select
env.tonumber = _G.tonumber
env.tostring = _G.tostring
env.type = _G.type
env.unpack = _G.unpack
env._VERSION = _G._VERSION
env.xpcall = _G.xpcall
env.coroutine = deepcopy(_G.coroutine)
env.string = deepcopy(_G.string)
env.string.dump = nil
env.table = deepcopy(_G.table)
env.math = deepcopy(_G.math)
env.io = {}
env.io.write = writewrapper
env.io.flush = dummy
env.io.type = typewrapper
env.io.output = outputwrapper
env.io.stdout = outputwrapper()
env.os = {}
env.os.clock = _G.os.clock
-- env.os.date = _G.os.date
env.os.difftime = _G.os.difftime
env.os.time = _G.os.time
-- Return the new sandbox environment
return env
end
– Creates a new debug hook that aborts with 'error(“LOC_LIMIT”)' after – 'maxlines' lines have been passed, or 'error(“RECURSION_LIMIT”)' after – 'maxcalls' levels of recursion have been entered. function make_hook(maxlines, maxcalls, diefunc)
local lines = 0
local calls = 0
function _hook(event, ...)
if event == "line" then
lines = lines + 1
if lines > maxlines then
error("LOC_LIMIT")
end
elseif event == "call" then
calls = calls + 1
if calls > maxcalls then
error("RECURSION_LIMIT")
end
elseif event == "return" then
calls = calls - 1
end
end
return _hook
end
– Creates and returns a function, 'wrap(input)', which reads a string into – a Lua chunk and executes it in a persistent sandbox environment, returning – 'output, err' where 'output' is the combined output of print() and friends – from within the chunk and 'err' is either nil or an error incurred while – executing the chunk; or halting after 'maxlines' lines or 'maxcalls' levels – of recursion. function make_wrapper(maxlines, maxcalls)
-- Create the debug hook and sandbox environment.
local hook = make_hook(maxlines, maxcalls)
local env = make_sandbox()
-- The actual 'wrap()' function.
-- All of the above variables will be bound in its closure.
function _wrap(chunkstr)
local chunk, err, done
-- Clear any leftover output from the last call
env._OUTPUT = ""
err = nil
-- Load the string into a chunk; fail on error
chunk, err = loadstring(chunkstr)
if err ~= nil then
return nil, err
end
-- Set the chunk's environment, enable the debug hook, and execute it
setfenv(chunk, env)
co = coroutine.create(chunk)
debug.sethook(co, hook, "crl")
done, err = coroutine.resume(co)
if done == true then
err = nil
end
-- Collect and return the results
return env._OUTPUT, err
end
return _wrap
end
– Listen on stdin for Lua chunks, parse and execute them, and print the – results of each on stdout. function main(arg)
if #arg ~= 2 then
io.stderr:write(string.format("usage: %s MAXLINES MAXCALLS\n", arg[0]))
os.exit(1)
end
-- Create a wrapper function, wrap()
local wrap = make_wrapper(tonumber(arg[1]), tonumber(arg[2]))
-- Turn off buffering, and loop through the input
io.stdout:setvbuf("no")
while true do
-- Read in a chunk
local chunkstr = ""
while true do
local line = io.stdin:read("*l")
if chunkstr == "" and line == nil then
-- On EOF, exit.
os.exit(0)
elseif line == "." or line == nil then
-- Finished this chunk; move on to the next step
break
elseif chunkstr ~= "" then
chunkstr = chunkstr .. "\n" .. line
else
chunkstr = line
end
end
-- Parse and execute the chunk
local res, err
res, err = wrap(chunkstr, env, hook)
-- Write out the results
if err == nil then
io.stdout:write("'", res, "', true\n.\n")
else
io.stdout:write("'", err, "', false\n.\n")
end
end
end
– If called as a script instead of imported as a library, run main(). if arg ~= nil then
main(arg)
end