[Zope3-checkins] SVN: Zope3/trunk/src/ Implemented a restricted Python interpreter for TALES expressions.

Fred L. Drake, Jr. fred at zope.com
Thu Jul 22 16:44:21 EDT 2004


Log message for revision 26688:
  Implemented a restricted Python interpreter for TALES expressions.
  
  - Copied the RestrictedPython package from the Zope 2 trunk, rev 26679
    (we're not using most of it, but copying this as a unit makes the
    most sense)
  
  - Created a new package, zope.restrictedpython, that uses
    RestrictedPython to implement the corresponding Zope 3 structures
  
  - Use zope.restrictedpython in zope.app.pagetemplate to provide
    restricted execution for Python expressions in page templates
  


Changed:
  A   Zope3/trunk/src/RestrictedPython/
  U   Zope3/trunk/src/zope/app/pagetemplate/engine.py
  A   Zope3/trunk/src/zope/restrictedpython/
  A   Zope3/trunk/src/zope/restrictedpython/DEPENDENCIES.cfg
  A   Zope3/trunk/src/zope/restrictedpython/README.txt
  A   Zope3/trunk/src/zope/restrictedpython/__init__.py
  A   Zope3/trunk/src/zope/restrictedpython/mutator.py
  A   Zope3/trunk/src/zope/restrictedpython/rcompile.py
  A   Zope3/trunk/src/zope/restrictedpython/tests.py


-=-
Copied: Zope3/trunk/src/RestrictedPython (from rev 26679, Zope/trunk/lib/python/RestrictedPython)

Modified: Zope3/trunk/src/zope/app/pagetemplate/engine.py
===================================================================
--- Zope3/trunk/src/zope/app/pagetemplate/engine.py	2004-07-22 20:35:49 UTC (rev 26687)
+++ Zope3/trunk/src/zope/app/pagetemplate/engine.py	2004-07-22 20:44:21 UTC (rev 26688)
@@ -29,6 +29,7 @@
 from zope.component.exceptions import ComponentLookupError
 from zope.exceptions import NotFoundError
 from zope.proxy import removeAllProxies
+from zope.restrictedpython import rcompile
 from zope.security.proxy import ProxyFactory
 from zope.security.builtins import RestrictedBuiltins
 from zope.i18n import translate
@@ -54,6 +55,23 @@
     def __init__(self, name, expr, engine):
         super(ZopePathExpr, self).__init__(name, expr, engine, zopeTraverser)
 
+
+# Create a version of the restricted built-ins that uses a safe
+# version of getattr() that wraps values in security proxies where
+# appropriate:
+
+_marker = object()
+
+def safe_getattr(object, name, default=_marker):
+    if default is _marker:
+        return ProxyFactory(getattr(object, name))
+    else:
+        return ProxyFactory(getattr(object, name, default))
+
+RestrictedBuiltins = RestrictedBuiltins.copy()
+RestrictedBuiltins["getattr"] = safe_getattr
+
+
 class ZopePythonExpr(PythonExpr):
 
     def __call__(self, econtext):
@@ -61,6 +79,10 @@
         vars = self._bind_used_names(econtext, RestrictedBuiltins)
         return eval(self._code, vars)
 
+    def _compile(self, text, filename):
+        return rcompile.compile(text, filename, 'eval')
+
+
 class ZopeContextBase(Context):
     """Base class for both trusted and untrusted evaluation contexts."""
 
@@ -205,6 +227,16 @@
         ...
       ForbiddenAttribute: ('_getframe', <module 'sys' (built-in)>)
 
