summaryrefslogtreecommitdiffstats
path: root/Lib/rexec.py
blob: 52c5eea34594722802ab91a0fc24617d7f80d110 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# Implement restricted execution of Python code

import __builtin__
import imp
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 = ['/usr/local/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 = imp.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 = imp.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()