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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
|
if not modules then modules = { } end modules ['l-sandbox'] = {
version = 1.001,
comment = "companion to luat-lib.mkiv",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
-- We use string instead of function variables, so 'io.open' instead of io.open. That
-- way we can still intercept repetetive overloads. One complication is that when we use
-- sandboxed function sin helpers in the sanbox checkers, we can get a recursion loop
-- so for that vreason we need to keep originals around till we enable the sandbox.
-- if sandbox then return end
local global = _G
local next = next
local unpack = unpack or table.unpack
local type = type
local tprint = texio.write_nl or print
local tostring = tostring
local format = string.format -- no formatters yet
local concat = table.concat
local sort = table.sort
local gmatch = string.gmatch
sandbox = { }
local sandboxed = false
local overloads = { }
local skiploads = { }
local initializers = { }
local finalizers = { }
local originals = { }
local comments = { }
local trace = false
local logger = false
-- this comes real early, so that we can still alias
local function report(...)
tprint("sandbox ! " .. format(...)) -- poor mans tracer
end
sandbox.report = report
function sandbox.setreporter(r)
report = r
sandbox.report = r
end
function sandbox.settrace(v)
trace = v
end
function sandbox.setlogger(l)
logger = type(l) == "function" and l or false
end
local function register(func,overload,comment)
if type(func) == "function" then
if type(overload) == "string" then
comment = overload
overload = nil
end
local function f(...)
if sandboxed then
local overload = overloads[f]
if overload then
if logger then
local result = { overload(func,...) }
logger {
comment = comments[f] or tostring(f),
arguments = { ... },
result = result[1] and true or false,
}
return unpack(result)
else
return overload(func,...)
end
else
-- ignored, maybe message
end
else
return func(...)
end
end
if comment then
comments[f] = comment
if trace then
report("registering function: %s",comment)
end
end
overloads[f] = overload or false
originals[f] = func
return f
end
end
local function redefine(func,comment)
if type(func) == "function" then
skiploads[func] = comment or comments[func] or "unknown"
if overloads[func] == false then
overloads[func] = nil -- not initialized anyway
end
end
end
sandbox.register = register
sandbox.redefine = redefine
function sandbox.original(func)
return originals and originals[func] or func
end
function sandbox.overload(func,overload,comment)
comment = comment or comments[func] or "?"
if type(func) ~= "function" then
if trace then
report("overloading unknown function: %s",comment)
end
elseif type(overload) ~= "function" then
if trace then
report("overloading function with bad overload: %s",comment)
end
elseif overloads[func] == nil then
if trace then
report("function is not registered: %s",comment)
end
elseif skiploads[func] then
if trace then
report("function is not skipped: %s",comment)
end
else
if trace then
report("overloading function: %s",comment)
end
overloads[func] = overload
end
return func
end
function sandbox.initializer(f)
if not sandboxed then
initializers[#initializers+1] = f
elseif trace then
report("already enabled, discarding initializer")
end
end
function sandbox.finalizer(f)
if not sandboxed then
finalizers[#finalizers+1] = f
elseif trace then
report("already enabled, discarding finalizer")
end
end
function sandbox.enable()
if not sandboxed then
for i=1,#initializers do
initializers[i]()
end
for i=1,#finalizers do
finalizers[i]()
end
local nnot = 0
local nyes = 0
local cnot = { }
local cyes = { }
local skip = { }
for k, v in next, overloads do
local c = comments[k]
if v then
if c then
cyes[#cyes+1] = c
else -- if not skiploads[k] then
nyes = nyes + 1
end
else
if c then
cnot[#cnot+1] = c
else -- if not skiploads[k] then
nnot = nnot + 1
end
end
end
for k, v in next, skiploads do
skip[#skip+1] = v
end
if #cyes > 0 then
sort(cyes)
report(" overloaded known : %s",concat(cyes," | "))
end
if nyes > 0 then
report(" overloaded unknown : %s",nyes)
end
if #cnot > 0 then
sort(cnot)
report("not overloaded known : %s",concat(cnot," | "))
end
if nnot > 0 then
report("not overloaded unknown : %s",nnot)
end
if #skip > 0 then
sort(skip)
report("not overloaded redefined : %s",concat(skip," | "))
end
initializers = nil
finalizers = nil
originals = nil
sandboxed = true
end
end
-- we sandbox some of the built-in functions now:
-- todo: require
-- todo: load
local function supported(library)
local l = _G[library]
-- if l then
-- for k, v in next, l do
-- report("%s.%s",library,k)
-- end
-- end
return l
end
-- io.tmpfile : we don't know where that one ends up but probably is user temp space
-- os.tmpname : no need to deal with this: outputs rubish anyway (\s9v0. \s9v0.1 \s9v0.2 etc)
-- os.tmpdir : not needed either (luatex.vob000 luatex.vob000 etc)
-- os.setenv : maybe
-- require : maybe (normally taken from tree)
-- http etc : maybe (all schemes that go outside)
loadfile = register(loadfile,"loadfile")
if supported("io") then
io.open = register(io.open, "io.open")
io.popen = register(io.popen, "io.popen") -- needs checking
io.lines = register(io.lines, "io.lines")
io.output = register(io.output, "io.output")
io.input = register(io.input, "io.input")
end
if supported("os") then
os.execute = register(os.execute, "os.execute")
os.spawn = register(os.spawn, "os.spawn")
os.exec = register(os.exec, "os.exec")
os.rename = register(os.rename, "os.rename")
os.remove = register(os.remove, "os.remove")
end
if supported("lfs") then
lfs.chdir = register(lfs.chdir, "lfs.chdir")
lfs.mkdir = register(lfs.mkdir, "lfs.mkdir")
lfs.rmdir = register(lfs.rmdir, "lfs.rmdir")
lfs.isfile = register(lfs.isfile, "lfs.isfile")
lfs.isdir = register(lfs.isdir, "lfs.isdir")
lfs.attributes = register(lfs.attributes, "lfs.attributes")
lfs.dir = register(lfs.dir, "lfs.dir")
lfs.lock_dir = register(lfs.lock_dir, "lfs.lock_dir")
lfs.touch = register(lfs.touch, "lfs.touch")
lfs.link = register(lfs.link, "lfs.link")
lfs.setmode = register(lfs.setmode, "lfs.setmode")
lfs.readlink = register(lfs.readlink, "lfs.readlink")
lfs.shortname = register(lfs.shortname, "lfs.shortname")
lfs.symlinkattributes = register(lfs.symlinkattributes,"lfs.symlinkattributes")
end
|