ยปCore Development>Code coverage>Lib/rexec.py

Python code coverage for Lib/rexec.py

#countcontent
1n/a"""Restricted execution facilities.
2n/a
3n/aThe class RExec exports methods r_exec(), r_eval(), r_execfile(), and
4n/ar_import(), which correspond roughly to the built-in operations
5n/aexec, eval(), execfile() and import, but executing the code in an
6n/aenvironment that only exposes those built-in operations that are
7n/adeemed safe. To this end, a modest collection of 'fake' modules is
8n/acreated which mimics the standard modules by the same names. It is a
9n/apolicy decision which built-in modules and operations are made
10n/aavailable; this module provides a reasonable default, but derived
11n/aclasses can change the policies e.g. by overriding or extending class
12n/avariables like ok_builtin_modules or methods like make_sys().
13n/a
14n/aXXX To do:
15n/a- r_open should allow writing tmp dir
16n/a- r_exec etc. with explicit globals/locals? (Use rexec("exec ... in ...")?)
17n/a
181"""
191from warnings import warnpy3k
201warnpy3k("the rexec module has been removed in Python 3.0", stacklevel=2)
211del warnpy3k
22n/a
23n/a
241import sys
251import __builtin__
261import os
271import ihooks
281import imp
29n/a
301__all__ = ["RExec"]
31n/a
322class FileBase:
33n/a
340 ok_file_methods = ('fileno', 'flush', 'isatty', 'read', 'readline',
350 'readlines', 'seek', 'tell', 'write', 'writelines', 'xreadlines',
361 '__iter__')
37n/a
38n/a
392class FileWrapper(FileBase):
40n/a
41n/a # XXX This is just like a Bastion -- should use that!
42n/a
431 def __init__(self, f):
440 for m in self.ok_file_methods:
450 if not hasattr(self, m) and hasattr(f, m):
460 setattr(self, m, getattr(f, m))
47n/a
481 def close(self):
490 self.flush()
50n/a
51n/a
52n/aTEMPLATE = """
53n/adef %s(self, *args):
54n/a return getattr(self.mod, self.name).%s(*args)
551"""
56n/a
572class FileDelegate(FileBase):
58n/a
591 def __init__(self, mod, name):
600 self.mod = mod
610 self.name = name
62n/a
6314 for m in FileBase.ok_file_methods + ('close',):
6413 exec TEMPLATE % (m, m)
65n/a
66n/a
672class RHooks(ihooks.Hooks):
68n/a
691 def __init__(self, *args):
70n/a # Hacks to support both old and new interfaces:
71n/a # old interface was RHooks(rexec[, verbose])
72n/a # new interface is RHooks([verbose])
730 verbose = 0
740 rexec = None
750 if args and type(args[-1]) == type(0):
760 verbose = args[-1]
770 args = args[:-1]
780 if args and hasattr(args[0], '__class__'):
790 rexec = args[0]
800 args = args[1:]
810 if args:
820 raise TypeError, "too many arguments"
830 ihooks.Hooks.__init__(self, verbose)
840 self.rexec = rexec
85n/a
861 def set_rexec(self, rexec):
87n/a # Called by RExec instance to complete initialization
880 self.rexec = rexec
89n/a
901 def get_suffixes(self):
910 return self.rexec.get_suffixes()
92n/a
931 def is_builtin(self, name):
940 return self.rexec.is_builtin(name)
95n/a
961 def init_builtin(self, name):
970 m = __import__(name)
980 return self.rexec.copy_except(m, ())
99n/a
1001 def init_frozen(self, name): raise SystemError, "don't use this"
1011 def load_source(self, *args): raise SystemError, "don't use this"
1021 def load_compiled(self, *args): raise SystemError, "don't use this"
1031 def load_package(self, *args): raise SystemError, "don't use this"
104n/a
1051 def load_dynamic(self, name, filename, file):
1060 return self.rexec.load_dynamic(name, filename, file)
107n/a
1081 def add_module(self, name):
1090 return self.rexec.add_module(name)
110n/a
1111 def modules_dict(self):
1120 return self.rexec.modules
113n/a
1141 def default_path(self):
1150 return self.rexec.modules['sys'].path
116n/a
117n/a
118n/a# XXX Backwards compatibility
1191RModuleLoader = ihooks.FancyModuleLoader
1201RModuleImporter = ihooks.ModuleImporter
121n/a
122n/a
1232class RExec(ihooks._Verbose):
124n/a """Basic restricted execution framework.
125n/a
126n/a Code executed in this restricted environment will only have access to
127n/a modules and functions that are deemed safe; you can subclass RExec to
128n/a add or remove capabilities as desired.
129n/a
130n/a The RExec class can prevent code from performing unsafe operations like
131n/a reading or writing disk files, or using TCP/IP sockets. However, it does
132n/a not protect against code using extremely large amounts of memory or
133n/a processor time.
134n/a
1351 """
136n/a
1371 ok_path = tuple(sys.path) # That's a policy decision
138n/a
1390 ok_builtin_modules = ('audioop', 'array', 'binascii',
1400 'cmath', 'errno', 'imageop',
1410 'marshal', 'math', 'md5', 'operator',
1420 'parser', 'select',
1430 'sha', '_sre', 'strop', 'struct', 'time',
1441 '_weakref')
145n/a
1460 ok_posix_names = ('error', 'fstat', 'listdir', 'lstat', 'readlink',
1470 'stat', 'times', 'uname', 'getpid', 'getppid',
1481 'getcwd', 'getuid', 'getgid', 'geteuid', 'getegid')
149n/a
1500 ok_sys_names = ('byteorder', 'copyright', 'exit', 'getdefaultencoding',
1510 'getrefcount', 'hexversion', 'maxint', 'maxunicode',
1521 'platform', 'ps1', 'ps2', 'version', 'version_info')
153n/a
1541 nok_builtin_names = ('open', 'file', 'reload', '__import__')
155n/a
1561 ok_file_types = (imp.C_EXTENSION, imp.PY_SOURCE)
157n/a
1581 def __init__(self, hooks = None, verbose = 0):
159n/a """Returns an instance of the RExec class.
160n/a
161n/a The hooks parameter is an instance of the RHooks class or a subclass
162n/a of it. If it is omitted or None, the default RHooks class is
163n/a instantiated.
164n/a
165n/a Whenever the RExec module searches for a module (even a built-in one)
166n/a or reads a module's code, it doesn't actually go out to the file
167n/a system itself. Rather, it calls methods of an RHooks instance that
168n/a was passed to or created by its constructor. (Actually, the RExec
169n/a object doesn't make these calls --- they are made by a module loader
170n/a object that's part of the RExec object. This allows another level of
171n/a flexibility, which can be useful when changing the mechanics of
172n/a import within the restricted environment.)
173n/a
174n/a By providing an alternate RHooks object, we can control the file
175n/a system accesses made to import a module, without changing the
176n/a actual algorithm that controls the order in which those accesses are
177n/a made. For instance, we could substitute an RHooks object that
178n/a passes all filesystem requests to a file server elsewhere, via some
179n/a RPC mechanism such as ILU. Grail's applet loader uses this to support
180n/a importing applets from a URL for a directory.
181n/a
182n/a If the verbose parameter is true, additional debugging output may be
183n/a sent to standard output.
184n/a
185n/a """
186n/a
1870 raise RuntimeError, "This code is not secure in Python 2.2 and later"
188n/a
1890 ihooks._Verbose.__init__(self, verbose)
190n/a # XXX There's a circular reference here:
1910 self.hooks = hooks or RHooks(verbose)
1920 self.hooks.set_rexec(self)
1930 self.modules = {}
1940 self.ok_dynamic_modules = self.ok_builtin_modules
1950 list = []
1960 for mname in self.ok_builtin_modules:
1970 if mname in sys.builtin_module_names:
1980 list.append(mname)
1990 self.ok_builtin_modules = tuple(list)
2000 self.set_trusted_path()
2010 self.make_builtin()
2020 self.make_initial_modules()
203n/a # make_sys must be last because it adds the already created
204n/a # modules to its builtin_module_names
2050 self.make_sys()
2060 self.loader = RModuleLoader(self.hooks, verbose)
2070 self.importer = RModuleImporter(self.loader, verbose)
208n/a
2091 def set_trusted_path(self):
210n/a # Set the path from which dynamic modules may be loaded.
211n/a # Those dynamic modules must also occur in ok_builtin_modules
2120 self.trusted_path = filter(os.path.isabs, sys.path)
213n/a
2141 def load_dynamic(self, name, filename, file):
2150 if name not in self.ok_dynamic_modules:
2160 raise ImportError, "untrusted dynamic module: %s" % name
2170 if name in sys.modules:
2180 src = sys.modules[name]
219n/a else:
2200 src = imp.load_dynamic(name, filename, file)
2210 dst = self.copy_except(src, [])
2220 return dst
223n/a
2241 def make_initial_modules(self):
2250 self.make_main()
2260 self.make_osname()
227n/a
228n/a # Helpers for RHooks
229n/a
2301 def get_suffixes(self):
2310 return [item # (suff, mode, type)
2320 for item in imp.get_suffixes()
2330 if item[2] in self.ok_file_types]
234n/a
2351 def is_builtin(self, mname):
2360 return mname in self.ok_builtin_modules
237n/a
238n/a # The make_* methods create specific built-in modules
239n/a
2401 def make_builtin(self):
2410 m = self.copy_except(__builtin__, self.nok_builtin_names)
2420 m.__import__ = self.r_import
2430 m.reload = self.r_reload
2440 m.open = m.file = self.r_open
245n/a
2461 def make_main(self):
2470 self.add_module('__main__')
248n/a
2491 def make_osname(self):
2500 osname = os.name
2510 src = __import__(osname)
2520 dst = self.copy_only(src, self.ok_posix_names)
2530 dst.environ = e = {}
2540 for key, value in os.environ.items():
2550 e[key] = value
256n/a
2571 def make_sys(self):
2580 m = self.copy_only(sys, self.ok_sys_names)
2590 m.modules = self.modules
2600 m.argv = ['RESTRICTED']
2610 m.path = map(None, self.ok_path)
2620 m.exc_info = self.r_exc_info
2630 m = self.modules['sys']
2640 l = self.modules.keys() + list(self.ok_builtin_modules)
2650 l.sort()
2660 m.builtin_module_names = tuple(l)
267n/a
268n/a # The copy_* methods copy existing modules with some changes
269n/a
2701 def copy_except(self, src, exceptions):
2710 dst = self.copy_none(src)
2720 for name in dir(src):
2730 setattr(dst, name, getattr(src, name))
2740 for name in exceptions:
2750 try:
2760 delattr(dst, name)
2770 except AttributeError:
2780 pass
2790 return dst
280n/a
2811 def copy_only(self, src, names):
2820 dst = self.copy_none(src)
2830 for name in names:
2840 try:
2850 value = getattr(src, name)
2860 except AttributeError:
2870 continue
2880 setattr(dst, name, value)
2890 return dst
290n/a
2911 def copy_none(self, src):
2920 m = self.add_module(src.__name__)
2930 m.__doc__ = src.__doc__
2940 return m
295n/a
296n/a # Add a module -- return an existing module or create one
297n/a
2981 def add_module(self, mname):
2990 m = self.modules.get(mname)
3000 if m is None:
3010 self.modules[mname] = m = self.hooks.new_module(mname)
3020 m.__builtins__ = self.modules['__builtin__']
3030 return m
304n/a
305n/a # The r* methods are public interfaces
306n/a
3071 def r_exec(self, code):
308n/a """Execute code within a restricted environment.
309n/a
310n/a The code parameter must either be a string containing one or more
311n/a lines of Python code, or a compiled code object, which will be
312n/a executed in the restricted environment's __main__ module.
313n/a
314n/a """
3150 m = self.add_module('__main__')
3160 exec code in m.__dict__
317n/a
3181 def r_eval(self, code):
319n/a """Evaluate code within a restricted environment.
320n/a
321n/a The code parameter must either be a string containing a Python
322n/a expression, or a compiled code object, which will be evaluated in
323n/a the restricted environment's __main__ module. The value of the
324n/a expression or code object will be returned.
325n/a
326n/a """
3270 m = self.add_module('__main__')
3280 return eval(code, m.__dict__)
329n/a
3301 def r_execfile(self, file):
331n/a """Execute the Python code in the file in the restricted
332n/a environment's __main__ module.
333n/a
334n/a """
3350 m = self.add_module('__main__')
3360 execfile(file, m.__dict__)
337n/a
3381 def r_import(self, mname, globals={}, locals={}, fromlist=[]):
339n/a """Import a module, raising an ImportError exception if the module
340n/a is considered unsafe.
341n/a
342n/a This method is implicitly called by code executing in the
343n/a restricted environment. Overriding this method in a subclass is
344n/a used to change the policies enforced by a restricted environment.
345n/a
346n/a """
3470 return self.importer.import_module(mname, globals, locals, fromlist)
348n/a
3491 def r_reload(self, m):
350n/a """Reload the module object, re-parsing and re-initializing it.
351n/a
352n/a This method is implicitly called by code executing in the
353n/a restricted environment. Overriding this method in a subclass is
354n/a used to change the policies enforced by a restricted environment.
355n/a
356n/a """
3570 return self.importer.reload(m)
358n/a
3591 def r_unload(self, m):
360n/a """Unload the module.
361n/a
362n/a Removes it from the restricted environment's sys.modules dictionary.
363n/a
364n/a This method is implicitly called by code executing in the
365n/a restricted environment. Overriding this method in a subclass is
366n/a used to change the policies enforced by a restricted environment.
367n/a
368n/a """
3690 return self.importer.unload(m)
370n/a
371n/a # The s_* methods are similar but also swap std{in,out,err}
372n/a
3731 def make_delegate_files(self):
3740 s = self.modules['sys']
3750 self.delegate_stdin = FileDelegate(s, 'stdin')
3760 self.delegate_stdout = FileDelegate(s, 'stdout')
3770 self.delegate_stderr = FileDelegate(s, 'stderr')
3780 self.restricted_stdin = FileWrapper(sys.stdin)
3790 self.restricted_stdout = FileWrapper(sys.stdout)
3800 self.restricted_stderr = FileWrapper(sys.stderr)
381n/a
3821 def set_files(self):
3830 if not hasattr(self, 'save_stdin'):
3840 self.save_files()
3850 if not hasattr(self, 'delegate_stdin'):
3860 self.make_delegate_files()
3870 s = self.modules['sys']
3880 s.stdin = self.restricted_stdin
3890 s.stdout = self.restricted_stdout
3900 s.stderr = self.restricted_stderr
3910 sys.stdin = self.delegate_stdin
3920 sys.stdout = self.delegate_stdout
3930 sys.stderr = self.delegate_stderr
394n/a
3951 def reset_files(self):
3960 self.restore_files()
3970 s = self.modules['sys']
3980 self.restricted_stdin = s.stdin
3990 self.restricted_stdout = s.stdout
4000 self.restricted_stderr = s.stderr
401n/a
402n/a
4031 def save_files(self):
4040 self.save_stdin = sys.stdin
4050 self.save_stdout = sys.stdout
4060 self.save_stderr = sys.stderr
407n/a
4081 def restore_files(self):
4090 sys.stdin = self.save_stdin
4100 sys.stdout = self.save_stdout
4110 sys.stderr = self.save_stderr
412n/a
4131 def s_apply(self, func, args=(), kw={}):
4140 self.save_files()
4150 try:
4160 self.set_files()
4170 r = func(*args, **kw)
418n/a finally:
4190 self.restore_files()
4200 return r
421n/a
4221 def s_exec(self, *args):
423n/a """Execute code within a restricted environment.
424n/a
425n/a Similar to the r_exec() method, but the code will be granted access
426n/a to restricted versions of the standard I/O streams sys.stdin,
427n/a sys.stderr, and sys.stdout.
428n/a
429n/a The code parameter must either be a string containing one or more
430n/a lines of Python code, or a compiled code object, which will be
431n/a executed in the restricted environment's __main__ module.
432n/a
433n/a """
4340 return self.s_apply(self.r_exec, args)
435n/a
4361 def s_eval(self, *args):
437n/a """Evaluate code within a restricted environment.
438n/a
439n/a Similar to the r_eval() method, but the code will be granted access
440n/a to restricted versions of the standard I/O streams sys.stdin,
441n/a sys.stderr, and sys.stdout.
442n/a
443n/a The code parameter must either be a string containing a Python
444n/a expression, or a compiled code object, which will be evaluated in
445n/a the restricted environment's __main__ module. The value of the
446n/a expression or code object will be returned.
447n/a
448n/a """
4490 return self.s_apply(self.r_eval, args)
450n/a
4511 def s_execfile(self, *args):
452n/a """Execute the Python code in the file in the restricted
453n/a environment's __main__ module.
454n/a
455n/a Similar to the r_execfile() method, but the code will be granted
456n/a access to restricted versions of the standard I/O streams sys.stdin,
457n/a sys.stderr, and sys.stdout.
458n/a
459n/a """
4600 return self.s_apply(self.r_execfile, args)
461n/a
4621 def s_import(self, *args):
463n/a """Import a module, raising an ImportError exception if the module
464n/a is considered unsafe.
465n/a
466n/a This method is implicitly called by code executing in the
467n/a restricted environment. Overriding this method in a subclass is
468n/a used to change the policies enforced by a restricted environment.
469n/a
470n/a Similar to the r_import() method, but has access to restricted
471n/a versions of the standard I/O streams sys.stdin, sys.stderr, and
472n/a sys.stdout.
473n/a
474n/a """
4750 return self.s_apply(self.r_import, args)
476n/a
4771 def s_reload(self, *args):
478n/a """Reload the module object, re-parsing and re-initializing it.
479n/a
480n/a This method is implicitly called by code executing in the
481n/a restricted environment. Overriding this method in a subclass is
482n/a used to change the policies enforced by a restricted environment.
483n/a
484n/a Similar to the r_reload() method, but has access to restricted
485n/a versions of the standard I/O streams sys.stdin, sys.stderr, and
486n/a sys.stdout.
487n/a
488n/a """
4890 return self.s_apply(self.r_reload, args)
490n/a
4911 def s_unload(self, *args):
492n/a """Unload the module.
493n/a
494n/a Removes it from the restricted environment's sys.modules dictionary.
495n/a
496n/a This method is implicitly called by code executing in the
497n/a restricted environment. Overriding this method in a subclass is
498n/a used to change the policies enforced by a restricted environment.
499n/a
500n/a Similar to the r_unload() method, but has access to restricted
501n/a versions of the standard I/O streams sys.stdin, sys.stderr, and
502n/a sys.stdout.
503n/a
504n/a """
5050 return self.s_apply(self.r_unload, args)
506n/a
507n/a # Restricted open(...)
508n/a
5091 def r_open(self, file, mode='r', buf=-1):
510n/a """Method called when open() is called in the restricted environment.
511n/a
512n/a The arguments are identical to those of the open() function, and a
513n/a file object (or a class instance compatible with file objects)
514n/a should be returned. RExec's default behaviour is allow opening
515n/a any file for reading, but forbidding any attempt to write a file.
516n/a
517n/a This method is implicitly called by code executing in the
518n/a restricted environment. Overriding this method in a subclass is
519n/a used to change the policies enforced by a restricted environment.
520n/a
521n/a """
5220 mode = str(mode)
5230 if mode not in ('r', 'rb'):
5240 raise IOError, "can't open files for writing in restricted mode"
5250 return open(file, mode, buf)
526n/a
527n/a # Restricted version of sys.exc_info()
528n/a
5291 def r_exc_info(self):
5300 ty, va, tr = sys.exc_info()
5310 tr = None
5320 return ty, va, tr
533n/a
534n/a
5351def test():
5360 import getopt, traceback
5370 opts, args = getopt.getopt(sys.argv[1:], 'vt:')
5380 verbose = 0
5390 trusted = []
5400 for o, a in opts:
5410 if o == '-v':
5420 verbose = verbose+1
5430 if o == '-t':
5440 trusted.append(a)
5450 r = RExec(verbose=verbose)
5460 if trusted:
5470 r.ok_builtin_modules = r.ok_builtin_modules + tuple(trusted)
5480 if args:
5490 r.modules['sys'].argv = args
5500 r.modules['sys'].path.insert(0, os.path.dirname(args[0]))
551n/a else:
5520 r.modules['sys'].path.insert(0, "")
5530 fp = sys.stdin
5540 if args and args[0] != '-':
5550 try:
5560 fp = open(args[0])
5570 except IOError, msg:
5580 print "%s: can't open file %r" % (sys.argv[0], args[0])
5590 return 1
5600 if fp.isatty():
5610 try:
5620 import readline
5630 except ImportError:
5640 pass
5650 import code
5660 class RestrictedConsole(code.InteractiveConsole):
5670 def runcode(self, co):
5680 self.locals['__builtins__'] = r.modules['__builtin__']
5690 r.s_apply(code.InteractiveConsole.runcode, (self, co))
5700 try:
5710 RestrictedConsole(r.modules['__main__'].__dict__).interact()
5720 except SystemExit, n:
5730 return n
574n/a else:
5750 text = fp.read()
5760 fp.close()
5770 c = compile(text, fp.name, 'exec')
5780 try:
5790 r.s_exec(c)
5800 except SystemExit, n:
5810 return n
5820 except:
5830 traceback.print_exc()
5840 return 1
585n/a
586n/a
5871if __name__ == '__main__':
5880 sys.exit(test())