-
Notifications
You must be signed in to change notification settings - Fork 0
/
utils.lua
276 lines (240 loc) · 9.91 KB
/
utils.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
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
272
273
274
275
276
function libox.get_default_hook(max_time)
local time = minetest.get_us_time
local start_time = time()
return function()
if time() - start_time > max_time then
debug.sethook()
error("Code timed out! Reason: Time limit exceeded, the limit:" ..
tostring(max_time / 1000) .. "ms, the program took:" .. ((time() - start_time) / 1000), 2)
end
end
end
-- luacheck: push ignore
--[[
PATH SHORTENING: from dbg
Path shortening is licensed under the MIT license:
Copyright 2022 Lars Müller
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]
-- luacheck: pop ignore
local modpath_trie = {}
for _, modname in pairs(minetest.get_modnames()) do
local path = minetest.get_modpath(modname)
local subtrie = modpath_trie
for char in path:gmatch "." do
subtrie[char] = subtrie[char] or {}
subtrie = subtrie[char]
end
subtrie["\\"] = modname
subtrie["/"] = modname
end
function libox.shorten_path(path)
-- Search for a prefix (paths have at most one prefix)
local subtrie = modpath_trie
for i = 1, #path do
if type(subtrie) == "string" then
return subtrie .. ":" .. path:sub(i)
end
subtrie = subtrie[path:sub(i, i)]
if not subtrie then return path end
end
return path
end
-- PATH SHORTENING END, rest is licensed as usual
if minetest.global_exists("dbg") then
libox.shorten_path = dbg.shorten_path
end
local TRACEBACK_LIMIT = 20
function libox.traceback(errmsg)
errmsg = tostring(errmsg) or ""
local traceback = "Traceback: " .. "\n"
local level = 1
while level < TRACEBACK_LIMIT do
local info = debug.getinfo(level, "nlS") -- can be quite slow actually, thats why TRACEBACK_LIMIT is in place
if not info then break end
local name = info.name
local text
if name ~= nil then
text = "In function " .. name
else
text = "In " .. info.what
end
if info.source == "=(load)" then
traceback = traceback .. text .. " at line " .. info.currentline .. "\n"
end
level = level + 1
end
if level == TRACEBACK_LIMIT then traceback = traceback .. "\n... and more" end
return libox.shorten_path(errmsg) .. "\n" .. traceback
end
-- dont rely on this to sethook and
function libox.unsafe_traceback(errmsg)
debug.sethook()
getmetatable("").__index = string
return libox.traceback(errmsg)
end
function libox.digiline_sanitize(input, allow_functions, wrap)
--[[
Parameters:
1) input: the thing
2) allow_functions: true/false, explains itself
3) wrap: function, the function that wraps around the functions in this table
]]
wrap = wrap or function(f) return f end
local function clean_and_weigh_digiline_message(msg, back_references)
local t = type(msg)
if t == "string" then
-- Strings are immutable so can be passed by reference, and cost their
-- length plus the size of the Lua object header (24 bytes on a 64-bit
-- platform) plus one byte for the NUL terminator.
return msg, #msg + 25
elseif t == "number" then
-- Numbers are passed by value so need not be touched, and cost 8 bytes
-- as all numbers in Lua are doubles. NaN values are removed.
if msg ~= msg then
return nil, 0
end
return msg, 8
elseif t == "boolean" then
-- Booleans are passed by value so need not be touched, and cost 1
-- byte.
return msg, 1
elseif t == "table" then
-- Tables are duplicated. Check if this table has been seen before
-- (self-referential or shared table); if so, reuse the cleaned value
-- of the previous occurrence, maintaining table topology and avoiding
-- infinite recursion, and charge zero bytes for this as the object has
-- already been counted.
back_references = back_references or {}
local bref = back_references[msg]
if bref then
return bref, 0
end
-- Construct a new table by cleaning all the keys and values and adding
-- up their costs, plus 8 bytes as a rough estimate of table overhead.
local cost = 8
local ret = {}
back_references[msg] = ret
for k, v in pairs(msg) do
local k_cost, v_cost
k, k_cost = clean_and_weigh_digiline_message(k, back_references)
v, v_cost = clean_and_weigh_digiline_message(v, back_references)
if k ~= nil and v ~= nil then
-- Only include an element if its key and value are of legal
-- types.
ret[k] = v
end
-- If we only counted the cost of a table element when we actually
-- used it, we would be vulnerable to the following attack:
-- 1. Construct a huge table (too large to pass the cost limit).
-- 2. Insert it somewhere in a table, with a function as a key.
-- 3. Insert it somewhere in another table, with a number as a key.
-- 4. The first occurrence doesn’t pay the cost because functions
-- are stripped and therefore the element is dropped.
-- 5. The second occurrence doesn’t pay the cost because it’s in
-- back_references.
-- By counting the costs regardless of whether the objects will be
-- included, we avoid this attack; it may overestimate the cost of
-- some messages, but only those that won’t be delivered intact
-- anyway because they contain illegal object types.
cost = cost + k_cost + v_cost
end
return ret, cost
elseif t == "function" and allow_functions then
local success, bytecode = pcall(function()
return string.dump(msg)
end)
if not success then
return nil, 0
else
return wrap(msg), #bytecode + 25
end
else
return nil, 0
end
end
local msg, cost = clean_and_weigh_digiline_message(input)
if not msg then
return nil, 0
else
return msg, cost
end
end
function libox.sandbox_lib_f(f, opt_str_limit)
--[[
Sandbox external functions, call this on functions that
- don't run user code
- use "":sub(1, 2, whatever) syntax and that syntax is critical or use pcall or xpcall
- get laggy when supplied with gigantic sting inputs
]]
return function(...)
local args = { ... }
for _, v in pairs(args) do
if type(v) == "string" and #v > (opt_str_limit or 64000) then error("String too large", 2) end
end
local string_meta = getmetatable("")
local sandbox = string_meta.__index
string_meta.__index = string
local retvalue = { f(...) }
string_meta.__index = sandbox
if not debug.gethook() then
error("Code timed out! (Reason: external function erased the debug hook)", 2)
end
return unpack(retvalue)
end
end
libox.safe_traceback = libox.sandbox_lib_f(libox.safe_traceback) -- make it work
-- strict type checking
--[[
thing: any
type_check: {
key = function | table
} | function
note: recursive
returns: boolean
]]
function libox.type_check(initial_thing, initial_check)
local function internal(thing, check, seen)
if seen[thing] == true then return true end
if type(check) == "function" then
seen[thing] = true
return check(thing)
end
for k, v in pairs(check) do
if thing[k] == nil then
if v(nil) == false then return false, k end
elseif type(v) == "table" then
if (internal(thing[k], v, seen)) == false then return false, k end
seen[thing[k]] = true
elseif type(v) == "function" then
if v(thing[k]) == false then return false, k end
seen[thing[k]] = true
else -- bad, just return false
return false, "invalid params to initial_check probably"
end
end
for k, _ in pairs(thing) do
if check[k] == nil then return false, "un-needed: " .. k end
end
return true
end
return internal(initial_thing, initial_check, {})
end
function libox.type(x)
return function(something)
return type(something) == x
end
end
function libox.type_vector(vec)
if type(vec) ~= "table" then return false end
if type(vec.x) ~= "number" then return false end
if type(vec.y) ~= "number" then return false end
if type(vec.z) ~= "number" then return false end
return true
end