Skip to content

Instantly share code, notes, and snippets.

@cdhowie
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cdhowie/9176507 to your computer and use it in GitHub Desktop.
Save cdhowie/9176507 to your computer and use it in GitHub Desktop.
do
-- Don't allow error() to be replaced out from underneath the security checks!
local real_error = error
do
local real_collectgarbage = collectgarbage
collectgarbage = function(oper)
if oper == 'collect' or oper == 'count' then
return real_collectgarbage(oper)
end
real_error(string.format('Security violation calling collectgarbage(): Operation %q not permitted.', oper), 2)
end
end
do
-- If no proxy is provided then permit access to the default print(). If the desire is to have print() be a no-op then the pre-sandbox code can set "printproxy = function() end".
if printproxy then
local real_printproxy = printproxy
local function print_base(is_write, ...)
-- Map each argument with tostring() and pass the result as a table to the proxy.
-- is_write allows the proxy to differentiate between print() and io.write(). This is important since in the Lua baselib, print() places tabs between arguments and a newline at the end, while io.write() does neither.
-- Note that we accept more than io.write() does, which only accepts strings and numbers. I'm fine with this.
local itemCount = select('#', ...)
local items = { is_write=is_write, n=itemCount }
for n=1, itemCount do
local e = select(n, ...)
table.insert(items, tostring(e))
end
return real_printproxy(items)
end
print = function(...)
return print_base(false, ...)
end
io.write = function(...)
return print_base(true, ...)
end
-- Make the proxy inaccessible directly.
printproxy = nil
end
end
do
-- Critical functions we need for the security check; don't allow it to be replaced.
local real_string_byte = string.byte
local real_string_len = string.len
local real_rawequal = rawequal
local function is_bytecode(str)
return real_string_byte(str, 1) == 27
end
local real_loadstring = loadstring
loadstring = function(str, chunkname)
if is_bytecode(str) then
real_error('Security violation calling loadstring(): Argument is Lua bytecode.', 2)
end
return real_loadstring(str, chunkname)
end
local real_load = load
load = function(func, chunkname)
local checked = false
local function delegate_func()
local piece = func()
if not checked and not rawequal(piece, nil) and real_string_len(piece) ~= 0 then
if is_bytecode(piece) then
real_error('Security violation calling load(): Argument is Lua bytecode.', 2)
end
checked = true
end
return piece
end
return real_load(delegate_func, chunkname)
end
end
do
local real_pcall = pcall
local real_string_find = string.find
local function test_result(flag, ...)
if not flag then
-- Test to see if this is a special uncatchable error.
local error_text = select(1, ...)
if real_string_find(error_text, '%$UNCATCHABLE%$') then
real_error(error_text, 0)
end
end
return flag, ...
end
local function handle_xpcall(err, flag, ...)
if not flag then
return false, err(...)
end
return true, ...
end
pcall = function(...)
return test_result(real_pcall(...))
end
xpcall = function(f, err)
return handle_xpcall(err, test_result(real_pcall(f)))
end
end
end
dofile = nil -- IO
loadfile = nil -- IO
-- These are theoretically fine since we sandbox the global environment. They can be permitted if there is a demonstrable need.
getfenv = nil
setfenv = nil
-- These could be made to be relatively secure, but it's more work than necessary for our needs.
module = nil
require = nil
package = nil
-- This could theoretically be used to examine the implementation of protected functions.
string.dump = nil
io = { write=io.write }
-- Allows too much access to the host.
debug = nil
newproxy = nil
os = {
clock = os.clock,
difftime = os.difftime,
time = os.time
}
-- Note that we do permit get/setmetatable, raw*, and we don't protect public tables (like math) since the Lua environment is not shared between scripts with different authors. An author can shoot himself in the foot, but no-one else.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment