[Checkins] SVN: five.pt/trunk/ Added access-control security to Python expression. Thanks to Leonardo Rochael Almeida for contributing to this feature.

Malthe Borch mborch at gmail.com
Fri Feb 18 09:47:59 EST 2011


Log message for revision 120422:
  Added access-control security to Python expression. Thanks to Leonardo Rochael Almeida for contributing to this feature.

Changed:
  U   five.pt/trunk/CHANGES.txt
  U   five.pt/trunk/setup.py
  U   five.pt/trunk/src/five/pt/configure.zcml
  U   five.pt/trunk/src/five/pt/expressions.py
  U   five.pt/trunk/src/five/pt/tests/locals.pt
  A   five.pt/trunk/src/five/pt/tests/secure.pt
  U   five.pt/trunk/src/five/pt/tests/test_viewpagetemplatefile.py
  U   five.pt/trunk/versions.cfg

-=-
Modified: five.pt/trunk/CHANGES.txt
===================================================================
--- five.pt/trunk/CHANGES.txt	2011-02-18 14:01:54 UTC (rev 120421)
+++ five.pt/trunk/CHANGES.txt	2011-02-18 14:47:59 UTC (rev 120422)
@@ -1,6 +1,11 @@
 Changelog
 =========
 
+In next release...
+
+- Python-expressions are now subject to access-control security.
+  [malthe]
+
 1.3.3 - 2010-09-30
 ~~~~~~~~~~~~~~~~~~
 

Modified: five.pt/trunk/setup.py
===================================================================
--- five.pt/trunk/setup.py	2011-02-18 14:01:54 UTC (rev 120421)
+++ five.pt/trunk/setup.py	2011-02-18 14:47:59 UTC (rev 120422)
@@ -27,6 +27,7 @@
           'setuptools',
           'z3c.pt>=1.2.1',
           'Chameleon>=1.2.11',
