Last active
August 29, 2015 13:56
-
-
Save cdhowie/9176507 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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