blob: 52c5eea34594722802ab91a0fc24617d7f80d110 [file] [log] [blame]
Guido van Rossum9a22de11995-01-12 12:29:47 +00001# Implement restricted execution of Python code
2
3import __builtin__
Guido van Rossumb4728151995-06-22 18:55:10 +00004import imp
Guido van Rossum9a22de11995-01-12 12:29:47 +00005import os
6import sys
7import types
8
9def trace(fmt, *args):
10 if 0:
11 sys.stderr.write(fmt % args + '\n')
12
13def copydict(src, dst, exceptions = [], only = None):
14 if only is None:
15 for key in src.keys():
16 if key not in exceptions:
17 dst[key] = src[key]
18 else:
19 for key in only:
20 dst[key] = src[key]
21
22def copymodule(src, dst, exceptions = [], only = None):
23 copydict(src.__dict__, dst.__dict__, exceptions, only)
24
Guido van Rossumb4728151995-06-22 18:55:10 +000025safe_path = ['/usr/local/lib/python']
Guido van Rossum9a22de11995-01-12 12:29:47 +000026safe_modules = ['array', 'math', 'regex', 'strop', 'time']
27unsafe_builtin_names = ['open', 'reload', '__import__',
Guido van Rossum9a22de11995-01-12 12:29:47 +000028 'raw_input', 'input']
29safe_posix_names = ['error', 'fstat', 'listdir', 'lstat', 'readlink', 'stat',
30 'times', 'uname', 'getpid', 'getppid', 'getcwd',
31 'getuid', 'getgid', 'geteuid', 'getegid']
32
Guido van Rossumb4728151995-06-22 18:55:10 +000033safe_sys = imp.new_module('sys')
Guido van Rossum9a22de11995-01-12 12:29:47 +000034safe_sys.modules = {}
35safe_sys.modules['sys'] = safe_sys
36safe_sys.path = safe_path[:]
37safe_sys.argv = ['-']
38safe_sys.builtin_module_names = safe_modules[:] + ['posix']
39safe_sys.builtin_module_names.sort()
40safe_sys.copyright = sys.copyright
41safe_sys.version = sys.version + ' [restricted mode]'
42safe_sys.exit = sys.exit
43
44def new_module(name):
Guido van Rossumb4728151995-06-22 18:55:10 +000045 safe_sys.modules[name] = m = imp.new_module(name)
Guido van Rossum9a22de11995-01-12 12:29:47 +000046 return m
47
48safe_builtin = new_module('__builtin__')
49copymodule(__builtin__, safe_builtin, unsafe_builtin_names)
50
51safe_main = new_module('__main__')
52
53safe_posix = new_module('posix')
54import posix
55copymodule(posix, safe_posix, None, safe_posix_names)
56safe_posix.environ = {}
57copydict(posix.environ, safe_posix.environ)
58
59safe_types = new_module('types')
60copymodule(types, safe_types)
61
Guido van Rossumb5f94601995-03-02 15:30:15 +000062def safe_import(name, globals=None, locals=None, fromlist=None):
63 if '.' in name:
64 raise ImportError, "import of dotted names not supported"
Guido van Rossum9a22de11995-01-12 12:29:47 +000065 if safe_sys.modules.has_key(name):
66 return safe_sys.modules[name]
67 if name in safe_modules:
68 temp = {}
69 exec "import "+name in temp
70 m = new_module(name)
71 copymodule(temp[name], m)
72 return m
73 for dirname in safe_path:
74 filename = os.path.join(dirname, name + '.py')
75 try:
76 f = open(filename, 'r')
77 f.close()
78 except IOError:
79 continue
80 m = new_module(name)
81 rexecfile(filename, m.__dict__)
82 return m
83 raise ImportError, name
84safe_builtin.__import__ = safe_import
85
86def safe_open(file, mode = 'r'):
87 if type(file) != types.StringType or type(mode) != types.StringType:
88 raise TypeError, 'open argument(s) must be string(s)'
89 if mode not in ('r', 'rb'):
90 raise IOError, 'open for writing not allowed'
Guido van Rossum8e8a5251995-01-17 15:58:37 +000091 file = os.path.join(os.getcwd(), file)
92 file = os.path.normpath(file)
93 if file[:2] == '//' or file[:5] == '/etc/' or file[:4] == '/../':
94 raise IOError, 'this path not allowed for reading'
Guido van Rossum9a22de11995-01-12 12:29:47 +000095 return open(file, mode)
96safe_builtin.open = safe_open
97
Guido van Rossum9a22de11995-01-12 12:29:47 +000098
99def exterior():
100 """Return env of caller's caller, as triple: (name, locals, globals).
101
102 Name will be None if env is __main__, and locals will be None if same
103 as globals, ie local env is global env."""
104
105 import sys, __main__
106
107 bogus = 'bogus' # A locally usable exception
108 try: raise bogus # Force an exception
109 except bogus:
110 at = sys.exc_traceback.tb_frame.f_back # The external frame.
111 if at.f_back: at = at.f_back # And further, if any.
112 where, globals, locals = at.f_code, at.f_globals, at.f_locals
113 if locals == globals: # Exterior is global?
114 locals = None
115 if where:
116 where = where.co_name
117 return (where, locals, globals)
118
119
120def rexec(str, globals = None, locals = None):
121 trace('rexec(%s, ...)', `str`)
122 if globals is None:
123 globals = locals = exterior()[2]
124 elif locals is None:
125 locals = globals
126 globals['__builtins__'] = safe_builtin.__dict__
127 safe_sys.stdout = sys.stdout
128 safe_sys.stderr = sys.stderr
129 exec str in globals, locals
130
131def rexecfile(file, globals = None, locals = None):
132 trace('rexecfile(%s, ...)', `file`)
133 if globals is None:
134 globals = locals = exterior()[2]
135 elif locals is None:
136 locals = globals
137 globals['__builtins__'] = safe_builtin.__dict__
138 safe_sys.stdout = sys.stdout
139 safe_sys.stderr = sys.stderr
140 return execfile(file, globals, locals)
141
142def reval(str, globals = None, locals = None):
143 trace('reval(%s, ...)', `str`)
144 if globals is None:
145 globals = locals = exterior()[2]
146 elif locals is None:
147 locals = globals
148 globals['__builtins__'] = safe_builtin.__dict__
149 safe_sys.stdout = sys.stdout
150 safe_sys.stderr = sys.stderr
151 return eval(str, globals, locals)
152safe_builtin.eval = reval
153
154
155def test():
156 import traceback
157 g = {}
158 while 1:
159 try:
160 s = raw_input('--> ')
161 except EOFError:
162 break
163 try:
164 try:
165 c = compile(s, '', 'eval')
166 except:
167 rexec(s, g)
168 else:
169 print reval(c, g)
170 except:
171 traceback.print_exc()
172
173if __name__ == '__main__':
174 test()