aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/factoids/sandbox.lua
blob: a9275350c2d87a8f99f1375dad65ab26b936b78a (plain) (blame)
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
function send(text)
  local text = tostring(text)
  local len = #output
  if len < 1 then
    output = { text }
  else
    output[len] = output[len] .. text
  end
end

function sendln(text)
  send(text)
  table.insert(output, "")
end

function trim(s)
  local from = s:match"^%s*()"
  return from > #s and "" or s:match(".*%S", from)
end

trimmedInput = trim(input)

if trimmedInput == "" then
  ioru = user
else
  ioru = trimmedInput
end

local sandbox_env = {
  print = send,
  println = sendln,
  trim = trim,
  eval = nil,
  sleep = nil,
  json = {decode = json_decode},
  args = args,
  input = input,
  user = user,
  ioru = ioru,
  channel = channel,
  request = download,
  string = string,
  math = math,
  table = table,
  pairs = pairs,
  ipairs = ipairs,
  next = next,
  select = select,
  unpack = unpack,
  tostring = tostring,
  tonumber = tonumber,
  type = type,
  assert = assert,
  error = error,
  pcall = pcall,
  xpcall = xpcall,
  _VERSION = _VERSION
}

sandbox_env.os = {
  clock = os.clock,
  time = os.time,
  difftime = os.difftime
}

sandbox_env.string.rep = nil
sandbox_env.string.dump = nil
sandbox_env.math.randomseed = nil

-- Temporary evaluation function
function eval(code)
  local c, e = load(code, nil, nil, sandbox_env)
  if c then
    return c()
  else
    error(e)
  end
end

-- Only sleeps for 1 second at a time
-- This ensures that the timeout check can still run
function safesleep(dur)
  while dur > 1000 do
    dur = dur - 1000
    sleep(1000)
  end
  sleep(dur)
end

sandbox_env.eval = eval
sandbox_env.sleep = safesleep

-- Check if the factoid timed out
function checktime()
  if os.time() - time >= timeout then
    error("Timed out after " .. timeout .. " seconds", 0)
  else
    -- Limit the cpu usage of factoids
    sleep(1)
  end
end

-- Check if the factoid uses too much memory
function checkmem()
  if collectgarbage("count") > maxmem then
    error("Factoid used over " .. maxmem .. " kbyte of ram")
  end
end

local f, e = load(factoid, nil, nil, sandbox_env)

-- Add timeout hook
time = os.time()
-- The timeout is defined in seconds
timeout = 30
debug.sethook(checktime, "l")
-- Add memory check hook
-- The max memory is defined in kilobytes
maxmem = 1000
debug.sethook(checkmem, "l")

if f then
  f()
else
  error(e)
end