RestrictedPython-3.6.0/bootstrap.py0000644000000000000000000000733011415565666017401 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Bootstrap a buildout-based project Simply run this script in a directory containing a buildout.cfg. The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. """ import os, shutil, sys, tempfile, urllib2 from optparse import OptionParser tmpeggs = tempfile.mkdtemp() is_jython = sys.platform.startswith('java') # parsing arguments parser = OptionParser() parser.add_option("-v", "--version", dest="version", help="use a specific zc.buildout version") parser.add_option("-d", "--distribute", action="store_true", dest="distribute", default=False, help="Use Disribute rather than Setuptools.") parser.add_option("-c", None, action="store", dest="config_file", help=("Specify the path to the buildout configuration " "file to be used.")) options, args = parser.parse_args() # if -c was provided, we push it back into args for buildout' main function if options.config_file is not None: args += ['-c', options.config_file] if options.version is not None: VERSION = '==%s' % options.version else: VERSION = '' USE_DISTRIBUTE = options.distribute args = args + ['bootstrap'] to_reload = False try: import pkg_resources if not hasattr(pkg_resources, '_distribute'): to_reload = True raise ImportError except ImportError: ez = {} if USE_DISTRIBUTE: exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True) else: exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) if to_reload: reload(pkg_resources) else: import pkg_resources if sys.platform == 'win32': def quote(c): if ' ' in c: return '"%s"' % c # work around spawn lamosity on windows else: return c else: def quote (c): return c cmd = 'from setuptools.command.easy_install import main; main()' ws = pkg_resources.working_set if USE_DISTRIBUTE: requirement = 'distribute' else: requirement = 'setuptools' if is_jython: import subprocess assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd', quote(tmpeggs), 'zc.buildout' + VERSION], env=dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse(requirement)).location ), ).wait() == 0 else: assert os.spawnle( os.P_WAIT, sys.executable, quote (sys.executable), '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION, dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse(requirement)).location ), ) == 0 ws.add_entry(tmpeggs) ws.require('zc.buildout' + VERSION) import zc.buildout.buildout zc.buildout.buildout.main(args) shutil.rmtree(tmpeggs) RestrictedPython-3.6.0/buildout.cfg0000644000000000000000000000030111415566100017271 0ustar rootroot00000000000000[buildout] develop = . parts = interpreter test [interpreter] recipe = zc.recipe.egg interpreter = python eggs = RestrictedPython [test] recipe = zc.recipe.testrunner eggs = RestrictedPython RestrictedPython-3.6.0/CHANGES.txt0000644000000000000000000000441011415572426016607 0ustar rootroot00000000000000Changes ======= 3.6.0 (2010-07-09) ------------------ - Added name check for names assigned during imports using the "from x import y" format. - Added test for name check when assigning an alias using multiple-context with statements in Python 2.7. - Added tests for protection of the iterators for dict and set comprehensions in Python 2.7. 3.6.0a1 (2010-06-05) -------------------- - Removed support for DocumentTemplate.sequence - this is handled in the DocumentTemplate package itself. 3.5.2 (2010-04-30) ------------------ - Removed a testing dependency on zope.testing. 3.5.1 (2009-03-17) ------------------ - Added tests for ``Utilities`` module. - Filtered DeprecationWarnings when importing Python's ``sets`` module. 3.5.0 (2009-02-09) ------------------ - Dropped legacy support for Python 2.1 / 2.2 (``__future__`` imports of ``nested_scopes`` / ``generators``.). 3.4.3 (2008-10-26) ------------------ - Fixed deprecation warning: ``with`` is now a reserved keyword on Python 2.6. That means RestrictedPython should run on Python 2.6 now. Thanks to Ranjith Kannikara, GSoC Student for the patch. - Added tests for ternary if expression and for 'with' keyword and context managers. 3.4.2 (2007-07-28) ------------------ - Changed homepage URL to the CheeseShop site - Greatly improved README.txt 3.4.1 (2007-06-23) ------------------ - Fixed http://www.zope.org/Collectors/Zope/2295: Bare conditional in a Zope 2 PythonScript followed by a comment causes SyntaxError. 3.4.0 (2007-06-04) ------------------ - RestrictedPython now has its own release cycle as a separate egg. - Synchronized with RestrictedPython from Zope 2 tree. 3.2.0 (2006-01-05) ------------------ - Corresponds to the verison of the RestrictedPython package shipped as part of the Zope 3.2.0 release. - No changes from 3.1.0. 3.1.0 (2005-10-03) ------------------ - Corresponds to the verison of the RestrictedPython package shipped as part of the Zope 3.1.0 release. - Removed unused fossil module, ``SafeMapping``. - Replaced use of deprecated 'whrandom' module with 'random' (aliased to 'whrandom' for backward compatibility). 3.0.0 (2004-11-07) ------------------ - Corresponds to the verison of the RestrictedPython package shipped as part of the Zope X3.0.0 release. RestrictedPython-3.6.0/COPYRIGHT.txt0000644000000000000000000000004011415154300017065 0ustar rootroot00000000000000Zope Foundation and ContributorsRestrictedPython-3.6.0/LICENSE.txt0000644000000000000000000000402611415154300016607 0ustar rootroot00000000000000Zope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. RestrictedPython-3.6.0/PKG-INFO0000644000000000000000000002466411415572640016106 0ustar rootroot00000000000000Metadata-Version: 1.0 Name: RestrictedPython Version: 3.6.0 Summary: RestrictedPython provides a restricted execution environment for Python, e.g. for running untrusted code. Home-page: http://pypi.python.org/pypi/RestrictedPython Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: .. contents:: Overview ======== RestrictedPython provides a ``restricted_compile`` function that works like the built-in ``compile`` function, except that it allows the controlled and restricted execution of code: >>> src = ''' ... def hello_world(): ... return "Hello World!" ... ''' >>> from RestrictedPython import compile_restricted >>> code = compile_restricted(src, '', 'exec') The resulting code can be executed using the ``exec`` built-in: >>> exec(code) As a result, the ``hello_world`` function is now available in the global namespace: >>> hello_world() 'Hello World!' Compatibility ============= This release of RestrictedPython is compatible with Python 2.3, 2.4, 2.5, 2.6, and 2.7. Implementing a policy ===================== RestrictedPython only provides the raw material for restricted execution. To actually enforce any restrictions, you need to supply a policy implementation by providing restricted versions of ``print``, ``getattr``, ``setattr``, ``import``, etc. These restricted implementations are hooked up by providing a set of specially named objects in the global dict that you use for execution of code. Specifically: 1. ``_print_`` is a callable object that returns a handler for print statements. This handler must have a ``write()`` method that accepts a single string argument, and must return a string when called. ``RestrictedPython.PrintCollector.PrintCollector`` is a suitable implementation. 2. ``_write_`` is a guard function taking a single argument. If the object passed to it may be written to, it should be returned, otherwise the guard function should raise an exception. ``_write`` is typically called on an object before a ``setattr`` operation. 3. ``_getattr_`` and ``_getitem_`` are guard functions, each of which takes two arguments. The first is the base object to be accessed, while the second is the attribute name or item index that will be read. The guard function should return the attribute or subitem, or raise an exception. 4. ``__import__`` is the normal Python import hook, and should be used to control access to Python packages and modules. 5. ``__builtins__`` is the normal Python builtins dictionary, which should be weeded down to a set that cannot be used to get around your restrictions. A usable "safe" set is ``RestrictedPython.Guards.safe_builtins``. To help illustrate how this works under the covers, here's an example function:: def f(x): x.foo = x.foo + x[0] print x return printed and (sort of) how it looks after restricted compilation:: def f(x): # Make local variables from globals. _print = _print_() _write = _write_ _getattr = _getattr_ _getitem = _getitem_ # Translation of f(x) above _write(x).foo = _getattr(x, 'foo') + _getitem(x, 0) print >>_print, x return _print() Examples ======== ``print`` --------- To support the ``print`` statement in restricted code, we supply a ``_print_`` object (note that it's a *factory*, e.g. a class or a callable, from which the restricted machinery will create the object): >>> from RestrictedPython.PrintCollector import PrintCollector >>> _print_ = PrintCollector >>> src = ''' ... print "Hello World!" ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) As you can see, the text doesn't appear on stdout. The print collector collects it. We can have access to the text using the ``printed`` variable, though: >>> src = ''' ... print "Hello World!" ... result = printed ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) >>> result 'Hello World!\n' Built-ins --------- By supplying a different ``__builtins__`` dictionary, we can rule out unsafe operations, such as opening files: >>> from RestrictedPython.Guards import safe_builtins >>> restricted_globals = dict(__builtins__ = safe_builtins) >>> src = ''' ... open('/etc/passwd') ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) in restricted_globals Traceback (most recent call last): ... NameError: name 'open' is not defined Guards ------ Here's an example of a write guard that never lets restricted code modify (assign, delete an attribute or item) except dictionaries and lists: >>> from RestrictedPython.Guards import full_write_guard >>> _write_ = full_write_guard >>> _getattr_ = getattr >>> class BikeShed(object): ... colour = 'green' ... >>> shed = BikeShed() Normally accessing attriutes works as expected, because we're using the standard ``getattr`` function for the ``_getattr_`` guard: >>> src = ''' ... print shed.colour ... result = printed ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) >>> result 'green\n' However, changing an attribute doesn't work: >>> src = ''' ... shed.colour = 'red' ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) Traceback (most recent call last): ... TypeError: attribute-less object (assign or del) As said, this particular write guard (``full_write_guard``) will allow restricted code to modify lists and dictionaries: >>> fibonacci = [1, 1, 2, 3, 4] >>> transl = dict(one=1, two=2, tres=3) >>> src = ''' ... # correct mistake in list ... fibonacci[-1] = 5 ... # one item doesn't belong ... del transl['tres'] ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) >>> fibonacci [1, 1, 2, 3, 5] >>> sorted(transl.keys()) ['one', 'two'] Changes ======= 3.6.0 (2010-07-09) ------------------ - Added name check for names assigned during imports using the "from x import y" format. - Added test for name check when assigning an alias using multiple-context with statements in Python 2.7. - Added tests for protection of the iterators for dict and set comprehensions in Python 2.7. 3.6.0a1 (2010-06-05) -------------------- - Removed support for DocumentTemplate.sequence - this is handled in the DocumentTemplate package itself. 3.5.2 (2010-04-30) ------------------ - Removed a testing dependency on zope.testing. 3.5.1 (2009-03-17) ------------------ - Added tests for ``Utilities`` module. - Filtered DeprecationWarnings when importing Python's ``sets`` module. 3.5.0 (2009-02-09) ------------------ - Dropped legacy support for Python 2.1 / 2.2 (``__future__`` imports of ``nested_scopes`` / ``generators``.). 3.4.3 (2008-10-26) ------------------ - Fixed deprecation warning: ``with`` is now a reserved keyword on Python 2.6. That means RestrictedPython should run on Python 2.6 now. Thanks to Ranjith Kannikara, GSoC Student for the patch. - Added tests for ternary if expression and for 'with' keyword and context managers. 3.4.2 (2007-07-28) ------------------ - Changed homepage URL to the CheeseShop site - Greatly improved README.txt 3.4.1 (2007-06-23) ------------------ - Fixed http://www.zope.org/Collectors/Zope/2295: Bare conditional in a Zope 2 PythonScript followed by a comment causes SyntaxError. 3.4.0 (2007-06-04) ------------------ - RestrictedPython now has its own release cycle as a separate egg. - Synchronized with RestrictedPython from Zope 2 tree. 3.2.0 (2006-01-05) ------------------ - Corresponds to the verison of the RestrictedPython package shipped as part of the Zope 3.2.0 release. - No changes from 3.1.0. 3.1.0 (2005-10-03) ------------------ - Corresponds to the verison of the RestrictedPython package shipped as part of the Zope 3.1.0 release. - Removed unused fossil module, ``SafeMapping``. - Replaced use of deprecated 'whrandom' module with 'random' (aliased to 'whrandom' for backward compatibility). 3.0.0 (2004-11-07) ------------------ - Corresponds to the verison of the RestrictedPython package shipped as part of the Zope X3.0.0 release. Platform: UNKNOWN RestrictedPython-3.6.0/README.txt0000644000000000000000000000006111415154300016455 0ustar rootroot00000000000000Please refer to src/RestrictedPython/README.txt. RestrictedPython-3.6.0/setup.cfg0000644000000000000000000000007311415572640016616 0ustar rootroot00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 RestrictedPython-3.6.0/setup.py0000644000000000000000000000275611415572456016526 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Setup for RestrictedPython package """ import os from setuptools import setup, find_packages def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() setup(name='RestrictedPython', version='3.6.0', url='http://pypi.python.org/pypi/RestrictedPython', license='ZPL 2.1', description='RestrictedPython provides a restricted execution ' 'environment for Python, e.g. for running untrusted code.', author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', long_description=(read('src', 'RestrictedPython', 'README.txt') + '\n' + read('CHANGES.txt')), packages = find_packages('src'), package_dir = {'': 'src'}, install_requires = ['setuptools'], include_package_data = True, zip_safe = False, ) RestrictedPython-3.6.0/src/RestrictedPython/__init__.py0000644000000000000000000000131611415565716023216 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## ''' RestrictedPython package. ''' from SelectCompiler import * from PrintCollector import PrintCollector RestrictedPython-3.6.0/src/RestrictedPython/Eval.py0000644000000000000000000000734511415565726022357 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## """Restricted Python Expressions """ __version__='$Revision: 1.6 $'[11:-2] from RestrictedPython import compile_restricted_eval from string import translate, strip import string nltosp = string.maketrans('\r\n',' ') default_guarded_getattr = getattr # No restrictions. def default_guarded_getitem(ob, index): # No restrictions. return ob[index] PROFILE = 0 class RestrictionCapableEval: """A base class for restricted code.""" globals = {'__builtins__': None} rcode = None # restricted ucode = None # unrestricted used = None def __init__(self, expr): """Create a restricted expression where: expr -- a string containing the expression to be evaluated. """ expr = strip(expr) self.__name__ = expr expr = translate(expr, nltosp) self.expr = expr self.prepUnrestrictedCode() # Catch syntax errors. def prepRestrictedCode(self): if self.rcode is None: if PROFILE: from time import clock start = clock() co, err, warn, used = compile_restricted_eval( self.expr, '') if PROFILE: end = clock() print 'prepRestrictedCode: %d ms for %s' % ( (end - start) * 1000, `self.expr`) if err: raise SyntaxError, err[0] self.used = tuple(used.keys()) self.rcode = co def prepUnrestrictedCode(self): if self.ucode is None: # Use the standard compiler. co = compile(self.expr, '', 'eval') if self.used is None: # Examine the code object, discovering which names # the expression needs. names=list(co.co_names) used={} i=0 code=co.co_code l=len(code) LOAD_NAME=101 HAVE_ARGUMENT=90 while(i < l): c=ord(code[i]) if c==LOAD_NAME: name=names[ord(code[i+1])+256*ord(code[i+2])] used[name]=1 i=i+3 elif c >= HAVE_ARGUMENT: i=i+3 else: i=i+1 self.used=tuple(used.keys()) self.ucode=co def eval(self, mapping): # This default implementation is probably not very useful. :-( # This is meant to be overridden. self.prepRestrictedCode() code = self.rcode d = {'_getattr_': default_guarded_getattr, '_getitem_': default_guarded_getitem} d.update(self.globals) has_key = d.has_key for name in self.used: try: if not has_key(name): d[name] = mapping[name] except KeyError: # Swallow KeyErrors since the expression # might not actually need the name. If it # does need the name, a NameError will occur. pass return eval(code, d) def __call__(self, **kw): return self.eval(kw) RestrictedPython-3.6.0/src/RestrictedPython/Guards.py0000644000000000000000000001050711415563304022675 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## __version__ = '$Revision: 1.14 $'[11:-2] import exceptions # This tiny set of safe builtins is extended by users of the module. # AccessControl.ZopeGuards contains a large set of wrappers for builtins. # DocumentTemplate.DT_UTil contains a few. safe_builtins = {} for name in ['False', 'None', 'True', 'abs', 'basestring', 'bool', 'callable', 'chr', 'cmp', 'complex', 'divmod', 'float', 'hash', 'hex', 'id', 'int', 'isinstance', 'issubclass', 'len', 'long', 'oct', 'ord', 'pow', 'range', 'repr', 'round', 'str', 'tuple', 'unichr', 'unicode', 'xrange', 'zip']: safe_builtins[name] = __builtins__[name] # Wrappers provided by this module: # delattr # setattr # Wrappers provided by ZopeGuards: # __import__ # apply # dict # enumerate # filter # getattr # hasattr # iter # list # map # max # min # sum # all # any # Builtins that are intentionally disabled # compile - don't let them produce new code # dir - a general purpose introspector, probably hard to wrap # execfile - no direct I/O # file - no direct I/O # globals - uncontrolled namespace access # input - no direct I/O # locals - uncontrolled namespace access # open - no direct I/O # raw_input - no direct I/O # vars - uncontrolled namespace access # There are several strings that describe Python. I think there's no # point to including these, although they are obviously safe: # copyright, credits, exit, help, license, quit # Not provided anywhere. Do something about these? Several are # related to new-style classes, which we are too scared of to support # <0.3 wink>. coerce, buffer, and reload are esoteric enough that no # one should care. # buffer # bytes # bytearray # classmethod # coerce # eval # intern # memoryview # object # property # reload # slice # staticmethod # super # type for name in dir(exceptions): if name[0] != "_": safe_builtins[name] = getattr(exceptions, name) def _write_wrapper(): # Construct the write wrapper class def _handler(secattr, error_msg): # Make a class method. def handler(self, *args): try: f = getattr(self.ob, secattr) except AttributeError: raise TypeError, error_msg f(*args) return handler class Wrapper: def __len__(self): # Required for slices with negative bounds. return len(self.ob) def __init__(self, ob): self.__dict__['ob'] = ob __setitem__ = _handler('__guarded_setitem__', 'object does not support item or slice assignment') __delitem__ = _handler('__guarded_delitem__', 'object does not support item or slice assignment') __setattr__ = _handler('__guarded_setattr__', 'attribute-less object (assign or del)') __delattr__ = _handler('__guarded_delattr__', 'attribute-less object (assign or del)') return Wrapper def _full_write_guard(): # Nested scope abuse! # safetype and Wrapper variables are used by guard() safetype = {dict: True, list: True}.has_key Wrapper = _write_wrapper() def guard(ob): # Don't bother wrapping simple types, or objects that claim to # handle their own write security. if safetype(type(ob)) or hasattr(ob, '_guarded_writes'): return ob # Hand the object to the Wrapper instance, then return the instance. return Wrapper(ob) return guard full_write_guard = _full_write_guard() def guarded_setattr(object, name, value): setattr(full_write_guard(object), name, value) safe_builtins['setattr'] = guarded_setattr def guarded_delattr(object, name): delattr(full_write_guard(object), name) safe_builtins['delattr'] = guarded_delattr RestrictedPython-3.6.0/src/RestrictedPython/Limits.py0000644000000000000000000000324011415154300022675 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## __version__='$Revision: 1.5 $'[11:-2] limited_builtins = {} def limited_range(iFirst, *args): # limited range function from Martijn Pieters RANGELIMIT = 1000 if not len(args): iStart, iEnd, iStep = 0, iFirst, 1 elif len(args) == 1: iStart, iEnd, iStep = iFirst, args[0], 1 elif len(args) == 2: iStart, iEnd, iStep = iFirst, args[0], args[1] else: raise AttributeError, 'range() requires 1-3 int arguments' if iStep == 0: raise ValueError, 'zero step for range()' iLen = int((iEnd - iStart) / iStep) if iLen < 0: iLen = 0 if iLen >= RANGELIMIT: raise ValueError, 'range() too large' return range(iStart, iEnd, iStep) limited_builtins['range'] = limited_range def limited_list(seq): if isinstance(seq, str): raise TypeError, 'cannot convert string to list' return list(seq) limited_builtins['list'] = limited_list def limited_tuple(seq): if isinstance(seq, str): raise TypeError, 'cannot convert string to tuple' return tuple(seq) limited_builtins['tuple'] = limited_tuple RestrictedPython-3.6.0/src/RestrictedPython/MutatingWalker.py0000644000000000000000000000460411415154300024377 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## __version__='$Revision: 1.6 $'[11:-2] from SelectCompiler import ast ListType = type([]) TupleType = type(()) SequenceTypes = (ListType, TupleType) class MutatingWalker: def __init__(self, visitor): self.visitor = visitor self._cache = {} def defaultVisitNode(self, node, walker=None, exclude=None): for name, child in node.__dict__.items(): if exclude is not None and name in exclude: continue v = self.dispatchObject(child) if v is not child: # Replace the node. node.__dict__[name] = v return node def visitSequence(self, seq): res = seq for idx in range(len(seq)): child = seq[idx] v = self.dispatchObject(child) if v is not child: # Change the sequence. if type(res) is ListType: res[idx : idx + 1] = [v] else: res = res[:idx] + (v,) + res[idx + 1:] return res def dispatchObject(self, ob): ''' Expected to return either ob or something that will take its place. ''' if isinstance(ob, ast.Node): return self.dispatchNode(ob) elif type(ob) in SequenceTypes: return self.visitSequence(ob) else: return ob def dispatchNode(self, node): klass = node.__class__ meth = self._cache.get(klass, None) if meth is None: className = klass.__name__ meth = getattr(self.visitor, 'visit' + className, self.defaultVisitNode) self._cache[klass] = meth return meth(node, self) def walk(tree, visitor): return MutatingWalker(visitor).dispatchNode(tree) RestrictedPython-3.6.0/src/RestrictedPython/notes.txt0000644000000000000000000000664411415154300022766 0ustar rootroot00000000000000How it works ============ Every time I see this code, I have to relearn it. These notes will hopefully make this a little easier. :) - The important module is RCompile. The entry points are the compile_restricted_* functions. + compile_restricted_function is used by Python scripts. + compile_restricted_eval is used by ZPT and by DTML indirectly through Eval.RestrictionCapableEval. - OK, so lets see how this works by following the logic of compile_restricted_eval. - First, we create an RExpression, passing the source and a "file name", to be used in tracebacks. Now, an RExpression is just: + a subclass of RestrictedCompileMode and Expression. Expression is a subclass of AbstractCompileMode that sets it's mode to 'eval' and everided compile. Sigh. + RestrictedCompileMode is a subclass of AbstractCompileMode that changes a bunch of things. :) These include compile, so we can ignore the compile we got from Expression. It would have been simpler to just set the dang mode in RExpression. Sigh. RestrictedCompileMode seem to be the interestng base class. I assume it implements the interesting functionality. We'll see below... - Next, we call compileAndTuplize. + This calls compile on the RExpression. It has an error handler that does something that I hope I don't care about. :) + It then calls the genCode method on the RExpression. This is boring, so we'll not worry about it. - The compile method provided by RestrictedCompileMode is interesting. + First it calls _get_tree. * It uses compiler.parse to parse the source * it uses MutatingWalker.walk to mutate the tree using the RestrictedCompileMode's 'rm' attr, which is a RestrictionMutator. The RestrictionMutator has the recipies for mutating the parse tree. (Note, for comparison, that Zope3's zope.security.untrustedpython.rcompile module an alternative RestrictionMutator that provides a much smaller set of changes.) A mutator has visit method for different kinds of AST nodes. These visit methods may mutate nodes or return new nodes that replace the originally visited nodes. There is a default visitor that visits a node's children and replaces the children whose visitors returned new nodes. The walk function just calls the visitor for the root node of the given tree. Note _get_tree ignores the walk return value, thus assuming that the visitor for the root node doesn't return a new node. This is a theoretical bug that we can ignore. + Second, it generates the code. This too is boring. - So this seems simple enough. ;) When we want to add a check, we need to update or add a visit function in RestrictionMutator. How does a visit function work. - First, we usually call walker.defaultVisitNode(node). This transforms the node's child nodes. - Then we hack the node, or possibly return the node. To do this, we have to know how the node works. - The hack often involved changing the code to call some checker function. These have names like _name_. These are names that would be illegal in the input source. If this is a new function, we have to provide it in AccessControl.ZopeGuards._safe_globals. - Don't forget to add a test case to tests.before_and_after. RestrictedPython-3.6.0/src/RestrictedPython/PrintCollector.py0000644000000000000000000000157511415154300024410 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## __version__='$Revision: 1.4 $'[11:-2] class PrintCollector: '''Collect written text, and return it when called.''' def __init__(self): self.txt = [] def write(self, text): self.txt.append(text) def __call__(self): return ''.join(self.txt) RestrictedPython-3.6.0/src/RestrictedPython/RCompile.py0000644000000000000000000002156511415154300023160 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## """Compiles restricted code using the compiler module from the Python standard library. """ __version__='$Revision: 1.6 $'[11:-2] from compiler import ast, parse, misc, syntax, pycodegen from compiler.pycodegen import AbstractCompileMode, Expression, \ Interactive, Module, ModuleCodeGenerator, FunctionCodeGenerator, findOp import MutatingWalker from RestrictionMutator import RestrictionMutator def niceParse(source, filename, mode): if isinstance(source, unicode): # Use the utf-8-sig BOM so the compiler # detects this as a UTF-8 encoded string. source = '\xef\xbb\xbf' + source.encode('utf-8') try: return parse(source, mode) except: # Try to make a clean error message using # the builtin Python compiler. try: compile(source, filename, mode) except SyntaxError: raise # Some other error occurred. raise class RestrictedCompileMode(AbstractCompileMode): """Abstract base class for hooking up custom CodeGenerator.""" # See concrete subclasses below. def __init__(self, source, filename): if source: source = '\n'.join(source.splitlines()) + '\n' self.rm = RestrictionMutator() AbstractCompileMode.__init__(self, source, filename) def parse(self): return niceParse(self.source, self.filename, self.mode) def _get_tree(self): tree = self.parse() MutatingWalker.walk(tree, self.rm) if self.rm.errors: raise SyntaxError, self.rm.errors[0] misc.set_filename(self.filename, tree) syntax.check(tree) return tree def compile(self): tree = self._get_tree() gen = self.CodeGeneratorClass(tree) self.code = gen.getCode() def compileAndTuplize(gen): try: gen.compile() except SyntaxError, v: return None, (str(v),), gen.rm.warnings, gen.rm.used_names return gen.getCode(), (), gen.rm.warnings, gen.rm.used_names def compile_restricted_function(p, body, name, filename, globalize=None): """Compiles a restricted code object for a function. The function can be reconstituted using the 'new' module: new.function(, ) The globalize argument, if specified, is a list of variable names to be treated as globals (code is generated as if each name in the list appeared in a global statement at the top of the function). """ gen = RFunction(p, body, name, filename, globalize) return compileAndTuplize(gen) def compile_restricted_exec(s, filename=''): """Compiles a restricted code suite.""" gen = RModule(s, filename) return compileAndTuplize(gen) def compile_restricted_eval(s, filename=''): """Compiles a restricted expression.""" gen = RExpression(s, filename) return compileAndTuplize(gen) def compile_restricted(source, filename, mode): """Replacement for the builtin compile() function.""" if mode == "single": gen = RInteractive(source, filename) elif mode == "exec": gen = RModule(source, filename) elif mode == "eval": gen = RExpression(source, filename) else: raise ValueError("compile_restricted() 3rd arg must be 'exec' or " "'eval' or 'single'") gen.compile() return gen.getCode() class RestrictedCodeGenerator: """Mixin for CodeGenerator to replace UNPACK_SEQUENCE bytecodes. The UNPACK_SEQUENCE opcode is not safe because it extracts elements from a sequence without using a safe iterator or making __getitem__ checks. This code generator replaces use of UNPACK_SEQUENCE with calls to a function that unpacks the sequence, performes the appropriate security checks, and returns a simple list. """ # Replace the standard code generator for assignments to tuples # and lists. def _gen_safe_unpack_sequence(self, num): # We're at a place where UNPACK_SEQUENCE should be generated, to # unpack num items. That's a security hole, since it exposes # individual items from an arbitrary iterable. We don't remove # the UNPACK_SEQUENCE, but instead insert a call to our _getiter_() # wrapper first. That applies security checks to each item as # it's delivered. codegen is (just) a bit messy because the # iterable is already on the stack, so we have to do a stack swap # to get things in the right order. self.emit('LOAD_GLOBAL', '_getiter_') self.emit('ROT_TWO') self.emit('CALL_FUNCTION', 1) self.emit('UNPACK_SEQUENCE', num) def _visitAssSequence(self, node): if findOp(node) != 'OP_DELETE': self._gen_safe_unpack_sequence(len(node.nodes)) for child in node.nodes: self.visit(child) visitAssTuple = _visitAssSequence visitAssList = _visitAssSequence # Call to generate code for unpacking nested tuple arguments # in function calls. def unpackSequence(self, tup): self._gen_safe_unpack_sequence(len(tup)) for elt in tup: if isinstance(elt, tuple): self.unpackSequence(elt) else: self._nameOp('STORE', elt) # A collection of code generators that adds the restricted mixin to # handle unpacking for all the different compilation modes. They # are defined here (at the end) so that can refer to RestrictedCodeGenerator. class RestrictedFunctionCodeGenerator(RestrictedCodeGenerator, pycodegen.FunctionCodeGenerator): pass class RestrictedExpressionCodeGenerator(RestrictedCodeGenerator, pycodegen.ExpressionCodeGenerator): pass class RestrictedInteractiveCodeGenerator(RestrictedCodeGenerator, pycodegen.InteractiveCodeGenerator): pass class RestrictedModuleCodeGenerator(RestrictedCodeGenerator, pycodegen.ModuleCodeGenerator): def initClass(self): ModuleCodeGenerator.initClass(self) self.__class__.FunctionGen = RestrictedFunctionCodeGenerator # These subclasses work around the definition of stub compile and mode # attributes in the common base class AbstractCompileMode. If it # didn't define new attributes, then the stub code inherited via # RestrictedCompileMode would override the real definitions in # Expression. class RExpression(RestrictedCompileMode, Expression): mode = "eval" CodeGeneratorClass = RestrictedExpressionCodeGenerator class RInteractive(RestrictedCompileMode, Interactive): mode = "single" CodeGeneratorClass = RestrictedInteractiveCodeGenerator class RModule(RestrictedCompileMode, Module): mode = "exec" CodeGeneratorClass = RestrictedModuleCodeGenerator class RFunction(RModule): """A restricted Python function built from parts.""" CodeGeneratorClass = RestrictedModuleCodeGenerator def __init__(self, p, body, name, filename, globals): self.params = p if body: body = '\n'.join(body.splitlines()) + '\n' self.body = body self.name = name self.globals = globals or [] RModule.__init__(self, None, filename) def parse(self): # Parse the parameters and body, then combine them. firstline = 'def f(%s): pass' % self.params tree = niceParse(firstline, '', 'exec') f = tree.node.nodes[0] body_code = niceParse(self.body, self.filename, 'exec') # Stitch the body code into the function. f.code.nodes = body_code.node.nodes f.name = self.name # Look for a docstring, if there are any nodes at all if len(f.code.nodes) > 0: stmt1 = f.code.nodes[0] if (isinstance(stmt1, ast.Discard) and isinstance(stmt1.expr, ast.Const) and isinstance(stmt1.expr.value, str)): f.doc = stmt1.expr.value # The caller may specify that certain variables are globals # so that they can be referenced before a local assignment. # The only known example is the variables context, container, # script, traverse_subpath in PythonScripts. if self.globals: f.code.nodes.insert(0, ast.Global(self.globals)) return tree RestrictedPython-3.6.0/src/RestrictedPython/README.txt0000644000000000000000000001304211415563422022572 0ustar rootroot00000000000000.. contents:: Overview ======== RestrictedPython provides a ``restricted_compile`` function that works like the built-in ``compile`` function, except that it allows the controlled and restricted execution of code: >>> src = ''' ... def hello_world(): ... return "Hello World!" ... ''' >>> from RestrictedPython import compile_restricted >>> code = compile_restricted(src, '', 'exec') The resulting code can be executed using the ``exec`` built-in: >>> exec(code) As a result, the ``hello_world`` function is now available in the global namespace: >>> hello_world() 'Hello World!' Compatibility ============= This release of RestrictedPython is compatible with Python 2.3, 2.4, 2.5, 2.6, and 2.7. Implementing a policy ===================== RestrictedPython only provides the raw material for restricted execution. To actually enforce any restrictions, you need to supply a policy implementation by providing restricted versions of ``print``, ``getattr``, ``setattr``, ``import``, etc. These restricted implementations are hooked up by providing a set of specially named objects in the global dict that you use for execution of code. Specifically: 1. ``_print_`` is a callable object that returns a handler for print statements. This handler must have a ``write()`` method that accepts a single string argument, and must return a string when called. ``RestrictedPython.PrintCollector.PrintCollector`` is a suitable implementation. 2. ``_write_`` is a guard function taking a single argument. If the object passed to it may be written to, it should be returned, otherwise the guard function should raise an exception. ``_write`` is typically called on an object before a ``setattr`` operation. 3. ``_getattr_`` and ``_getitem_`` are guard functions, each of which takes two arguments. The first is the base object to be accessed, while the second is the attribute name or item index that will be read. The guard function should return the attribute or subitem, or raise an exception. 4. ``__import__`` is the normal Python import hook, and should be used to control access to Python packages and modules. 5. ``__builtins__`` is the normal Python builtins dictionary, which should be weeded down to a set that cannot be used to get around your restrictions. A usable "safe" set is ``RestrictedPython.Guards.safe_builtins``. To help illustrate how this works under the covers, here's an example function:: def f(x): x.foo = x.foo + x[0] print x return printed and (sort of) how it looks after restricted compilation:: def f(x): # Make local variables from globals. _print = _print_() _write = _write_ _getattr = _getattr_ _getitem = _getitem_ # Translation of f(x) above _write(x).foo = _getattr(x, 'foo') + _getitem(x, 0) print >>_print, x return _print() Examples ======== ``print`` --------- To support the ``print`` statement in restricted code, we supply a ``_print_`` object (note that it's a *factory*, e.g. a class or a callable, from which the restricted machinery will create the object): >>> from RestrictedPython.PrintCollector import PrintCollector >>> _print_ = PrintCollector >>> src = ''' ... print "Hello World!" ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) As you can see, the text doesn't appear on stdout. The print collector collects it. We can have access to the text using the ``printed`` variable, though: >>> src = ''' ... print "Hello World!" ... result = printed ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) >>> result 'Hello World!\n' Built-ins --------- By supplying a different ``__builtins__`` dictionary, we can rule out unsafe operations, such as opening files: >>> from RestrictedPython.Guards import safe_builtins >>> restricted_globals = dict(__builtins__ = safe_builtins) >>> src = ''' ... open('/etc/passwd') ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) in restricted_globals Traceback (most recent call last): ... NameError: name 'open' is not defined Guards ------ Here's an example of a write guard that never lets restricted code modify (assign, delete an attribute or item) except dictionaries and lists: >>> from RestrictedPython.Guards import full_write_guard >>> _write_ = full_write_guard >>> _getattr_ = getattr >>> class BikeShed(object): ... colour = 'green' ... >>> shed = BikeShed() Normally accessing attriutes works as expected, because we're using the standard ``getattr`` function for the ``_getattr_`` guard: >>> src = ''' ... print shed.colour ... result = printed ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) >>> result 'green\n' However, changing an attribute doesn't work: >>> src = ''' ... shed.colour = 'red' ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) Traceback (most recent call last): ... TypeError: attribute-less object (assign or del) As said, this particular write guard (``full_write_guard``) will allow restricted code to modify lists and dictionaries: >>> fibonacci = [1, 1, 2, 3, 4] >>> transl = dict(one=1, two=2, tres=3) >>> src = ''' ... # correct mistake in list ... fibonacci[-1] = 5 ... # one item doesn't belong ... del transl['tres'] ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) >>> fibonacci [1, 1, 2, 3, 5] >>> sorted(transl.keys()) ['one', 'two'] RestrictedPython-3.6.0/src/RestrictedPython/RestrictionMutator.py0000644000000000000000000003566311415563304025343 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## """Modify AST to include security checks. RestrictionMutator modifies a tree produced by compiler.transformer.Transformer, restricting and enhancing the code in various ways before sending it to pycodegen. $Revision: 1.13 $ """ from SelectCompiler import ast, parse, OP_ASSIGN, OP_DELETE, OP_APPLY # These utility functions allow us to generate AST subtrees without # line number attributes. These trees can then be inserted into other # trees without affecting line numbers shown in tracebacks, etc. def rmLineno(node): """Strip lineno attributes from a code tree.""" if node.__dict__.has_key('lineno'): del node.lineno for child in node.getChildren(): if isinstance(child, ast.Node): rmLineno(child) def stmtNode(txt): """Make a "clean" statement node.""" node = parse(txt).node.nodes[0] rmLineno(node) return node # The security checks are performed by a set of six functions that # must be provided by the restricted environment. _apply_name = ast.Name("_apply_") _getattr_name = ast.Name("_getattr_") _getitem_name = ast.Name("_getitem_") _getiter_name = ast.Name("_getiter_") _print_target_name = ast.Name("_print") _write_name = ast.Name("_write_") _inplacevar_name = ast.Name("_inplacevar_") # Constants. _None_const = ast.Const(None) _write_const = ast.Const("write") _printed_expr = stmtNode("_print()").expr _print_target_node = stmtNode("_print = _print_()") class FuncInfo: print_used = False printed_used = False class RestrictionMutator: def __init__(self): self.warnings = [] self.errors = [] self.used_names = {} self.funcinfo = FuncInfo() def error(self, node, info): """Records a security error discovered during compilation.""" lineno = getattr(node, 'lineno', None) if lineno is not None and lineno > 0: self.errors.append('Line %d: %s' % (lineno, info)) else: self.errors.append(info) def checkName(self, node, name): """Verifies that a name being assigned is safe. This is to prevent people from doing things like: __metatype__ = mytype (opens up metaclasses, a big unknown in terms of security) __path__ = foo (could this confuse the import machinery?) _getattr = somefunc (not very useful, but could open a hole) Note that assigning a variable is not the only way to assign a name. def _badname, class _badname, import foo as _badname, and perhaps other statements assign names. Special case: '_' is allowed. """ if name.startswith("_") and name != "_": # Note: "_" *is* allowed. self.error(node, '"%s" is an invalid variable name because' ' it starts with "_"' % name) if name.endswith('__roles__'): self.error(node, '"%s" is an invalid variable name because ' 'it ends with "__roles__".' % name) if name == "printed": self.error(node, '"printed" is a reserved name.') def checkAttrName(self, node): """Verifies that an attribute name does not start with _. As long as guards (security proxies) have underscored names, this underscore protection is important regardless of the security policy. Special case: '_' is allowed. """ name = node.attrname if name.startswith("_") and name != "_": # Note: "_" *is* allowed. self.error(node, '"%s" is an invalid attribute name ' 'because it starts with "_".' % name) if name.endswith('__roles__'): self.error(node, '"%s" is an invalid attribute name ' 'because it ends with "__roles__".' % name) def prepBody(self, body): """Insert code for print at the beginning of the code suite.""" if self.funcinfo.print_used or self.funcinfo.printed_used: # Add code at top for creating _print_target body.insert(0, _print_target_node) if not self.funcinfo.printed_used: self.warnings.append( "Prints, but never reads 'printed' variable.") elif not self.funcinfo.print_used: self.warnings.append( "Doesn't print, but reads 'printed' variable.") def visitFunction(self, node, walker): """Checks and mutates a function definition. Checks the name of the function and the argument names using checkName(). It also calls prepBody() to prepend code to the beginning of the code suite. """ self.checkName(node, node.name) for argname in node.argnames: if isinstance(argname, str): self.checkName(node, argname) else: for name in argname: self.checkName(node, name) walker.visitSequence(node.defaults) former_funcinfo = self.funcinfo self.funcinfo = FuncInfo() node = walker.defaultVisitNode(node, exclude=('defaults',)) self.prepBody(node.code.nodes) self.funcinfo = former_funcinfo return node def visitLambda(self, node, walker): """Checks and mutates an anonymous function definition. Checks the argument names using checkName(). It also calls prepBody() to prepend code to the beginning of the code suite. """ for argname in node.argnames: self.checkName(node, argname) return walker.defaultVisitNode(node) def visitPrint(self, node, walker): """Checks and mutates a print statement. Adds a target to all print statements. 'print foo' becomes 'print >> _print, foo', where _print is the default print target defined for this scope. Alternatively, if the untrusted code provides its own target, we have to check the 'write' method of the target. 'print >> ob, foo' becomes 'print >> (_getattr(ob, 'write') and ob), foo'. Otherwise, it would be possible to call the write method of templates and scripts; 'write' happens to be the name of the method that changes them. """ node = walker.defaultVisitNode(node) self.funcinfo.print_used = True if node.dest is None: node.dest = _print_target_name else: # Pre-validate access to the "write" attribute. # "print >> ob, x" becomes # "print >> (_getattr(ob, 'write') and ob), x" node.dest = ast.And([ ast.CallFunc(_getattr_name, [node.dest, _write_const]), node.dest]) return node visitPrintnl = visitPrint def visitName(self, node, walker): """Prevents access to protected names as defined by checkName(). Also converts use of the name 'printed' to an expression. """ if node.name == 'printed': # Replace name lookup with an expression. self.funcinfo.printed_used = True return _printed_expr self.checkName(node, node.name) self.used_names[node.name] = True return node def visitCallFunc(self, node, walker): """Checks calls with *-args and **-args. That's a way of spelling apply(), and needs to use our safe _apply_ instead. """ walked = walker.defaultVisitNode(node) if node.star_args is None and node.dstar_args is None: # This is not an extended function call return walked # Otherwise transform foo(a, b, c, d=e, f=g, *args, **kws) into a call # of _apply_(foo, a, b, c, d=e, f=g, *args, **kws). The interesting # thing here is that _apply_() is defined with just *args and **kws, # so it gets Python to collapse all the myriad ways to call functions # into one manageable form. # # From there, _apply_() digs out the first argument of *args (it's the # function to call), wraps args and kws in guarded accessors, then # calls the function, returning the value. # Transform foo(...) to _apply(foo, ...) walked.args.insert(0, walked.node) walked.node = _apply_name return walked def visitAssName(self, node, walker): """Checks a name assignment using checkName().""" self.checkName(node, node.name) return node def visitFor(self, node, walker): # convert # for x in expr: # to # for x in _getiter(expr): # # Note that visitListCompFor is the same thing. # # Also for list comprehensions: # [... for x in expr ...] # to # [... for x in _getiter(expr) ...] node = walker.defaultVisitNode(node) node.list = ast.CallFunc(_getiter_name, [node.list]) return node visitListCompFor = visitFor def visitGenExprFor(self, node, walker): # convert # (... for x in expr ...) # to # (... for x in _getiter(expr) ...) node = walker.defaultVisitNode(node) node.iter = ast.CallFunc(_getiter_name, [node.iter]) return node def visitGetattr(self, node, walker): """Converts attribute access to a function call. 'foo.bar' becomes '_getattr(foo, "bar")'. Also prevents augmented assignment of attributes, which would be difficult to support correctly. """ self.checkAttrName(node) node = walker.defaultVisitNode(node) if getattr(node, 'in_aug_assign', False): # We're in an augmented assignment # We might support this later... self.error(node, 'Augmented assignment of ' 'attributes is not allowed.') return ast.CallFunc(_getattr_name, [node.expr, ast.Const(node.attrname)]) def visitSubscript(self, node, walker): """Checks all kinds of subscripts. 'foo[bar] += baz' is disallowed. 'a = foo[bar, baz]' becomes 'a = _getitem(foo, (bar, baz))'. 'a = foo[bar]' becomes 'a = _getitem(foo, bar)'. 'a = foo[bar:baz]' becomes 'a = _getitem(foo, slice(bar, baz))'. 'a = foo[:baz]' becomes 'a = _getitem(foo, slice(None, baz))'. 'a = foo[bar:]' becomes 'a = _getitem(foo, slice(bar, None))'. 'del foo[bar]' becomes 'del _write(foo)[bar]'. 'foo[bar] = a' becomes '_write(foo)[bar] = a'. The _write function returns a security proxy. """ node = walker.defaultVisitNode(node) if node.flags == OP_APPLY: # Set 'subs' to the node that represents the subscript or slice. if getattr(node, 'in_aug_assign', False): # We're in an augmented assignment # We might support this later... self.error(node, 'Augmented assignment of ' 'object items and slices is not allowed.') if hasattr(node, 'subs'): # Subscript. subs = node.subs if len(subs) > 1: # example: ob[1,2] subs = ast.Tuple(subs) else: # example: ob[1] subs = subs[0] else: # Slice. # example: obj[0:2] lower = node.lower if lower is None: lower = _None_const upper = node.upper if upper is None: upper = _None_const subs = ast.Sliceobj([lower, upper]) return ast.CallFunc(_getitem_name, [node.expr, subs]) elif node.flags in (OP_DELETE, OP_ASSIGN): # set or remove subscript or slice node.expr = ast.CallFunc(_write_name, [node.expr]) return node visitSlice = visitSubscript def visitAssAttr(self, node, walker): """Checks and mutates attribute assignment. 'a.b = c' becomes '_write(a).b = c'. The _write function returns a security proxy. """ self.checkAttrName(node) node = walker.defaultVisitNode(node) node.expr = ast.CallFunc(_write_name, [node.expr]) return node def visitExec(self, node, walker): self.error(node, 'Exec statements are not allowed.') def visitYield(self, node, walker): self.error(node, 'Yield statements are not allowed.') def visitClass(self, node, walker): """Checks the name of a class using checkName(). Should classes be allowed at all? They don't cause security issues, but they aren't very useful either since untrusted code can't assign instance attributes. """ self.checkName(node, node.name) return walker.defaultVisitNode(node) def visitModule(self, node, walker): """Adds prep code at module scope. Zope doesn't make use of this. The body of Python scripts is always at function scope. """ node = walker.defaultVisitNode(node) self.prepBody(node.node.nodes) return node def visitAugAssign(self, node, walker): """Makes a note that augmented assignment is in use. Note that although augmented assignment of attributes and subscripts is disallowed, augmented assignment of names (such as 'n += 1') is allowed. This could be a problem if untrusted code got access to a mutable database object that supports augmented assignment. """ if node.node.__class__.__name__ == 'Name': node = walker.defaultVisitNode(node) newnode = ast.Assign( [ast.AssName(node.node.name, OP_ASSIGN)], ast.CallFunc( _inplacevar_name, [ast.Const(node.op), ast.Name(node.node.name), node.expr, ] ), ) newnode.lineno = node.lineno return newnode else: node.node.in_aug_assign = True return walker.defaultVisitNode(node) def visitImport(self, node, walker): """Checks names imported using checkName().""" for name, asname in node.names: self.checkName(node, name) if asname: self.checkName(node, asname) return node visitFrom = visitImport RestrictedPython-3.6.0/src/RestrictedPython/SelectCompiler.py0000644000000000000000000000171411415565736024375 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## """Compiler selector. """ # Use the compiler from the standard library. import compiler from compiler import ast from compiler.transformer import parse from compiler.consts import OP_ASSIGN, OP_DELETE, OP_APPLY from RCompile import \ compile_restricted, \ compile_restricted_function, \ compile_restricted_exec, \ compile_restricted_eval RestrictedPython-3.6.0/src/RestrictedPython/Utilities.py0000644000000000000000000000522411415154300023413 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## __version__='$Revision: 1.7 $'[11:-2] import math import random import string import warnings _old_filters = warnings.filters[:] warnings.filterwarnings('ignore', category=DeprecationWarning) try: try: import sets except ImportError: sets = None finally: warnings.filters[:] = _old_filters utility_builtins = {} utility_builtins['string'] = string utility_builtins['math'] = math utility_builtins['random'] = random utility_builtins['whrandom'] = random utility_builtins['sets'] = sets try: import DateTime utility_builtins['DateTime']= DateTime.DateTime except ImportError: pass def same_type(arg1, *args): '''Compares the class or type of two or more objects.''' t = getattr(arg1, '__class__', type(arg1)) for arg in args: if getattr(arg, '__class__', type(arg)) is not t: return 0 return 1 utility_builtins['same_type'] = same_type def test(*args): length = len(args) for i in range(1, length, 2): if args[i-1]: return args[i] if length % 2: return args[-1] utility_builtins['test'] = test def reorder(s, with_=None, without=()): # s, with_, and without are sequences treated as sets. # The result is subtract(intersect(s, with_), without), # unless with_ is None, in which case it is subtract(s, without). if with_ is None: with_ = s orig = {} for item in s: if isinstance(item, tuple) and len(item) == 2: key, value = item else: key = value = item orig[key] = value result = [] for item in without: if isinstance(item, tuple) and len(item) == 2: key, ignored = item else: key = item if key in orig: del orig[key] for item in with_: if isinstance(item, tuple) and len(item) == 2: key, ignored = item else: key = item if key in orig: result.append((key, orig[key])) del orig[key] return result utility_builtins['reorder'] = reorder RestrictedPython-3.6.0/src/RestrictedPython/tests/__init__.py0000644000000000000000000000002611415154300024334 0ustar rootroot00000000000000'''Python package.''' RestrictedPython-3.6.0/src/RestrictedPython/tests/before_and_after.py0000644000000000000000000001405511415565746026075 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Restricted Python transformation examples This module contains pairs of functions. Each pair has a before and an after function. The after function shows the source code equivalent of the before function after it has been modified by the restricted compiler. These examples are actually used in the testRestrictions.py checkBeforeAndAfter() unit tests, which verifies that the restricted compiler actually produces the same output as would be output by the normal compiler for the after function. """ # getattr def simple_getattr_before(x): return x.y def simple_getattr_after(x): return _getattr_(x, 'y') # set attr def simple_setattr_before(): x.y = "bar" def simple_setattr_after(): _write_(x).y = "bar" # for loop and list comprehensions def simple_forloop_before(x): for x in [1, 2, 3]: pass def simple_forloop_after(x): for x in _getiter_([1, 2, 3]): pass def nested_forloop_before(x): for x in [1, 2, 3]: for y in "abc": pass def nested_forloop_after(x): for x in _getiter_([1, 2, 3]): for y in _getiter_("abc"): pass def simple_list_comprehension_before(): x = [y**2 for y in whatever if y > 3] def simple_list_comprehension_after(): x = [y**2 for y in _getiter_(whatever) if y > 3] def nested_list_comprehension_before(): x = [x**2 + y**2 for x in whatever1 if x >= 0 for y in whatever2 if y >= x] def nested_list_comprehension_after(): x = [x**2 + y**2 for x in _getiter_(whatever1) if x >= 0 for y in _getiter_(whatever2) if y >= x] # print def simple_print_before(): print "foo" def simple_print_after(): _print = _print_() print >> _print, "foo" # getitem def simple_getitem_before(): return x[0] def simple_getitem_after(): return _getitem_(x, 0) def simple_get_tuple_key_before(): x = y[1,2] def simple_get_tuple_key_after(): x = _getitem_(y, (1,2)) # set item def simple_setitem_before(): x[0] = "bar" def simple_setitem_after(): _write_(x)[0] = "bar" # delitem def simple_delitem_before(): del x[0] def simple_delitem_after(): del _write_(x)[0] # a collection of function parallels to many of the above def function_with_print_before(): def foo(): print "foo" return printed def function_with_print_after(): def foo(): _print = _print_() print >> _print, "foo" return _print() def function_with_getattr_before(): def foo(): return x.y def function_with_getattr_after(): def foo(): return _getattr_(x, 'y') def function_with_setattr_before(): def foo(x): x.y = "bar" def function_with_setattr_after(): def foo(x): _write_(x).y = "bar" def function_with_getitem_before(): def foo(x): return x[0] def function_with_getitem_after(): def foo(x): return _getitem_(x, 0) def function_with_forloop_before(): def foo(): for x in [1, 2, 3]: pass def function_with_forloop_after(): def foo(): for x in _getiter_([1, 2, 3]): pass # this, and all slices, won't work in these tests because the before code # parses the slice as a slice object, while the after code can't generate a # slice object in this way. The after code as written below # is parsed as a call to the 'slice' name, not as a slice object. # XXX solutions? #def simple_slice_before(): # x = y[:4] #def simple_slice_after(): # _getitem = _getitem_ # x = _getitem(y, slice(None, 4)) # Assignment stmts in Python can be very complicated. The "no_unpack" # test makes sure we're not doing unnecessary rewriting. def no_unpack_before(): x = y x = [y] x = y, x = (y, (y, y), [y, (y,)], x, (x, y)) x = y = z = (x, y, z) no_unpack_after = no_unpack_before # that is, should be untouched # apply() variations. Native apply() is unsafe because, e.g., # # def f(a, b, c): # whatever # # apply(f, two_element_sequence, dict_with_key_c) # # or (different spelling of the same thing) # # f(*two_element_sequence, **dict_with_key_c) # # makes the elements of two_element_sequence visible to f via its 'a' and # 'b' arguments, and the dict_with_key_c['c'] value visible via its 'c' # argument. That is, it's a devious way to extract values without going # thru security checks. def star_call_before(): foo(*a) def star_call_after(): _apply_(foo, *a) def star_call_2_before(): foo(0, *a) def star_call_2_after(): _apply_(foo, 0, *a) def starstar_call_before(): foo(**d) def starstar_call_after(): _apply_(foo, **d) def star_and_starstar_call_before(): foo(*a, **d) def star_and_starstar_call_after(): _apply_(foo, *a, **d) def positional_and_star_and_starstar_call_before(): foo(b, *a, **d) def positional_and_star_and_starstar_call_after(): _apply_(foo, b, *a, **d) def positional_and_defaults_and_star_and_starstar_call_before(): foo(b, x=y, w=z, *a, **d) def positional_and_defaults_and_star_and_starstar_call_after(): _apply_(foo, b, x=y, w=z, *a, **d) def lambda_with_getattr_in_defaults_before(): f = lambda x=y.z: x def lambda_with_getattr_in_defaults_after(): f = lambda x=_getattr_(y, "z"): x # augmented operators # Note that we don't have to worry about item, attr, or slice assignment, # as they are disallowed. Yay! ## def inplace_id_add_before(): ## x += y+z ## def inplace_id_add_after(): ## x = _inplacevar_('+=', x, y+z) RestrictedPython-3.6.0/src/RestrictedPython/tests/before_and_after24.py0000644000000000000000000000314011415565756026235 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Restricted Python transformation examples This module contains pairs of functions. Each pair has a before and an after function. The after function shows the source code equivalent of the before function after it has been modified by the restricted compiler. These examples are actually used in the testRestrictions.py checkBeforeAndAfter() unit tests, which verifies that the restricted compiler actually produces the same output as would be output by the normal compiler for the after function. """ def simple_generator_expression_before(): x = (y**2 for y in whatever if y > 3) def simple_generator_expression_after(): x = (y**2 for y in _getiter_(whatever) if y > 3) def nested_generator_expression_before(): x = (x**2 + y**2 for x in whatever1 if x >= 0 for y in whatever2 if y >= x) def nested_generator_expression_after(): x = (x**2 + y**2 for x in _getiter_(whatever1) if x >= 0 for y in _getiter_(whatever2) if y >= x) RestrictedPython-3.6.0/src/RestrictedPython/tests/before_and_after25.py0000644000000000000000000000245011415565766026242 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Restricted Python transformation examples This module contains pairs of functions. Each pair has a before and an after function. The after function shows the source code equivalent of the before function after it has been modified by the restricted compiler. These examples are actually used in the testRestrictions.py checkBeforeAndAfter() unit tests, which verifies that the restricted compiler actually produces the same output as would be output by the normal compiler for the after function. """ def simple_ternary_if_before(): x.y = y.z if y.z else y.x def simple_ternary_if_after(): _write_(x).y = _getattr_(y, 'z') if _getattr_(y, 'z') else _getattr_(y, 'x') RestrictedPython-3.6.0/src/RestrictedPython/tests/before_and_after26.py0000644000000000000000000000317611415565774026250 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2008 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Restricted Python transformation examples This module contains pairs of functions. Each pair has a before and an after function. The after function shows the source code equivalent of the before function after it has been modified by the restricted compiler. These examples are actually used in the testRestrictions.py checkBeforeAndAfter() unit tests, which verifies that the restricted compiler actually produces the same output as would be output by the normal compiler for the after function. """ def simple_context_before(): with whatever as x: x.y = z def simple_context_after(): with whatever as x: _write_(x).y = z def simple_context_assign_attr_before(): with whatever as x.y: x.y = z def simple_context_assign_attr_after(): with whatever as _write_(x).y: _write_(x).y = z def simple_context_load_attr_before(): with whatever.w as z: x.y = z def simple_context_load_attr_after(): with _getattr_(whatever, 'w') as z: _write_(x).y = z RestrictedPython-3.6.0/src/RestrictedPython/tests/before_and_after27.py0000644000000000000000000000330011415563304026221 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2010 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Restricted Python transformation examples This module contains pairs of functions. Each pair has a before and an after function. The after function shows the source code equivalent of the before function after it has been modified by the restricted compiler. These examples are actually used in the testRestrictions.py checkBeforeAndAfter() unit tests, which verifies that the restricted compiler actually produces the same output as would be output by the normal compiler for the after function. """ # dictionary and set comprehensions def simple_dict_comprehension_before(): x = {y: y for y in whatever if y} def simple_dict_comprehension_after(): x = {y: y for y in _getiter_(whatever) if y} def dict_comprehension_attrs_before(): x = {y: y.q for y in whatever.z if y.q} def dict_comprehension_attrs_after(): x = {y: _getattr_(y, 'q') for y in _getiter_(_getattr_(whatever, 'z')) if _getattr_(y, 'q')} def simple_set_comprehension_before(): x = {y for y in whatever if y} def simple_set_comprehension_after(): x = {y for y in _getiter_(whatever) if y} RestrictedPython-3.6.0/src/RestrictedPython/tests/class.py0000644000000000000000000000034711415154300023710 0ustar rootroot00000000000000class MyClass: def set(self, val): self.state = val def get(self): return self.state x = MyClass() x.set(12) x.set(x.get() + 1) if x.get() != 13: raise AssertionError, "expected 13, got %d" % x.get() RestrictedPython-3.6.0/src/RestrictedPython/tests/lambda.py0000644000000000000000000000014211415154300024014 0ustar rootroot00000000000000f = lambda x, y=1: x + y if f(2) != 3: raise ValueError if f(2, 2) != 4: raise ValueError RestrictedPython-3.6.0/src/RestrictedPython/tests/restricted_module.py0000644000000000000000000000652511415154300026324 0ustar rootroot00000000000000import sys def print0(): print 'Hello, world!', return printed def print1(): print 'Hello,', print 'world!', return printed def printStuff(): print 'a', 'b', 'c', return printed def printToNone(): x = None print >>x, 'Hello, world!', return printed def printLines(): # This failed before Zope 2.4.0a2 r = range(3) for n in r: for m in r: print m + n * len(r), print return printed def try_map(): inc = lambda i: i+1 x = [1, 2, 3] print map(inc, x), return printed def try_apply(): def f(x, y, z): return x + y + z print f(*(300, 20), **{'z': 1}), return printed def try_inplace(): x = 1 x += 3 def primes(): # Somewhat obfuscated code on purpose print filter(None,map(lambda y:y*reduce(lambda x,y:x*y!=0, map(lambda x,y=y:y%x,range(2,int(pow(y,0.5)+1))),1),range(2,20))), return printed def allowed_read(ob): print ob.allowed print ob.s print ob[0] print ob[2] print ob[3:-1] print len(ob) return printed def allowed_default_args(ob): def f(a=ob.allowed, s=ob.s): return a, s def allowed_simple(): q = {'x':'a'} q['y'] = 'b' q.update({'z': 'c'}) r = ['a'] r.append('b') r[2:2] = ['c'] s = 'a' s = s[:100] + 'b' s += 'c' if sys.version_info >= (2, 3): t = ['l', 'm', 'n', 'o', 'p', 'q'] t[1:5:2] = ['n', 'p'] _ = q return q['x'] + q['y'] + q['z'] + r[0] + r[1] + r[2] + s def allowed_write(ob): ob.writeable = 1 #ob.writeable += 1 [1 for ob.writeable in 1,2] ob['safe'] = 2 #ob['safe'] += 2 [1 for ob['safe'] in 1,2] def denied_print(ob): print >> ob, 'Hello, world!', def denied_getattr(ob): #ob.disallowed += 1 ob.disallowed = 1 return ob.disallowed def denied_default_args(ob): def f(d=ob.disallowed): return d def denied_setattr(ob): ob.allowed = -1 def denied_setattr2(ob): #ob.allowed += -1 ob.allowed = -1 def denied_setattr3(ob): [1 for ob.allowed in 1,2] def denied_getitem(ob): ob[1] def denied_getitem2(ob): #ob[1] += 1 ob[1] def denied_setitem(ob): ob['x'] = 2 def denied_setitem2(ob): #ob[0] += 2 ob['x'] = 2 def denied_setitem3(ob): [1 for ob['x'] in 1,2] def denied_setslice(ob): ob[0:1] = 'a' def denied_setslice2(ob): #ob[0:1] += 'a' ob[0:1] = 'a' def denied_setslice3(ob): [1 for ob[0:1] in 1,2] ##def strange_attribute(): ## # If a guard has attributes with names that don't start with an ## # underscore, those attributes appear to be an attribute of ## # anything. ## return [].attribute_of_anything def order_of_operations(): return 3 * 4 * -2 + 2 * 12 def rot13(ss): mapping = {} orda = ord('a') ordA = ord('A') for n in range(13): c1 = chr(orda + n) c2 = chr(orda + n + 13) c3 = chr(ordA + n) c4 = chr(ordA + n + 13) mapping[c1] = c2 mapping[c2] = c1 mapping[c3] = c4 mapping[c4] = c3 del c1, c2, c3, c4, orda, ordA res = '' for c in ss: res = res + mapping.get(c, c) return res def nested_scopes_1(): # Fails if 'a' is consumed by the first function. a = 1 def f1(): return a def f2(): return a return f1() + f2() class Classic: pass RestrictedPython-3.6.0/src/RestrictedPython/tests/security_in_syntax.py0000644000000000000000000000272011415563304026553 0ustar rootroot00000000000000# These are all supposed to raise a SyntaxError when using # compile_restricted() but not when using compile(). # Each function in this module is compiled using compile_restricted(). def overrideGuardWithFunction(): def _getattr(o): return o def overrideGuardWithLambda(): lambda o, _getattr=None: o def overrideGuardWithClass(): class _getattr: pass def overrideGuardWithName(): _getattr = None def overrideGuardWithArgument(): def f(_getattr=None): pass def reserved_names(): printed = '' def bad_name(): __ = 12 def bad_attr(): some_ob._some_attr = 15 def no_exec(): exec 'q = 1' def no_yield(): yield 42 def check_getattr_in_lambda(arg=lambda _getattr=(lambda ob, name: name): _getattr): 42 def import_as_bad_name(): import os as _leading_underscore def from_import_as_bad_name(): from x import y as _leading_underscore def except_using_bad_name(): try: foo except NameError, _leading_underscore: # The name of choice (say, _write) is now assigned to an exception # object. Hard to exploit, but conceivable. pass def keyword_arg_with_bad_name(): def f(okname=1, __badname=2): pass def no_augmeneted_assignment_to_sub(): a[b] += c def no_augmeneted_assignment_to_attr(): a.b += c def no_augmeneted_assignment_to_slice(): a[x:y] += c def no_augmeneted_assignment_to_slice2(): a[x:y:z] += c RestrictedPython-3.6.0/src/RestrictedPython/tests/security_in_syntax26.py0000644000000000000000000000067211415563304026727 0ustar rootroot00000000000000# These are all supposed to raise a SyntaxError when using # compile_restricted() but not when using compile(). # Each function in this module is compiled using compile_restricted(). def with_as_bad_name(): with x as _leading_underscore: pass def relative_import_as_bad_name(): from .x import y as _leading_underscore def except_as_bad_name(): try: 1/0 except Exception as _leading_underscore: pass RestrictedPython-3.6.0/src/RestrictedPython/tests/security_in_syntax27.py0000644000000000000000000000060711415563304026726 0ustar rootroot00000000000000# These are all supposed to raise a SyntaxError when using # compile_restricted() but not when using compile(). # Each function in this module is compiled using compile_restricted(). def dict_comp_bad_name(): {y: y for _restricted_name in x} def set_comp_bad_name(): {y for _restricted_name in x} def compound_with_bad_name(): with a as b, c as _restricted_name: pass RestrictedPython-3.6.0/src/RestrictedPython/tests/testCompile.py0000644000000000000000000000253211415154300025071 0ustar rootroot00000000000000# -*- coding: utf-8 -*- ############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## __version__ = '$Revision: 110600 $'[11:-2] import unittest from RestrictedPython.RCompile import niceParse import compiler.ast class CompileTests(unittest.TestCase): def testUnicodeSource(self): # We support unicode sourcecode. source = u"u'Ä väry nice säntänce with umlauts.'" parsed = niceParse(source, "test.py", "exec") self.failUnless(isinstance(parsed, compiler.ast.Module)) parsed = niceParse(source, "test.py", "single") self.failUnless(isinstance(parsed, compiler.ast.Module)) parsed = niceParse(source, "test.py", "eval") self.failUnless(isinstance(parsed, compiler.ast.Expression)) def test_suite(): return unittest.makeSuite(CompileTests) RestrictedPython-3.6.0/src/RestrictedPython/tests/testREADME.py0000644000000000000000000000156611415154300024444 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2007 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Run tests in README.txt """ import unittest from doctest import DocFileSuite __docformat__ = "reStructuredText" def test_suite(): return unittest.TestSuite([ DocFileSuite('README.txt', package='RestrictedPython'), ]) RestrictedPython-3.6.0/src/RestrictedPython/tests/testRestrictions.py0000644000000000000000000004535311415563304026211 0ustar rootroot00000000000000import os import re import sys import unittest # Note that nothing should be imported from AccessControl, and in particular # nothing from ZopeGuards.py. Transformed code may need several wrappers # in order to run at all, and most of the production wrappers are defined # in ZopeGuards. But RestrictedPython isn't supposed to depend on # AccessControl, so we need to define throwaway wrapper implementations # here instead. from RestrictedPython import compile_restricted, PrintCollector from RestrictedPython.Eval import RestrictionCapableEval from RestrictedPython.tests import restricted_module, verify from RestrictedPython.RCompile import RModule, RFunction try: __file__ except NameError: __file__ = os.path.abspath(sys.argv[1]) _FILEPATH = os.path.abspath( __file__ ) _HERE = os.path.dirname( _FILEPATH ) def _getindent(line): """Returns the indentation level of the given line.""" indent = 0 for c in line: if c == ' ': indent = indent + 1 elif c == '\t': indent = indent + 8 else: break return indent def find_source(fn, func): """Given a func_code object, this function tries to find and return the python source code of the function. Originally written by Harm van der Heijden (H.v.d.Heijden@phys.tue.nl)""" f = open(fn,"r") for i in range(func.co_firstlineno): line = f.readline() ind = _getindent(line) msg = "" while line: msg = msg + line line = f.readline() # the following should be <= ind, but then we get # confused by multiline docstrings. Using == works most of # the time... but not always! if _getindent(line) == ind: break f.close() return fn, msg def get_source(func): """Less silly interface to find_source""" file = func.func_globals['__file__'] if file.endswith('.pyc'): file = file[:-1] source = find_source(file, func.func_code)[1] assert source.strip(), "Source should not be empty!" return source def create_rmodule(): global rmodule fn = os.path.join(_HERE, 'restricted_module.py') f = open(fn, 'r') source = f.read() f.close() # Sanity check compile(source, fn, 'exec') # Now compile it for real code = compile_restricted(source, fn, 'exec') rmodule = {'__builtins__':{'__import__':__import__, 'None':None, '__name__': 'restricted_module'}} builtins = getattr(__builtins__, '__dict__', __builtins__) for name in ('map', 'reduce', 'int', 'pow', 'range', 'filter', 'len', 'chr', 'ord', ): rmodule[name] = builtins[name] exec code in rmodule class AccessDenied (Exception): pass DisallowedObject = [] class RestrictedObject: disallowed = DisallowedObject allowed = 1 _ = 2 __ = 3 _some_attr = 4 __some_other_attr__ = 5 s = 'Another day, another test...' __writeable_attrs__ = ('writeable',) def __getitem__(self, idx): if idx == 'protected': raise AccessDenied elif idx == 0 or idx == 'safe': return 1 elif idx == 1: return DisallowedObject else: return self.s[idx] def __getslice__(self, lo, hi): return self.s[lo:hi] def __len__(self): return len(self.s) def __setitem__(self, idx, v): if idx == 'safe': self.safe = v else: raise AccessDenied def __setslice__(self, lo, hi, value): raise AccessDenied write = DisallowedObject def guarded_getattr(ob, name): v = getattr(ob, name) if v is DisallowedObject: raise AccessDenied return v SliceType = type(slice(0)) def guarded_getitem(ob, index): if type(index) is SliceType and index.step is None: start = index.start stop = index.stop if start is None: start = 0 if stop is None: v = ob[start:] else: v = ob[start:stop] else: v = ob[index] if v is DisallowedObject: raise AccessDenied return v def minimal_import(name, _globals, _locals, names): if name != "__future__": raise ValueError, "Only future imports are allowed" import __future__ return __future__ class TestGuard: '''A guard class''' def __init__(self, _ob, write=None): self.__dict__['_ob'] = _ob # Write guard methods def __setattr__(self, name, value): _ob = self.__dict__['_ob'] writeable = getattr(_ob, '__writeable_attrs__', ()) if name not in writeable: raise AccessDenied if name[:5] == 'func_': raise AccessDenied setattr(_ob, name, value) def __setitem__(self, index, value): _ob = self.__dict__['_ob'] _ob[index] = value def __setslice__(self, lo, hi, value): _ob = self.__dict__['_ob'] _ob[lo:hi] = value # A wrapper for _apply_. apply_wrapper_called = [] def apply_wrapper(func, *args, **kws): apply_wrapper_called.append('yes') return func(*args, **kws) inplacevar_wrapper_called = {} def inplacevar_wrapper(op, x, y): inplacevar_wrapper_called[op] = x, y # This is really lame. But it's just a test. :) globs = {'x': x, 'y': y} exec 'x'+op+'y' in globs return globs['x'] class RestrictionTests(unittest.TestCase): def execFunc(self, name, *args, **kw): func = rmodule[name] verify.verify(func.func_code) func.func_globals.update({'_getattr_': guarded_getattr, '_getitem_': guarded_getitem, '_write_': TestGuard, '_print_': PrintCollector, # I don't want to write something as involved as ZopeGuard's # SafeIter just for these tests. Using the builtin list() function # worked OK for everything the tests did at the time this was added, # but may fail in the future. If Python 2.1 is no longer an # interesting platform then, using 2.2's builtin iter() here should # work for everything. '_getiter_': list, '_apply_': apply_wrapper, '_inplacevar_': inplacevar_wrapper, }) return func(*args, **kw) def checkPrint(self): for i in range(2): res = self.execFunc('print%s' % i) self.assertEqual(res, 'Hello, world!') def checkPrintToNone(self): try: res = self.execFunc('printToNone') except AttributeError: # Passed. "None" has no "write" attribute. pass else: self.fail(0, res) def checkPrintStuff(self): res = self.execFunc('printStuff') self.assertEqual(res, 'a b c') def checkPrintLines(self): res = self.execFunc('printLines') self.assertEqual(res, '0 1 2\n3 4 5\n6 7 8\n') def checkPrimes(self): res = self.execFunc('primes') self.assertEqual(res, '[2, 3, 5, 7, 11, 13, 17, 19]') def checkAllowedSimple(self): res = self.execFunc('allowed_simple') self.assertEqual(res, 'abcabcabc') def checkAllowedRead(self): self.execFunc('allowed_read', RestrictedObject()) def checkAllowedWrite(self): self.execFunc('allowed_write', RestrictedObject()) def checkAllowedArgs(self): self.execFunc('allowed_default_args', RestrictedObject()) def checkTryMap(self): res = self.execFunc('try_map') self.assertEqual(res, "[2, 3, 4]") def checkApply(self): del apply_wrapper_called[:] res = self.execFunc('try_apply') self.assertEqual(apply_wrapper_called, ["yes"]) self.assertEqual(res, "321") def checkInplace(self): inplacevar_wrapper_called.clear() res = self.execFunc('try_inplace') self.assertEqual(inplacevar_wrapper_called['+='], (1, 3)) def checkDenied(self): for k in rmodule.keys(): if k[:6] == 'denied': try: self.execFunc(k, RestrictedObject()) except AccessDenied: # Passed the test pass else: self.fail('%s() did not trip security' % k) def checkSyntaxSecurity(self): self._checkSyntaxSecurity('security_in_syntax.py') if sys.version_info >= (2, 6): self._checkSyntaxSecurity('security_in_syntax26.py') if sys.version_info >= (2, 7): self._checkSyntaxSecurity('security_in_syntax27.py') def _checkSyntaxSecurity(self, mod_name): # Ensures that each of the functions in security_in_syntax.py # throws a SyntaxError when using compile_restricted. fn = os.path.join(_HERE, mod_name) f = open(fn, 'r') source = f.read() f.close() # Unrestricted compile. code = compile(source, fn, 'exec') m = {'__builtins__': {'__import__':minimal_import}} exec code in m for k, v in m.items(): if hasattr(v, 'func_code'): filename, source = find_source(fn, v.func_code) # Now compile it with restrictions try: code = compile_restricted(source, filename, 'exec') except SyntaxError: # Passed the test. pass else: self.fail('%s should not have compiled' % k) def checkOrderOfOperations(self): res = self.execFunc('order_of_operations') self.assertEqual(res, 0) def checkRot13(self): res = self.execFunc('rot13', 'Zope is k00l') self.assertEqual(res, 'Mbcr vf x00y') def checkNestedScopes1(self): res = self.execFunc('nested_scopes_1') self.assertEqual(res, 2) def checkUnrestrictedEval(self): expr = RestrictionCapableEval("{'a':[m.pop()]}['a'] + [m[0]]") v = [12, 34] expect = v[:] expect.reverse() res = expr.eval({'m':v}) self.assertEqual(res, expect) v = [12, 34] res = expr(m=v) self.assertEqual(res, expect) def checkStackSize(self): for k, rfunc in rmodule.items(): if not k.startswith('_') and hasattr(rfunc, 'func_code'): rss = rfunc.func_code.co_stacksize ss = getattr(restricted_module, k).func_code.co_stacksize self.failUnless( rss >= ss, 'The stack size estimate for %s() ' 'should have been at least %d, but was only %d' % (k, ss, rss)) def checkBeforeAndAfter(self): from RestrictedPython.RCompile import RModule from RestrictedPython.tests import before_and_after from compiler import parse defre = re.compile(r'def ([_A-Za-z0-9]+)_(after|before)\(') beforel = [name for name in before_and_after.__dict__ if name.endswith("_before")] for name in beforel: before = getattr(before_and_after, name) before_src = get_source(before) before_src = re.sub(defre, r'def \1(', before_src) rm = RModule(before_src, '') tree_before = rm._get_tree() after = getattr(before_and_after, name[:-6]+'after') after_src = get_source(after) after_src = re.sub(defre, r'def \1(', after_src) tree_after = parse(after_src) self.assertEqual(str(tree_before), str(tree_after)) rm.compile() verify.verify(rm.getCode()) def _checkBeforeAndAfter(self, mod): from RestrictedPython.RCompile import RModule from compiler import parse defre = re.compile(r'def ([_A-Za-z0-9]+)_(after|before)\(') beforel = [name for name in mod.__dict__ if name.endswith("_before")] for name in beforel: before = getattr(mod, name) before_src = get_source(before) before_src = re.sub(defre, r'def \1(', before_src) rm = RModule(before_src, '') tree_before = rm._get_tree() after = getattr(mod, name[:-6]+'after') after_src = get_source(after) after_src = re.sub(defre, r'def \1(', after_src) tree_after = parse(after_src) self.assertEqual(str(tree_before), str(tree_after)) rm.compile() verify.verify(rm.getCode()) if sys.version_info[:2] >= (2, 4): def checkBeforeAndAfter24(self): from RestrictedPython.tests import before_and_after24 self._checkBeforeAndAfter(before_and_after24) if sys.version_info[:2] >= (2, 5): def checkBeforeAndAfter25(self): from RestrictedPython.tests import before_and_after25 self._checkBeforeAndAfter(before_and_after25) if sys.version_info[:2] >= (2, 6): def checkBeforeAndAfter26(self): from RestrictedPython.tests import before_and_after26 self._checkBeforeAndAfter(before_and_after26) if sys.version_info[:2] >= (2, 7): def checkBeforeAndAfter27(self): from RestrictedPython.tests import before_and_after27 self._checkBeforeAndAfter(before_and_after27) def _compile_file(self, name): path = os.path.join(_HERE, name) f = open(path, "r") source = f.read() f.close() co = compile_restricted(source, path, "exec") verify.verify(co) return co def checkUnpackSequence(self): co = self._compile_file("unpack.py") calls = [] def getiter(seq): calls.append(seq) return list(seq) globals = {"_getiter_": getiter, '_inplacevar_': inplacevar_wrapper} exec co in globals, {} # The comparison here depends on the exact code that is # contained in unpack.py. # The test doing implicit unpacking in an "except:" clause is # a pain, because there are two levels of unpacking, and the top # level is unpacking the specific TypeError instance constructed # by the test. We have to worm around that one. ineffable = "a TypeError instance" expected = [[1, 2], (1, 2), "12", [1], [1, [2, 3], 4], [2, 3], (1, (2, 3), 4), (2, 3), [1, 2, 3], 2, ('a', 'b'), ((1, 2), (3, 4)), (1, 2), ((1, 2), (3, 4)), (3, 4), ineffable, [42, 666], [[0, 1], [2, 3], [4, 5]], [0, 1], [2, 3], [4, 5], ([[[1, 2]]], [[[3, 4]]]), [[[1, 2]]], [[1, 2]], [1, 2], [[[3, 4]]], [[3, 4]], [3, 4], ] i = expected.index(ineffable) self.assert_(isinstance(calls[i], TypeError)) expected[i] = calls[i] self.assertEqual(calls, expected) def checkUnpackSequenceExpression(self): co = compile_restricted("[x for x, y in [(1, 2)]]", "", "eval") verify.verify(co) calls = [] def getiter(s): calls.append(s) return list(s) globals = {"_getiter_": getiter} exec co in globals, {} self.assertEqual(calls, [[(1,2)], (1, 2)]) def checkUnpackSequenceSingle(self): co = compile_restricted("x, y = 1, 2", "", "single") verify.verify(co) calls = [] def getiter(s): calls.append(s) return list(s) globals = {"_getiter_": getiter} exec co in globals, {} self.assertEqual(calls, [(1, 2)]) def checkClass(self): getattr_calls = [] setattr_calls = [] def test_getattr(obj, attr): getattr_calls.append(attr) return getattr(obj, attr) def test_setattr(obj): setattr_calls.append(obj.__class__.__name__) return obj co = self._compile_file("class.py") globals = {"_getattr_": test_getattr, "_write_": test_setattr, } exec co in globals, {} # Note that the getattr calls don't correspond to the method call # order, because the x.set method is fetched before its arguments # are evaluated. self.assertEqual(getattr_calls, ["set", "set", "get", "state", "get", "state"]) self.assertEqual(setattr_calls, ["MyClass", "MyClass"]) def checkLambda(self): co = self._compile_file("lambda.py") exec co in {}, {} def checkEmpty(self): rf = RFunction("", "", "issue945", "empty.py", {}) rf.parse() rf2 = RFunction("", "# still empty\n\n# by", "issue945", "empty.py", {}) rf2.parse() def checkSyntaxError(self): err = ("def f(x, y):\n" " if x, y < 2 + 1:\n" " return x + y\n" " else:\n" " return x - y\n") self.assertRaises(SyntaxError, compile_restricted, err, "", "exec") # these two tests check that source code with Windows line # endings still works. def checkLineEndingsRFunction(self): from RestrictedPython.RCompile import RFunction gen = RFunction( p='', body='# testing\r\nprint "testing"\r\nreturn printed\n', name='test', filename='', globals=(), ) gen.mode = 'exec' # if the source has any line ending other than \n by the time # parse() is called, then you'll get a syntax error. gen.parse() def checkLineEndingsRestrictedCompileMode(self): from RestrictedPython.RCompile import RestrictedCompileMode gen = RestrictedCompileMode( '# testing\r\nprint "testing"\r\nreturn printed\n', '' ) gen.mode='exec' # if the source has any line ending other than \n by the time # parse() is called, then you'll get a syntax error. gen.parse() def checkCollector2295(self): from RestrictedPython.RCompile import RestrictedCompileMode gen = RestrictedCompileMode( 'if False:\n pass\n# Me Grok, Say Hi', '' ) gen.mode='exec' # if the source has any line ending other than \n by the time # parse() is called, then you'll get a syntax error. gen.parse() create_rmodule() def test_suite(): return unittest.makeSuite(RestrictionTests, 'check') if __name__=='__main__': unittest.main(defaultTest="test_suite") RestrictedPython-3.6.0/src/RestrictedPython/tests/testUtiliities.py0000644000000000000000000001362211415154300025627 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2009 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Run tests in README.txt """ import unittest class UtilitiesTests(unittest.TestCase): def test_string_in_utility_builtins(self): import string from RestrictedPython.Utilities import utility_builtins self.failUnless(utility_builtins['string'] is string) def test_math_in_utility_builtins(self): import math from RestrictedPython.Utilities import utility_builtins self.failUnless(utility_builtins['math'] is math) def test_whrandom_in_utility_builtins(self): import random from RestrictedPython.Utilities import utility_builtins self.failUnless(utility_builtins['whrandom'] is random) def test_random_in_utility_builtins(self): import random from RestrictedPython.Utilities import utility_builtins self.failUnless(utility_builtins['random'] is random) def test_sets_in_utility_builtins_if_importable(self): import warnings from RestrictedPython.Utilities import utility_builtins _old_filters = warnings.filters[:] warnings.filterwarnings('ignore', category=DeprecationWarning) try: try: import sets except ImportError: sets = None finally: warnings.filters[:] = _old_filters self.failUnless(utility_builtins['sets'] is sets) def test_DateTime_in_utility_builtins_if_importable(self): try: import DateTime except ImportError: pass else: from RestrictedPython.Utilities import utility_builtins self.failUnless('DateTime' in utility_builtins) def test_same_type_in_utility_builtins(self): from RestrictedPython.Utilities import same_type from RestrictedPython.Utilities import utility_builtins self.failUnless(utility_builtins['same_type'] is same_type) def test_test_in_utility_builtins(self): from RestrictedPython.Utilities import test from RestrictedPython.Utilities import utility_builtins self.failUnless(utility_builtins['test'] is test) def test_reorder_in_utility_builtins(self): from RestrictedPython.Utilities import reorder from RestrictedPython.Utilities import utility_builtins self.failUnless(utility_builtins['reorder'] is reorder) def test_sametype_only_one_arg(self): from RestrictedPython.Utilities import same_type self.failUnless(same_type(object())) def test_sametype_only_two_args_same(self): from RestrictedPython.Utilities import same_type self.failUnless(same_type(object(), object())) def test_sametype_only_two_args_different(self): from RestrictedPython.Utilities import same_type class Foo(object): pass self.failIf(same_type(object(), Foo())) def test_sametype_only_multiple_args_same(self): from RestrictedPython.Utilities import same_type self.failUnless(same_type(object(), object(), object(), object())) def test_sametype_only_multipe_args_one_different(self): from RestrictedPython.Utilities import same_type class Foo(object): pass self.failIf(same_type(object(), object(), Foo())) def test_test_single_value_true(self): from RestrictedPython.Utilities import test self.failUnless(test(True)) def test_test_single_value_False(self): from RestrictedPython.Utilities import test self.failIf(test(False)) def test_test_even_values_first_true(self): from RestrictedPython.Utilities import test self.assertEqual(test(True, 'first', True, 'second'), 'first') def test_test_even_values_not_first_true(self): from RestrictedPython.Utilities import test self.assertEqual(test(False, 'first', True, 'second'), 'second') def test_test_odd_values_first_true(self): from RestrictedPython.Utilities import test self.assertEqual(test(True, 'first', True, 'second', False), 'first') def test_test_odd_values_not_first_true(self): from RestrictedPython.Utilities import test self.assertEqual(test(False, 'first', True, 'second', False), 'second') def test_test_odd_values_last_true(self): from RestrictedPython.Utilities import test self.assertEqual(test(False, 'first', False, 'second', 'third'), 'third') def test_test_odd_values_last_false(self): from RestrictedPython.Utilities import test self.assertEqual(test(False, 'first', False, 'second', False), False) def test_reorder_with__None(self): from RestrictedPython.Utilities import reorder before = ['a', 'b', 'c', 'd', 'e'] without = ['a', 'c', 'e'] after = reorder(before, without=without) self.assertEqual(after, [('b', 'b'), ('d', 'd')]) def test_reorder_with__not_None(self): from RestrictedPython.Utilities import reorder before = ['a', 'b', 'c', 'd', 'e'] with_ = ['a', 'd'] without = ['a', 'c', 'e'] after = reorder(before, with_=with_, without=without) self.assertEqual(after, [('d', 'd')]) def test_suite(): return unittest.TestSuite(( unittest.makeSuite(UtilitiesTests), )) if __name__ == '__main__': unittest.main(defaultTest='test_suite') RestrictedPython-3.6.0/src/RestrictedPython/tests/unpack.py0000644000000000000000000000263511415154300024066 0ustar rootroot00000000000000# A series of short tests for unpacking sequences. def u1(L): x, y = L assert x == 1 assert y == 2 u1([1,2]) u1((1, 2)) def u1a(L): x, y = L assert x == '1' assert y == '2' u1a("12") try: u1([1]) except ValueError: pass else: raise AssertionError, "expected 'unpack list of wrong size'" def u2(L): x, (a, b), y = L assert x == 1 assert a == 2 assert b == 3 assert y == 4 u2([1, [2, 3], 4]) u2((1, (2, 3), 4)) try: u2([1, 2, 3]) except TypeError: pass else: raise AssertionError, "expected 'iteration over non-sequence'" def u3((x, y)): assert x == 'a' assert y == 'b' return x, y u3(('a', 'b')) def u4(x): (a, b), c = d, (e, f) = x assert a == 1 and b == 2 and c == (3, 4) assert d == (1, 2) and e == 3 and f == 4 u4( ((1, 2), (3, 4)) ) def u5(x): try: raise TypeError(x) # This one is tricky to test, because the first level of unpacking # has a TypeError instance. That's a headache for the test driver. except TypeError, [(a, b)]: assert a == 42 assert b == 666 u5([42, 666]) def u6(x): expected = 0 for i, j in x: assert i == expected expected += 1 assert j == expected expected += 1 u6([[0, 1], [2, 3], [4, 5]]) def u7(x): stuff = [i + j for toplevel, in x for i, j in toplevel] assert stuff == [3, 7] u7( ([[[1, 2]]], [[[3, 4]]]) ) RestrictedPython-3.6.0/src/RestrictedPython/tests/verify.py0000644000000000000000000001711711415154300024112 0ustar rootroot00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE # ############################################################################## """Verify simple properties of bytecode. Some of the transformations performed by the RestrictionMutator are tricky. This module checks the generated bytecode as a way to verify the correctness of the transformations. Violations of some restrictions are obvious from inspection of the bytecode. For example, the bytecode should never contain a LOAD_ATTR call, because all attribute access is performed via the _getattr_() checker function. """ import dis import types def verify(code): """Verify all code objects reachable from code. In particular, traverse into contained code objects in the co_consts table. """ verifycode(code) for ob in code.co_consts: if isinstance(ob, types.CodeType): verify(ob) def verifycode(code): try: _verifycode(code) except: dis.dis(code) raise def _verifycode(code): line = code.co_firstlineno # keep a window of the last three opcodes, with the most recent first window = (None, None, None) with_context = (None, None) for op in disassemble(code): if op.line is not None: line = op.line if op.opname.endswith("LOAD_ATTR"): # All the user code that generates LOAD_ATTR should be # rewritten, but the code generated for a list comp # includes a LOAD_ATTR to extract the append method. # Another exception is the new-in-Python 2.6 'context # managers', which do a LOAD_ATTR for __exit__ and # __enter__. if op.arg == "__exit__": with_context = (op, with_context[1]) elif op.arg == "__enter__": with_context = (with_context[0], op) elif not ((op.arg == "__enter__" and window[0].opname == "ROT_TWO" and window[1].opname == "DUP_TOP") or (op.arg == "append" and window[0].opname == "DUP_TOP" and window[1].opname == "BUILD_LIST")): raise ValueError("direct attribute access %s: %s, %s:%d" % (op.opname, op.arg, code.co_filename, line)) if op.opname in ("WITH_CLEANUP"): # Here we check if the LOAD_ATTR for __exit__ and # __enter__ were part of a 'with' statement by checking # for the 'WITH_CLEANUP' bytecode. If one is seen, we # clear the with_context variable and let it go. The # access was safe. with_context = (None, None) if op.opname in ("STORE_ATTR", "DEL_ATTR"): if not (window[0].opname == "CALL_FUNCTION" and window[2].opname == "LOAD_GLOBAL" and window[2].arg == "_write_"): # check that arg is appropriately wrapped for i, op in enumerate(window): print i, op.opname, op.arg raise ValueError("unguard attribute set/del at %s:%d" % (code.co_filename, line)) if op.opname.startswith("UNPACK"): # An UNPACK opcode extracts items from iterables, and that's # unsafe. The restricted compiler doesn't remove UNPACK opcodes, # but rather *inserts* a call to _getiter_() before each, and # that's the pattern we need to see. if not (window[0].opname == "CALL_FUNCTION" and window[1].opname == "ROT_TWO" and window[2].opname == "LOAD_GLOBAL" and window[2].arg == "_getiter_"): raise ValueError("unguarded unpack sequence at %s:%d" % (code.co_filename, line)) # should check CALL_FUNCTION_{VAR,KW,VAR_KW} but that would # require a potentially unlimited history. need to refactor # the "window" before I can do that. if op.opname == "LOAD_SUBSCR": raise ValueError("unguarded index of sequence at %s:%d" % (code.co_filename, line)) window = (op,) + window[:2] if not with_context == (None, None): # An access to __enter__ and __exit__ was performed but not as # part of a 'with' statement. This is not allowed. for op in with_context: if op is not None: if op.line is not None: line = op.line raise ValueError("direct attribute access %s: %s, %s:%d" % (op.opname, op.arg, code.co_filename, line)) class Op(object): __slots__ = ( "opname", # string, name of the opcode "argcode", # int, the number of the argument "arg", # any, the object, name, or value of argcode "line", # int, line number or None "target", # boolean, is this op the target of a jump "pos", # int, offset in the bytecode ) def __init__(self, opcode, pos): self.opname = dis.opname[opcode] self.arg = None self.line = None self.target = False self.pos = pos def disassemble(co, lasti=-1): code = co.co_code labels = dis.findlabels(code) linestarts = dict(findlinestarts(co)) n = len(code) i = 0 extended_arg = 0 free = co.co_cellvars + co.co_freevars while i < n: op = ord(code[i]) o = Op(op, i) i += 1 if i in linestarts and i > 0: o.line = linestarts[i] if i in labels: o.target = True if op > dis.HAVE_ARGUMENT: arg = ord(code[i]) + ord(code[i+1]) * 256 + extended_arg extended_arg = 0 i += 2 if op == dis.EXTENDED_ARG: extended_arg = arg << 16 o.argcode = arg if op in dis.hasconst: o.arg = co.co_consts[arg] elif op in dis.hasname: o.arg = co.co_names[arg] elif op in dis.hasjrel: o.arg = i + arg elif op in dis.haslocal: o.arg = co.co_varnames[arg] elif op in dis.hascompare: o.arg = dis.cmp_op[arg] elif op in dis.hasfree: o.arg = free[arg] yield o # findlinestarts is copied from Python 2.4's dis module. The code # didn't exist in 2.3, but it would be painful to code disassemble() # without it. def findlinestarts(code): """Find the offsets in a byte code which are start of lines in the source. Generate pairs (offset, lineno) as described in Python/compile.c. """ byte_increments = [ord(c) for c in code.co_lnotab[0::2]] line_increments = [ord(c) for c in code.co_lnotab[1::2]] lastlineno = None lineno = code.co_firstlineno addr = 0 for byte_incr, line_incr in zip(byte_increments, line_increments): if byte_incr: if lineno != lastlineno: yield (addr, lineno) lastlineno = lineno addr += byte_incr lineno += line_incr if lineno != lastlineno: yield (addr, lineno) RestrictedPython-3.6.0/src/RestrictedPython.egg-info/dependency_links.txt0000644000000000000000000000000111415572640026636 0ustar rootroot00000000000000 RestrictedPython-3.6.0/src/RestrictedPython.egg-info/not-zip-safe0000644000000000000000000000000111415572636025023 0ustar rootroot00000000000000 RestrictedPython-3.6.0/src/RestrictedPython.egg-info/PKG-INFO0000644000000000000000000002466411415572640023701 0ustar rootroot00000000000000Metadata-Version: 1.0 Name: RestrictedPython Version: 3.6.0 Summary: RestrictedPython provides a restricted execution environment for Python, e.g. for running untrusted code. Home-page: http://pypi.python.org/pypi/RestrictedPython Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: .. contents:: Overview ======== RestrictedPython provides a ``restricted_compile`` function that works like the built-in ``compile`` function, except that it allows the controlled and restricted execution of code: >>> src = ''' ... def hello_world(): ... return "Hello World!" ... ''' >>> from RestrictedPython import compile_restricted >>> code = compile_restricted(src, '', 'exec') The resulting code can be executed using the ``exec`` built-in: >>> exec(code) As a result, the ``hello_world`` function is now available in the global namespace: >>> hello_world() 'Hello World!' Compatibility ============= This release of RestrictedPython is compatible with Python 2.3, 2.4, 2.5, 2.6, and 2.7. Implementing a policy ===================== RestrictedPython only provides the raw material for restricted execution. To actually enforce any restrictions, you need to supply a policy implementation by providing restricted versions of ``print``, ``getattr``, ``setattr``, ``import``, etc. These restricted implementations are hooked up by providing a set of specially named objects in the global dict that you use for execution of code. Specifically: 1. ``_print_`` is a callable object that returns a handler for print statements. This handler must have a ``write()`` method that accepts a single string argument, and must return a string when called. ``RestrictedPython.PrintCollector.PrintCollector`` is a suitable implementation. 2. ``_write_`` is a guard function taking a single argument. If the object passed to it may be written to, it should be returned, otherwise the guard function should raise an exception. ``_write`` is typically called on an object before a ``setattr`` operation. 3. ``_getattr_`` and ``_getitem_`` are guard functions, each of which takes two arguments. The first is the base object to be accessed, while the second is the attribute name or item index that will be read. The guard function should return the attribute or subitem, or raise an exception. 4. ``__import__`` is the normal Python import hook, and should be used to control access to Python packages and modules. 5. ``__builtins__`` is the normal Python builtins dictionary, which should be weeded down to a set that cannot be used to get around your restrictions. A usable "safe" set is ``RestrictedPython.Guards.safe_builtins``. To help illustrate how this works under the covers, here's an example function:: def f(x): x.foo = x.foo + x[0] print x return printed and (sort of) how it looks after restricted compilation:: def f(x): # Make local variables from globals. _print = _print_() _write = _write_ _getattr = _getattr_ _getitem = _getitem_ # Translation of f(x) above _write(x).foo = _getattr(x, 'foo') + _getitem(x, 0) print >>_print, x return _print() Examples ======== ``print`` --------- To support the ``print`` statement in restricted code, we supply a ``_print_`` object (note that it's a *factory*, e.g. a class or a callable, from which the restricted machinery will create the object): >>> from RestrictedPython.PrintCollector import PrintCollector >>> _print_ = PrintCollector >>> src = ''' ... print "Hello World!" ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) As you can see, the text doesn't appear on stdout. The print collector collects it. We can have access to the text using the ``printed`` variable, though: >>> src = ''' ... print "Hello World!" ... result = printed ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) >>> result 'Hello World!\n' Built-ins --------- By supplying a different ``__builtins__`` dictionary, we can rule out unsafe operations, such as opening files: >>> from RestrictedPython.Guards import safe_builtins >>> restricted_globals = dict(__builtins__ = safe_builtins) >>> src = ''' ... open('/etc/passwd') ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) in restricted_globals Traceback (most recent call last): ... NameError: name 'open' is not defined Guards ------ Here's an example of a write guard that never lets restricted code modify (assign, delete an attribute or item) except dictionaries and lists: >>> from RestrictedPython.Guards import full_write_guard >>> _write_ = full_write_guard >>> _getattr_ = getattr >>> class BikeShed(object): ... colour = 'green' ... >>> shed = BikeShed() Normally accessing attriutes works as expected, because we're using the standard ``getattr`` function for the ``_getattr_`` guard: >>> src = ''' ... print shed.colour ... result = printed ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) >>> result 'green\n' However, changing an attribute doesn't work: >>> src = ''' ... shed.colour = 'red' ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) Traceback (most recent call last): ... TypeError: attribute-less object (assign or del) As said, this particular write guard (``full_write_guard``) will allow restricted code to modify lists and dictionaries: >>> fibonacci = [1, 1, 2, 3, 4] >>> transl = dict(one=1, two=2, tres=3) >>> src = ''' ... # correct mistake in list ... fibonacci[-1] = 5 ... # one item doesn't belong ... del transl['tres'] ... ''' >>> code = compile_restricted(src, '', 'exec') >>> exec(code) >>> fibonacci [1, 1, 2, 3, 5] >>> sorted(transl.keys()) ['one', 'two'] Changes ======= 3.6.0 (2010-07-09) ------------------ - Added name check for names assigned during imports using the "from x import y" format. - Added test for name check when assigning an alias using multiple-context with statements in Python 2.7. - Added tests for protection of the iterators for dict and set comprehensions in Python 2.7. 3.6.0a1 (2010-06-05) -------------------- - Removed support for DocumentTemplate.sequence - this is handled in the DocumentTemplate package itself. 3.5.2 (2010-04-30) ------------------ - Removed a testing dependency on zope.testing. 3.5.1 (2009-03-17) ------------------ - Added tests for ``Utilities`` module. - Filtered DeprecationWarnings when importing Python's ``sets`` module. 3.5.0 (2009-02-09) ------------------ - Dropped legacy support for Python 2.1 / 2.2 (``__future__`` imports of ``nested_scopes`` / ``generators``.). 3.4.3 (2008-10-26) ------------------ - Fixed deprecation warning: ``with`` is now a reserved keyword on Python 2.6. That means RestrictedPython should run on Python 2.6 now. Thanks to Ranjith Kannikara, GSoC Student for the patch. - Added tests for ternary if expression and for 'with' keyword and context managers. 3.4.2 (2007-07-28) ------------------ - Changed homepage URL to the CheeseShop site - Greatly improved README.txt 3.4.1 (2007-06-23) ------------------ - Fixed http://www.zope.org/Collectors/Zope/2295: Bare conditional in a Zope 2 PythonScript followed by a comment causes SyntaxError. 3.4.0 (2007-06-04) ------------------ - RestrictedPython now has its own release cycle as a separate egg. - Synchronized with RestrictedPython from Zope 2 tree. 3.2.0 (2006-01-05) ------------------ - Corresponds to the verison of the RestrictedPython package shipped as part of the Zope 3.2.0 release. - No changes from 3.1.0. 3.1.0 (2005-10-03) ------------------ - Corresponds to the verison of the RestrictedPython package shipped as part of the Zope 3.1.0 release. - Removed unused fossil module, ``SafeMapping``. - Replaced use of deprecated 'whrandom' module with 'random' (aliased to 'whrandom' for backward compatibility). 3.0.0 (2004-11-07) ------------------ - Corresponds to the verison of the RestrictedPython package shipped as part of the Zope X3.0.0 release. Platform: UNKNOWN RestrictedPython-3.6.0/src/RestrictedPython.egg-info/requires.txt0000644000000000000000000000001211415572640025161 0ustar rootroot00000000000000setuptoolsRestrictedPython-3.6.0/src/RestrictedPython.egg-info/SOURCES.txt0000644000000000000000000000303211415572640024452 0ustar rootroot00000000000000CHANGES.txt COPYRIGHT.txt LICENSE.txt README.txt bootstrap.py buildout.cfg setup.py src/RestrictedPython/Eval.py src/RestrictedPython/Guards.py src/RestrictedPython/Limits.py src/RestrictedPython/MutatingWalker.py src/RestrictedPython/PrintCollector.py src/RestrictedPython/RCompile.py src/RestrictedPython/README.txt src/RestrictedPython/RestrictionMutator.py src/RestrictedPython/SelectCompiler.py src/RestrictedPython/Utilities.py src/RestrictedPython/__init__.py src/RestrictedPython/notes.txt src/RestrictedPython.egg-info/PKG-INFO src/RestrictedPython.egg-info/SOURCES.txt src/RestrictedPython.egg-info/dependency_links.txt src/RestrictedPython.egg-info/not-zip-safe src/RestrictedPython.egg-info/requires.txt src/RestrictedPython.egg-info/top_level.txt src/RestrictedPython/tests/__init__.py src/RestrictedPython/tests/before_and_after.py src/RestrictedPython/tests/before_and_after24.py src/RestrictedPython/tests/before_and_after25.py src/RestrictedPython/tests/before_and_after26.py src/RestrictedPython/tests/before_and_after27.py src/RestrictedPython/tests/class.py src/RestrictedPython/tests/lambda.py src/RestrictedPython/tests/restricted_module.py src/RestrictedPython/tests/security_in_syntax.py src/RestrictedPython/tests/security_in_syntax26.py src/RestrictedPython/tests/security_in_syntax27.py src/RestrictedPython/tests/testCompile.py src/RestrictedPython/tests/testREADME.py src/RestrictedPython/tests/testRestrictions.py src/RestrictedPython/tests/testUtiliities.py src/RestrictedPython/tests/unpack.py src/RestrictedPython/tests/verify.pyRestrictedPython-3.6.0/src/RestrictedPython.egg-info/top_level.txt0000644000000000000000000000002111415572640025313 0ustar rootroot00000000000000RestrictedPython