+    The results of Python expressions evaluated by this engine are
+    wrapped in security proxies::
+
+      >>> r = context.evaluate('python: {12: object()}.values')
+      >>> type(r)
+      <type 'zope.security._proxy._Proxy'>
+      >>> r = context.evaluate('python: {12: object()}.values()[0].__class__')
+      >>> type(r)
+      <type 'zope.security._proxy._Proxy'>
+
     """
 
     _create_context = ZopeContext

Added: Zope3/trunk/src/zope/restrictedpython/DEPENDENCIES.cfg
===================================================================
--- Zope3/trunk/src/zope/restrictedpython/DEPENDENCIES.cfg	2004-07-22 20:35:49 UTC (rev 26687)
+++ Zope3/trunk/src/zope/restrictedpython/DEPENDENCIES.cfg	2004-07-22 20:44:21 UTC (rev 26688)
@@ -0,0 +1,2 @@
+zope.testing
+RestrictedPython

Added: Zope3/trunk/src/zope/restrictedpython/README.txt
===================================================================
--- Zope3/trunk/src/zope/restrictedpython/README.txt	2004-07-22 20:35:49 UTC (rev 26687)
+++ Zope3/trunk/src/zope/restrictedpython/README.txt	2004-07-22 20:44:21 UTC (rev 26688)
@@ -0,0 +1,69 @@
+==================================
+Support for Restricted Python Code
+==================================
+
+This package, `zope.restrictedpython`, provides a way to compile
+untrusted Python code so that it can be executed safely.
+
+This currently only supports expressions (compile()'s "eval" mode);
+support for "exec" and "single" modes will be added later.
+
+This form of restricted Python assumes that security proxies will be
+used to protect assets.  Given this, the only thing that actually
+needs to be done differently by the generated code is to ensure that
+all attribute lookups go through a safe version of the getattr()
+function that's been provided in the built-in functions used in the
+execution environment.  No other special treatment is needed to
+support safe expression evaluation.  (Additional behaviors are needed
+for the "exec" and "single" modes.)
+
+The implementation makes use of the `RestrictedPython` package,
+originally written for Zope 2.  There is a new AST re-writer in
+`zope.restrictedpython.mutator` which performs the
+tree-transformation, and a top-level `compile()` function in
+`zope.restrictedpython.rcompile`; the later is what client
+applications are expected to use.
+
+The signature of the `compile()` function is very similar to that of
+Python's built-in `compile()` function::
+
+  compile(source, filename, mode)
+
+Using it is equally simple::
+
+  >>> from zope.restrictedpython.rcompile import compile
+
+  >>> code = compile("21 * 2", "<string>", "eval")
+  >>> eval(code)
+  42
+
+What's interesting about the restricted code is that all attribute
+lookups go through the `getattr()` function.  This is generally
+provided as a built-in function in the restricted environment::
+
+  >>> def mygetattr(object, name, default="Yahoo!"):
+  ...     marker = []
+  ...     print "Looking up", name
+  ...     if getattr(object, name, marker) is marker:
+  ...         return default
+  ...     else:
+  ...         return "Yeehaw!"
+
+  >>> import __builtin__
+  >>> builtins = __builtin__.__dict__.copy()
+  >>> builtins["getattr"] = mygetattr
+
+  >>> def reval(source):
+  ...     code = compile(source, "README.txt", "eval")
+  ...     globals = {"__builtins__": builtins}
+  ...     return eval(code, globals, {})
+
+  >>> reval("(42).__class__")
+  Looking up __class__
+  'Yeehaw!'
+  >>> reval("(42).not_really_there")
+  Looking up not_really_there
+  'Yahoo!'
+
+This allows a `getattr()` to be used that ensures the result of
+evaluation is a security proxy.


Property changes on: Zope3/trunk/src/zope/restrictedpython/README.txt
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/restrictedpython/__init__.py
===================================================================
--- Zope3/trunk/src/zope/restrictedpython/__init__.py	2004-07-22 20:35:49 UTC (rev 26687)
+++ Zope3/trunk/src/zope/restrictedpython/__init__.py	2004-07-22 20:44:21 UTC (rev 26688)
@@ -0,0 +1 @@
+# This directory is a Python package.


Property changes on: Zope3/trunk/src/zope/restrictedpython/__init__.py
___________________________________________________________________
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/restrictedpython/mutator.py
===================================================================
--- Zope3/trunk/src/zope/restrictedpython/mutator.py	2004-07-22 20:35:49 UTC (rev 26687)
+++ Zope3/trunk/src/zope/restrictedpython/mutator.py	2004-07-22 20:44:21 UTC (rev 26688)
@@ -0,0 +1,57 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation 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.
+#
+##############################################################################
+"""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 RestrictedPython.SelectCompiler import ast, OP_ASSIGN, OP_DELETE, OP_APPLY
+
+
+# The security checks are performed by a set of six functions that
+# must be provided by the restricted environment.
+
+_getattr_name = ast.Name("getattr")
+
+
+class RestrictionMutator:
+
+    def __init__(self):
+        self.errors = []
+        self.warnings = []
+        self.used_names = {}
+
+    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 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.
+        """
+        node = walker.defaultVisitNode(node)
+        return ast.CallFunc(_getattr_name,
+                            [node.expr, ast.Const(node.attrname)])


Property changes on: Zope3/trunk/src/zope/restrictedpython/mutator.py
___________________________________________________________________
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/restrictedpython/rcompile.py
===================================================================
--- Zope3/trunk/src/zope/restrictedpython/rcompile.py	2004-07-22 20:35:49 UTC (rev 26687)
+++ Zope3/trunk/src/zope/restrictedpython/rcompile.py	2004-07-22 20:44:21 UTC (rev 26688)
@@ -0,0 +1,44 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation 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.
+#
+##############################################################################
+"""compile() equivalent that produces restricted code.
+
+Only 'eval' is supported at this time.
+
+$Id$
+"""
+
+import compiler.pycodegen
+
+import RestrictedPython.RCompile
+
+import zope.restrictedpython.mutator
+
+
+def compile(text, filename, mode):
+    if mode != "eval":
+        raise ValueError("only 'eval' mode is supported")
+    gen = RExpression(text, filename)
+    gen.compile()
+    return gen.getCode()
+
+
+class RExpression(RestrictedPython.RCompile.RestrictedCompileMode):
+
+    mode = "eval"
+    CodeGeneratorClass = compiler.pycodegen.ExpressionCodeGenerator
+
+    def __init__(self, source, filename):
+        RestrictedPython.RCompile.RestrictedCompileMode.__init__(
+            self, source, filename)
+        self.rm = zope.restrictedpython.mutator.RestrictionMutator()


Property changes on: Zope3/trunk/src/zope/restrictedpython/rcompile.py
___________________________________________________________________
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/restrictedpython/tests.py
===================================================================
--- Zope3/trunk/src/zope/restrictedpython/tests.py	2004-07-22 20:35:49 UTC (rev 26687)
+++ Zope3/trunk/src/zope/restrictedpython/tests.py	2004-07-22 20:44:21 UTC (rev 26688)
@@ -0,0 +1,23 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation 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.
+#
+##############################################################################
+"""Tests for zope.restrictedpython.
+
+$Id$
+"""
+import os
+import zope.testing.doctestunit
+
+
+def test_suite():
+    return zope.testing.doctestunit.DocFileSuite("README.txt")


Property changes on: Zope3/trunk/src/zope/restrictedpython/tests.py
___________________________________________________________________
Name: svn:mime-type
   + text/x-python
Name: svn:eol-style
   + native



More information about the Zope3-Checkins mailing list