-- example/simple-example.lua -- -- Sandbox (for) Untrusted Procedure Partitioning (in) Lua Engine -- -- Simple example -- -- Copyright 2012 Daniel Silverstone -- -- For licence terms, see COPYING -- supple = require'supple' luxio = require'luxio' -- Code string to run in the untrusted environment subcode = [[ local t = ... local tot = 0 for i = 1, #t do tot = tot + t[i]() end t.tot = tot return tot, -t ]] -- Generate a function to return 'n' local function give_n(n) return function() return n end end -- A useful table for the test local tab = { give_n(4), give_n(8), give_n(12), } -- A metatable which defines -tab to equal 'JEFF' local mt = { __unm = function () return "JEFF" end } setmetatable(tab, mt) local tests, passed = 0, 0 local function lprint(expect, result, ...) tests = tests + 1 luxio.write(1, ("[%d]\r"):format(tests)) local foo = {n=select("#",...),...} if expect and not result then print "Want OK, got FAIL" elseif (not expect) and result then print "Want FAIL, got OK" end if expect ~= result then for i = 1, foo.n do if type(foo[i]) == "table" then print("{") for k, v in pairs(foo[i]) do print("",k,v) end print("}") else print(foo[i]) end end print() else passed = passed + 1 end end local function lprint_ok(...) return lprint(true, ...) end local function lprint_fail(...) return lprint(false, ...) end -- Finally, run the subcode lprint_ok(supple.host.run(subcode, "@test-code", tab)) assert(tab.tot == 24) -- Now run a supple command which we expect to error out. lprint_fail(supple.host.run("unknown()", "@test-code")) -- And now, one where we pass an error from host to sandbox and back lprint_fail(supple.host.run("local f = ... f()", "@test-code", function() unknown() end)) -- And now, one where we pass an error from sandbox to host to sandbox and back lprint_fail(supple.host.run("local f = ... f(function() unknown() end)", "@test-code", function(ff) ff() end)) -- Next , a reasonable traceback via named functions on each end... local errsrc = [[ function raises() does_not_exist() end function passes() raises() end function chains(f) f(passes) end local callme = ... callme(chains) ]] function loopback(f) f(loopback) end lprint_fail(supple.host.run(errsrc, "@error-code", loopback)) -- Now we try the sandboxing limits local long_run = [[ local s = "" for i = 1, 100000 do s = s .. "FISHFISHFISHFISHFISH" end return #s ]] supple.host.set_limits { count = 100 } lprint_fail(supple.host.run(long_run, "@long-code")) supple.host.set_limits { memory = 1000 } lprint_fail(supple.host.run(long_run, "@big-code")) -- next we demonstrate that ipairs works on proxied tables local summing = [[ local t = ... local tot = 0 for i, v in ipairs(t) do tot = tot + v end return tot ]] lprint_ok(supple.host.run(summing, "@summing", { 10, 14, 3 })) -- next we demonstrate that next works on proxied tables local isempty = [[ local t = ... return next(t) == nil ]] lprint_ok(supple.host.run(isempty, "@isempty", {})) lprint_ok(supple.host.run(isempty, "@isempty", {"bar"})) -- And now that pairs works on proxied tables local keys = [[ local t = ... local ret = {} for k, v in pairs(t) do ret[#ret+1] = k end ret.ret = ret return ret ]] lprint_ok(supple.host.run(keys, "@keys", { foo="bar", baz="meta" })) -- Next demonstrate that using supple.host.loadstring works to allow -- globals set in the parent to be seen/changed local function loader(n) return supple.host.loadstring(([[return { print=print, name = %q }]]):format(n), "@" .. n) end local getname = [[ local f = ... local mod = (f("Jeff"))() return mod.print and "PRINT" or mod.name ]] -- Expect to see 'PRINT' lprint_ok(supple.host.run(getname, "@getname", loader)) -- Attempt to do a test which shows the same issue as seen with gitano-web local updates = { ["refs/heads/master"] = { oldsha = "0123456789abcdef0123456789abcdef01234567", newsha = "76543210fedcba9876543210fedcba9876543210", } } local update_code = [[ local repo, updates = ... local hadmaster = false local foundrefs = {} if updates["refs/heads/master"] then hadmaster = true end for ref, data in pairs(updates) do foundrefs[ref] = { data.oldsha, data.newsha } end return hadmaster, foundrefs ]] for i = 1, 1000 do supple.track.start() local ok, res = pcall(supple.host.run, update_code, "@update-hook", "REPO", updates) if not ok then print(res) print(supple.track.stop()) end lprint_ok(ok) lprint_ok(res) supple.track.stop() if passed ~= tests then os.exit(1) end end print(("[%d/%d OK]"):format(passed, tests)) -- Dump the final trace, just for edification --print(supple.track.stop())