[Checkins] SVN: RestrictedPython/trunk/ Greatly improved
README.txt. Large parts were borrowed from a very helpful
Philipp von Weitershausen
philikon at philikon.de
Sat Jul 28 05:49:09 EDT 2007
Log message for revision 78416:
Greatly improved README.txt. Large parts were borrowed from a very helpful
email by Evan Simpson 4 (!) years ago:
http://mail.python.org/pipermail/python-list/2003-October/228231.html
Changed:
U RestrictedPython/trunk/README.txt
U RestrictedPython/trunk/setup.py
-=-
Modified: RestrictedPython/trunk/README.txt
===================================================================
--- RestrictedPython/trunk/README.txt 2007-07-28 09:48:00 UTC (rev 78415)
+++ RestrictedPython/trunk/README.txt 2007-07-28 09:49:09 UTC (rev 78416)
@@ -1,9 +1,207 @@
-RestrictedPython provides a restricted execution environment for
-Python, e.g. for running untrusted code.
+.. 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, '<string>', '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!'
+
+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, '<string>', '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, '<string>', '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, '<string>', '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, '<string>', 'exec')
+ >>> exec(code)
+
+ >>> result
+ 'green\n'
+
+However, changing an attribute doesn't work:
+
+ >>> src = '''
+ ... shed.colour = 'red'
+ ... '''
+ >>> code = compile_restricted(src, '<string>', '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, '<string>', 'exec')
+ >>> exec(code)
+
+ >>> fibonacci
+ [1, 1, 2, 3, 5]
+ >>> sorted(transl.keys())
+ ['one', 'two']
+
Changes
=======
+3.4.2 (2007/07/28)
+------------------
+
+- Changed homepage URL to the CheeseShop site
+
+- Greatly improved README.txt
+
3.4.1 (2007/06/23)
------------------
Modified: RestrictedPython/trunk/setup.py
===================================================================
--- RestrictedPython/trunk/setup.py 2007-07-28 09:48:00 UTC (rev 78415)
+++ RestrictedPython/trunk/setup.py 2007-07-28 09:49:09 UTC (rev 78416)
@@ -25,10 +25,11 @@
version='3.4.2',
url='http://cheeseshop.zope.org/pypi/RestrictedPython',
license='ZPL 2.1',
- description='Restricted Python execution handlers',
+ description='RestrictedPython provides a restricted execution '
+ 'environment for Python, e.g. for running untrusted code.',
author='Zope Corporation and Contributors',
author_email='zope3-dev at zope.org',
- long_description=open('README.xt').read(),
+ long_description=open('README.txt').read(),
packages = find_packages('src'),
package_dir = {'': 'src'},
More information about the Checkins
mailing list