cpython/Lib/rexec.py

175 lines
4.6 KiB
Python

# 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()