-
Notifications
You must be signed in to change notification settings - Fork 7
/
samplesandboxed.lua
114 lines (101 loc) · 3.99 KB
/
samplesandboxed.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
-- Illustration of simple sandboxing techniques that can be used in luaj.
--
-- This sandboxing is done in lua. These same techniques are all
-- possible directly from Java, as shown in /examples/jse/SampleSandboxed.java.
--
-- The main goals of this sandbox are:
-- * lightweight sandbox controlled by single lua script
-- * use new globals per-script and leave out dangerous libraries
-- * use debug hook functions with yield to limit lua scripts
-- * use read-only tables to protect shared metatables
-- Replace the string metatable with a read-only version.
debug.setmetatable('', {
__index = string,
__newindex = function() error('table is read only') end,
__metatable = false,
})
-- Duplicate contents of a table.
local function dup(table)
local t = {}
for k,v in pairs(table) do t[k] = v end
return t
end
-- Produce a new user environment.
-- Only a subset of functionality is exposed.
-- Must not expose debug, luajava, or other easily abused functions.
local function new_user_globals()
local g = {
print = print,
pcall = pcall,
xpcall = xpcall,
pairs = pairs,
ipairs = ipairs,
getmetatable = getmetatable,
setmetatable = setmetatable,
load = load,
package = { preload = {}, loaded = {}, },
table = dup(table),
string = dup(string),
math = dup(math),
bit32 = dup(bit32),
-- functions can also be customized here
}
g._G = g
return g
end
-- Run a script in it's own user environment,
-- and limit it to a certain number of cycles before abandoning it.
local function run_user_script_in_sandbox(script)
do
-- load the chunk using the main globals for the environment
-- initially so debug hooks will be usable in these threads.
local chunk, err = _G.load(script, 'main', 't')
if not chunk then
print('error loading', err, script)
return
end
-- set the user environment to user-specific globals.
-- these must not contain debug, luajava, coroutines, or other
-- dangerous functionality.
local user_globals = new_user_globals()
debug.setupvalue(chunk, 1, user_globals)
-- run the thread for a specific number of cycles.
-- when it yields out, abandon it.
local thread = coroutine.create(chunk)
local hook = function() coroutine.yield('resource used too many cycles') end
debug.sethook(thread, hook, '', 40)
local errhook = function(msg) print("in error hook", msg); return msg; end
print(script, xpcall(coroutine.resume, errhook, thread))
end
-- run garbage collection to clean up orphaned threads
collectgarbage()
end
-- Tun various test scripts that should succeed.
run_user_script_in_sandbox( "return 'foo'" )
run_user_script_in_sandbox( "return ('abc'):len()" )
run_user_script_in_sandbox( "return getmetatable('abc')" )
run_user_script_in_sandbox( "return getmetatable('abc').len" )
run_user_script_in_sandbox( "return getmetatable('abc').__index" )
-- Example user scripts that attempt rogue operations, and will fail.
run_user_script_in_sandbox( "return setmetatable('abc', {})" )
run_user_script_in_sandbox( "getmetatable('abc').len = function() end" )
run_user_script_in_sandbox( "getmetatable('abc').__index = {}" )
run_user_script_in_sandbox( "getmetatable('abc').__index.x = 1" )
run_user_script_in_sandbox( "while true do print('loop') end" )
-- Example use of other shared metatables, which should also be made read-only.
-- this toy example allows booleans to be added to numbers.
-- Normally boolean cannot participate in arithmetic.
local number_script = "return 2 + 7, 2 + true, false + 7"
run_user_script_in_sandbox(number_script)
-- Create a shared metatable that includes addition for booleans.
-- This would only be set up by the server, not by client scripts.
debug.setmetatable(true, {
__newindex = function() error('table is read only') end,
__metatable = false,
__add = function(a, b)
return (a == true and 1 or a == false and 0 or a) +
(b == true and 1 or b == false and 0 or b)
end
})
-- All user scripts will now get addition involving booleans.
run_user_script_in_sandbox(number_script)