Guido van Rossum | 9a22de1 | 1995-01-12 12:29:47 +0000 | [diff] [blame] | 1 | # Implement restricted execution of Python code |
| 2 | |
| 3 | import __builtin__ |
| 4 | import new |
| 5 | import os |
| 6 | import sys |
| 7 | import types |
| 8 | |
| 9 | def trace(fmt, *args): |
| 10 | if 0: |
| 11 | sys.stderr.write(fmt % args + '\n') |
| 12 | |
| 13 | def 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 | |
| 22 | def copymodule(src, dst, exceptions = [], only = None): |
| 23 | copydict(src.__dict__, dst.__dict__, exceptions, only) |
| 24 | |
| 25 | safe_path = ['/ufs/guido/lib/python'] |
| 26 | safe_modules = ['array', 'math', 'regex', 'strop', 'time'] |
| 27 | unsafe_builtin_names = ['open', 'reload', '__import__', |
Guido van Rossum | 9a22de1 | 1995-01-12 12:29:47 +0000 | [diff] [blame] | 28 | 'raw_input', 'input'] |
| 29 | safe_posix_names = ['error', 'fstat', 'listdir', 'lstat', 'readlink', 'stat', |
| 30 | 'times', 'uname', 'getpid', 'getppid', 'getcwd', |
| 31 | 'getuid', 'getgid', 'geteuid', 'getegid'] |
| 32 | |
| 33 | safe_sys = new.module('sys') |
| 34 | safe_sys.modules = {} |
| 35 | safe_sys.modules['sys'] = safe_sys |
| 36 | safe_sys.path = safe_path[:] |
| 37 | safe_sys.argv = ['-'] |
| 38 | safe_sys.builtin_module_names = safe_modules[:] + ['posix'] |
| 39 | safe_sys.builtin_module_names.sort() |
| 40 | safe_sys.copyright = sys.copyright |
| 41 | safe_sys.version = sys.version + ' [restricted mode]' |
| 42 | safe_sys.exit = sys.exit |
| 43 | |
| 44 | def new_module(name): |
| 45 | safe_sys.modules[name] = m = new.module(name) |
| 46 | return m |
| 47 | |
| 48 | safe_builtin = new_module('__builtin__') |
| 49 | copymodule(__builtin__, safe_builtin, unsafe_builtin_names) |
| 50 | |
| 51 | safe_main = new_module('__main__') |
| 52 | |
| 53 | safe_posix = new_module('posix') |
| 54 | import posix |
| 55 | copymodule(posix, safe_posix, None, safe_posix_names) |
| 56 | safe_posix.environ = {} |
| 57 | copydict(posix.environ, safe_posix.environ) |
| 58 | |
| 59 | safe_types = new_module('types') |
| 60 | copymodule(types, safe_types) |
| 61 | |
Guido van Rossum | b5f9460 | 1995-03-02 15:30:15 +0000 | [diff] [blame] | 62 | def safe_import(name, globals=None, locals=None, fromlist=None): |
| 63 | if '.' in name: |
| 64 | raise ImportError, "import of dotted names not supported" |
Guido van Rossum | 9a22de1 | 1995-01-12 12:29:47 +0000 | [diff] [blame] | 65 | 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 |
| 84 | safe_builtin.__import__ = safe_import |
| 85 | |
| 86 | def 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 Rossum | 8e8a525 | 1995-01-17 15:58:37 +0000 | [diff] [blame] | 91 | 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 Rossum | 9a22de1 | 1995-01-12 12:29:47 +0000 | [diff] [blame] | 95 | return open(file, mode) |
| 96 | safe_builtin.open = safe_open |
| 97 | |
Guido van Rossum | 9a22de1 | 1995-01-12 12:29:47 +0000 | [diff] [blame] | 98 | |
| 99 | def 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 | |
| 120 | def 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 | |
| 131 | def 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 | |
| 142 | def 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) |
| 152 | safe_builtin.eval = reval |
| 153 | |
| 154 | |
| 155 | def 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 | |
| 173 | if __name__ == '__main__': |
| 174 | test() |