+          'sourcecodegen',
       ],
       entry_points="""
       [z3c.autoinclude.plugin]

Modified: five.pt/trunk/src/five/pt/configure.zcml
===================================================================
--- five.pt/trunk/src/five/pt/configure.zcml	2011-02-18 14:01:54 UTC (rev 120421)
+++ five.pt/trunk/src/five/pt/configure.zcml	2011-02-18 14:47:59 UTC (rev 120422)
@@ -10,6 +10,10 @@
       component=".expressions.path_translator" />
 
   <utility
+      name="python"
+      component=".expressions.python_translator" />
+
+  <utility
       name="exists"
       component=".expressions.exists_translator" />
 

Modified: five.pt/trunk/src/five/pt/expressions.py
===================================================================
--- five.pt/trunk/src/five/pt/expressions.py	2011-02-18 14:01:54 UTC (rev 120421)
+++ five.pt/trunk/src/five/pt/expressions.py	2011-02-18 14:47:59 UTC (rev 120422)
@@ -1,3 +1,5 @@
+from compiler import parse
+
 from z3c.pt.expressions import PathTranslator
 from z3c.pt.expressions import ExistsTranslator
 from z3c.pt.expressions import ZopeExistsTraverser
@@ -2,2 +4,5 @@
 from z3c.pt.expressions import ProviderTranslator
+from chameleon.zpt.expressions import PythonTranslator
+from chameleon.core import types
+from sourcecodegen import generate_code
 
@@ -7,7 +12,16 @@
 from OFS.interfaces import ITraversable
 from Products.PageTemplates import ZRPythonExpr
 from zExceptions import NotFound, Unauthorized
+from RestrictedPython.RestrictionMutator import RestrictionMutator
+from RestrictedPython import MutatingWalker
+from RestrictedPython.Guards import safe_builtins
 
+from AccessControl.ZopeGuards import guarded_getattr
+from AccessControl.ZopeGuards import guarded_getitem
+from AccessControl.ZopeGuards import guarded_apply
+from AccessControl.ZopeGuards import guarded_iter
+from AccessControl.ZopeGuards import protected_inplacevar
+
 from zope import component
 from zope.proxy import removeAllProxies
 from zope.traversing.adapters import traversePathElement
@@ -121,6 +135,50 @@
 class FiveProviderTranslator(ProviderTranslator):
     content_provider_traverser = FiveContentProviderTraverser()
 
+
+class FivePythonTranslator(PythonTranslator):
+    rm = RestrictionMutator()
+
+    secured = {
+        '_getattr_': guarded_getattr,
+        '_getitem_': guarded_getitem,
+        '_apply_': guarded_apply,
+        '_getiter_': guarded_iter,
+        '_inplacevar_': protected_inplacevar,
+    }
+
+    def translate(self, string, escape=None):
+        """We use the ``parser`` module to determine if
+        an expression is a valid python expression.
+
+        Make sure the formatted syntax error exception contains the
+        expression string.
+
+        >>> from traceback import format_exc
+        >>> translate = PythonTranslator().translate
+        >>> try: translate('abc:def:ghi')
+        ... except SyntaxError, e: 'abc:def:ghi' in format_exc(e)
+        True
+        """
+
+        if isinstance(string, unicode):
+            string = string.encode('utf-8')
+
+        if string:
+            expression = string.strip()
+            node = parse(expression, 'eval').node
+            MutatingWalker.walk(node, self.rm)
+            string = generate_code(node)
+
+            if isinstance(string, str):
+                string = string.decode('utf-8')
+
+            value = types.value(string.strip())
+            value.symbol_mapping.update(self.secured)
+
+            return value
+
+python_translator = FivePythonTranslator()
 path_translator = PathTranslator()
 exists_translator = ExistsTranslator()
 provider_translator = FiveProviderTranslator()

Modified: five.pt/trunk/src/five/pt/tests/locals.pt
===================================================================
--- five.pt/trunk/src/five/pt/tests/locals.pt	2011-02-18 14:01:54 UTC (rev 120421)
+++ five.pt/trunk/src/five/pt/tests/locals.pt	2011-02-18 14:47:59 UTC (rev 120422)
@@ -5,7 +5,6 @@
     <div tal:replace="python:'here==container:'+str(here==container)" />
     <div tal:replace="string:root:${root/getPhysicalPath}" />
     <div tal:replace="string:nothing:${nothing}" />
-    <div tal:define="cgi python:modules['cgi']">
-        modules:<span tal:replace="python:cgi.escape(view.tagsoup())" />
-    </div>
+    <div tal:define="cgi python:modules['cgi']"
+         tal:replace="python: dir(cgi)" />
 </div>

Added: five.pt/trunk/src/five/pt/tests/secure.pt
===================================================================
--- five.pt/trunk/src/five/pt/tests/secure.pt	                        (rev 0)
+++ five.pt/trunk/src/five/pt/tests/secure.pt	2011-02-18 14:47:59 UTC (rev 120422)
@@ -0,0 +1,4 @@
+<div xmlns="http://www.w3.org/1999/xhtml"
+     xmlns:tal="http://xml.zope.org/namespaces/tal">
+  <span tal:replace="python: modules['cgi'].escape(view.tagsoup())" />
+</div>

Modified: five.pt/trunk/src/five/pt/tests/test_viewpagetemplatefile.py
===================================================================
--- five.pt/trunk/src/five/pt/tests/test_viewpagetemplatefile.py	2011-02-18 14:01:54 UTC (rev 120421)
+++ five.pt/trunk/src/five/pt/tests/test_viewpagetemplatefile.py	2011-02-18 14:47:59 UTC (rev 120422)
@@ -15,9 +15,6 @@
     def available(self):
         return 'yes'
 
-    def tagsoup(self):
-        return '<foo></bar>'
-
     index = ViewPageTemplateFile('locals.pt')
 
 
@@ -30,6 +27,15 @@
     index = ViewPageTemplateFile('options.pt')
 
 
+class SecureView(BrowserView):
+    index = ViewPageTemplateFile('secure.pt')
+
+    __allow_access_to_unprotected_subobjects__ = True
+
+    def tagsoup(self):
+        return '<foo></bar>'
+
+
 class MissingView(BrowserView):
     index = ViewPageTemplateFile('missing.pt')
 
@@ -49,6 +55,21 @@
         result = view.index()
         self.failUnless('Hello world!' in result)
 
+    def test_secure(self):
+        view = SecureView(self.folder, self.folder.REQUEST)
+        from zExceptions import Unauthorized
+        try:
+            result = view.index()
+        except Unauthorized:
+            pass
+        else:
+            self.fail("Expected unauthorized.")
+
+        from AccessControl.SecurityInfo import allow_module
+        allow_module("cgi")
+        result = view.index()
+        self.failUnless('&lt;foo&gt;&lt;/bar&gt;' in result)
+
     def test_locals(self):
         view = LocalsView(self.folder, self.folder.REQUEST)
         result = view.index()
@@ -59,7 +80,7 @@
         self.failUnless('here==container:True' in result)
         self.failUnless("root:(\'\',)" in result)
         self.failUnless("nothing:None" in result)
-        self.failUnless("modules:&lt;foo&gt;" in result)
+        self.failUnless("rfc822" in result)
 
     def test_locals_base(self):
         view = LocalsBaseView(self.folder, self.folder.REQUEST)

Modified: five.pt/trunk/versions.cfg
===================================================================
--- five.pt/trunk/versions.cfg	2011-02-18 14:01:54 UTC (rev 120421)
+++ five.pt/trunk/versions.cfg	2011-02-18 14:47:59 UTC (rev 120422)
@@ -1,7 +1,7 @@
 [versions]
 chameleon.core = 1.0.4
 chameleon.zpt = 1.1.3
-Chameleon = 1.2.3
+Chameleon = 1.3.0-rc1
 z3c.pt = 1.2.1
 zope.testing = 3.7.1
 zope.i18n = 3.6.0



More information about the checkins mailing list