Add a new Sandbox Lua module to the luaintf framework.
[vlc.git] / share / luaintf / modules / sandbox.lua
1 --[==========================================================================[\r
2  sandbox.lua: Lua sandboxing facilities\r
3 --[==========================================================================[\r
4  Copyright (C) 2007 the VideoLAN team\r
5  $Id$\r
6 \r
7  Authors: Antoine Cellerier <dionoea at videolan dot org>\r
8 \r
9  This program is free software; you can redistribute it and/or modify\r
10  it under the terms of the GNU General Public License as published by\r
11  the Free Software Foundation; either version 2 of the License, or\r
12  (at your option) any later version.\r
13 \r
14  This program is distributed in the hope that it will be useful,\r
15  but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
17  GNU General Public License for more details.\r
18 \r
19  You should have received a copy of the GNU General Public License\r
20  along with this program; if not, write to the Free Software\r
21  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.\r
22 --]==========================================================================]\r
23 \r
24 module("sandbox",package.seeall)\r
25 \r
26 -- See Programming in Lua (second edition) for sandbox examples\r
27 -- See http://lua-users.org/wiki/SandBoxes for a list of SAFE/UNSAFE variables\r
28 \r
29 local sandbox_blacklist = {\r
30     collectgarbage = true,\r
31     dofile = true,\r
32     _G = true,\r
33     getfenv = true,\r
34     getmetatable = true,\r
35     load = true, -- Can be protected I guess\r
36     loadfile = true, -- Can be protected I guess\r
37     loadstring = true, -- Can be protected I guess\r
38     rawequal = true,\r
39     rawget = true,\r
40     rawset = true,\r
41     setfenv = true,\r
42     setmetatable = true,\r
43     module = true,\r
44     require = true,\r
45     package = true,\r
46     debug = true,\r
47 }\r
48 \r
49 function readonly_table_proxy(name,src,blacklist)\r
50     if type(src)=="nil" then return end\r
51     if type(src)~="table" then error("2nd argument must be a table (or nil)") end\r
52     local name = name\r
53     local t = src\r
54     local blist = {}\r
55     if blacklist then\r
56         for _, v in pairs(blacklist) do\r
57             blist[v] = true\r
58         end\r
59     end\r
60     local metatable_readonly = {\r
61         __index = function(self,key)\r
62             if blist[key] then\r
63                 error("Sandbox: Access to `"..name.."."..key.."' is forbidden.")\r
64             end\r
65             return t[key]\r
66         end,\r
67         __newindex = function(self,key,value)\r
68             error("It is forbidden to modify elements of this table.")\r
69         end,\r
70     }\r
71     return setmetatable({},metatable_readonly)\r
72 end\r
73 \r
74 -- Of course, all of this is useless if the sandbox calling code has\r
75 -- another reference to one of these tables in his global environement.\r
76 local sandbox_proxy = {\r
77     coroutine   = readonly_table_proxy("coroutine",coroutine),\r
78     string      = readonly_table_proxy("string",string,{"dump"}),\r
79     table       = readonly_table_proxy("table",table),\r
80     math        = readonly_table_proxy("math",math),\r
81     io          = readonly_table_proxy("io",io),\r
82     os          = readonly_table_proxy("os",os,{"exit","getenv","remove",\r
83                                                 "rename","setlocale"}),\r
84     sandbox     = readonly_table_proxy("sandbox",sandbox),\r
85 }\r
86 \r
87 function sandbox(func,override)\r
88     local _G = getfenv(2)\r
89     local override = override or {}\r
90     local sandbox_metatable =\r
91     {\r
92         __index = function(self,key)\r
93             if override[key] then\r
94                 return override[key]\r
95             end\r
96             if sandbox_blacklist[key] then\r
97                 error( "Sandbox: Access to `"..key.."' is forbidden." )\r
98             end\r
99             --print(key,"not found in env. Looking in sandbox_proxy and _G")\r
100             local value = sandbox_proxy[key] or _G[key]\r
101             rawset(self,key,value) -- Keep a local copy\r
102             return value\r
103         end,\r
104         __newindex = function(self,key,value)\r
105             if override and override[key] then\r
106                 error( "Sandbox: Variable `"..key.."' is read only." )\r
107             end\r
108             return rawset(self,key,value)\r
109         end,\r
110     }\r
111     local sandbox_env = setmetatable({},sandbox_metatable)\r
112     return function(...)\r
113         setfenv(func,sandbox_env)\r
114         local ret = {func(...)} -- Not perfect (if func returns nil before\r
115                                 -- another return value) ... but it's better\r
116                                 -- than nothing\r
117         setfenv(func,_G)\r
118         return unpack(ret)\r
119     end\r
120 end