| # Implement restricted execution of Python code |
| |
| import __builtin__ |
| import new |
| import os |
| import sys |
| import types |
| |
| def trace(fmt, *args): |
| if 0: |
| sys.stderr.write(fmt % args + '\n') |
| |
| def copydict(src, dst, exceptions = [], only = None): |
| if only is None: |
| for key in src.keys(): |
| if key not in exceptions: |
| dst[key] = src[key] |
| else: |
| for key in only: |
| dst[key] = src[key] |
| |
| def copymodule(src, dst, exceptions = [], only = None): |
| copydict(src.__dict__, dst.__dict__, exceptions, only) |
| |
| safe_path = ['/ufs/guido/lib/python'] |
| safe_modules = ['array', 'math', 'regex', 'strop', 'time'] |
| unsafe_builtin_names = ['open', 'reload', '__import__', |
| 'raw_input', 'input'] |
| safe_posix_names = ['error', 'fstat', 'listdir', 'lstat', 'readlink', 'stat', |
| 'times', 'uname', 'getpid', 'getppid', 'getcwd', |
| 'getuid', 'getgid', 'geteuid', 'getegid'] |
| |
| safe_sys = new.module('sys') |
| safe_sys.modules = {} |
| safe_sys.modules['sys'] = safe_sys |
| safe_sys.path = safe_path[:] |
| safe_sys.argv = ['-'] |
| safe_sys.builtin_module_names = safe_modules[:] + ['posix'] |
| safe_sys.builtin_module_names.sort() |
| safe_sys.copyright = sys.copyright |
| safe_sys.version = sys.version + ' [restricted mode]' |
| safe_sys.exit = sys.exit |
| |
| def new_module(name): |
| safe_sys.modules[name] = m = new.module(name) |
| return m |
| |
| safe_builtin = new_module('__builtin__') |
| copymodule(__builtin__, safe_builtin, unsafe_builtin_names) |
| |
| safe_main = new_module('__main__') |
| |
| safe_posix = new_module('posix') |
| import posix |
| copymodule(posix, safe_posix, None, safe_posix_names) |
| safe_posix.environ = {} |
| copydict(posix.environ, safe_posix.environ) |
| |
| safe_types = new_module('types') |
| copymodule(types, safe_types) |
| |
| def safe_import(name, globals=None, locals=None, fromlist=None): |
| if '.' in name: |
| raise ImportError, "import of dotted names not supported" |
| if safe_sys.modules.has_key(name): |
| return safe_sys.modules[name] |
| if name in safe_modules: |
| temp = {} |
| exec "import "+name in temp |
| m = new_module(name) |
| copymodule(temp[name], m) |
| return m |
| for dirname in safe_path: |
| filename = os.path.join(dirname, name + '.py') |
| try: |
| f = open(filename, 'r') |
| f.close() |
| except IOError: |
| continue |
| m = new_module(name) |
| rexecfile(filename, m.__dict__) |
| return m |
| raise ImportError, name |
| safe_builtin.__import__ = safe_import |
| |
| def safe_open(file, mode = 'r'): |
| if type(file) != types.StringType or type(mode) != types.StringType: |
| raise TypeError, 'open argument(s) must be string(s)' |
| if mode not in ('r', 'rb'): |
| raise IOError, 'open for writing not allowed' |
| file = os.path.join(os.getcwd(), file) |
| file = os.path.normpath(file) |
| if file[:2] == '//' or file[:5] == '/etc/' or file[:4] == '/../': |
| raise IOError, 'this path not allowed for reading' |
| return open(file, mode) |
| safe_builtin.open = safe_open |
| |
| |
| def exterior(): |
| """Return env of caller's caller, as triple: (name, locals, globals). |
| |
| Name will be None if env is __main__, and locals will be None if same |
| as globals, ie local env is global env.""" |
| |
| import sys, __main__ |
| |
| bogus = 'bogus' # A locally usable exception |
| try: raise bogus # Force an exception |
| except bogus: |
| at = sys.exc_traceback.tb_frame.f_back # The external frame. |
| if at.f_back: at = at.f_back # And further, if any. |
| where, globals, locals = at.f_code, at.f_globals, at.f_locals |
| if locals == globals: # Exterior is global? |
| locals = None |
| if where: |
| where = where.co_name |
| return (where, locals, globals) |
| |
| |
| def rexec(str, globals = None, locals = None): |
| trace('rexec(%s, ...)', `str`) |
| if globals is None: |
| globals = locals = exterior()[2] |
| elif locals is None: |
| locals = globals |
| globals['__builtins__'] = safe_builtin.__dict__ |
| safe_sys.stdout = sys.stdout |
| safe_sys.stderr = sys.stderr |
| exec str in globals, locals |
| |
| def rexecfile(file, globals = None, locals = None): |
| trace('rexecfile(%s, ...)', `file`) |
| if globals is None: |
| globals = locals = exterior()[2] |
| elif locals is None: |
| locals = globals |
| globals['__builtins__'] = safe_builtin.__dict__ |
| safe_sys.stdout = sys.stdout |
| safe_sys.stderr = sys.stderr |
| return execfile(file, globals, locals) |
| |
| def reval(str, globals = None, locals = None): |
| trace('reval(%s, ...)', `str`) |
| if globals is None: |
| globals = locals = exterior()[2] |
| elif locals is None: |
| locals = globals |
| globals['__builtins__'] = safe_builtin.__dict__ |
| safe_sys.stdout = sys.stdout |
| safe_sys.stderr = sys.stderr |
| return eval(str, globals, locals) |
| safe_builtin.eval = reval |
| |
| |
| def test(): |
| import traceback |
| g = {} |
| while 1: |
| try: |
| s = raw_input('--> ') |
| except EOFError: |
| break |
| try: |
| try: |
| c = compile(s, '', 'eval') |
| except: |
| rexec(s, g) |
| else: |
| print reval(c, g) |
| except: |
| traceback.print_exc() |
| |
| if __name__ == '__main__': |
| test() |