[Zope3-checkins] SVN: Zope3/trunk/src/zope/ Added untrusted dtml support

Jim Fulton jim at zope.com
Thu Jul 29 00:57:37 EDT 2004


Log message for revision 26826:
  Added untrusted dtml support
  
  - Added restricted compilation of python functions
  
  - Added more careful getting of instance attrs
  
  - Added additional hooks in the trusted code to
    make swithing in untrusted versions possible.
  
  - Centralized the untrusted support so that it's easier to use it, for
    example in both dtmlpage and sqlscript.
  
  


Changed:
  U   Zope3/trunk/src/zope/app/dtmlpage/dtmlpage.py
  U   Zope3/trunk/src/zope/app/sqlscript/dtml.py
  U   Zope3/trunk/src/zope/documenttemplate/dt_in.py
  U   Zope3/trunk/src/zope/documenttemplate/dt_string.py
  U   Zope3/trunk/src/zope/documenttemplate/dt_try.py
  U   Zope3/trunk/src/zope/documenttemplate/dt_util.py
  U   Zope3/trunk/src/zope/documenttemplate/dt_with.py
  U   Zope3/trunk/src/zope/documenttemplate/pdocumenttemplate.py
  A   Zope3/trunk/src/zope/documenttemplate/untrusted/
  A   Zope3/trunk/src/zope/documenttemplate/untrusted/README.txt
  A   Zope3/trunk/src/zope/documenttemplate/untrusted/__init__.py
  A   Zope3/trunk/src/zope/documenttemplate/untrusted/tests.py
  A   Zope3/trunk/src/zope/documenttemplate/untrusted/untrusted.py


-=-
Modified: Zope3/trunk/src/zope/app/dtmlpage/dtmlpage.py
===================================================================
--- Zope3/trunk/src/zope/app/dtmlpage/dtmlpage.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/app/dtmlpage/dtmlpage.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -20,7 +20,7 @@
 from persistent import Persistent
 
 from zope.security.proxy import ProxyFactory
-from zope.documenttemplate.dt_html import HTML
+from zope.documenttemplate.untrusted import UntrustedHTML
 from zope.interface import implements
 
 from zope.app.annotation.interfaces import IAnnotatable
@@ -42,22 +42,14 @@
 
     def setSource(self, text, content_type='text/html'):
         '''See interface `IDTMLPage`'''
-        self.template = HTML(text)
+        self.template = UntrustedHTML(text)
         self.content_type = content_type
 
     def render(self, request, *args, **kw):
         """See interface `IDTMLRenderPage`"""
+        return self.template(self.__parent__, request, REQUEST=request, **kw)
 
-        instance = ProxyFactory(self.__parent__)
-        request = ProxyFactory(request)
 
-        for k in kw:
-            kw[k] = ProxyFactory(kw[k])
-        kw['REQUEST'] = request
-
-        return self.template(instance, request, **kw)
-
-
     __call__ = render
 
     source = property(getSource, setSource, None,

Modified: Zope3/trunk/src/zope/app/sqlscript/dtml.py
===================================================================
--- Zope3/trunk/src/zope/app/sqlscript/dtml.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/app/sqlscript/dtml.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -20,6 +20,7 @@
 
 from zope.documenttemplate.dt_html import HTML
 from zope.documenttemplate.dt_util import ParseError, parse_params, name_param
+from zope.documenttemplate.untrusted import UntrustedHTML
 
 from interfaces import MissingInput
 
@@ -77,7 +78,10 @@
                 return ''
             raise KeyError, key, sys.exc_info()[2]
 
-        if isinstance(v, (list, tuple)):
+        if (list in v.__class__.__mro__  # isinstance doesn't work w 
+            or                           # security proxies, so we use
+            tuple in v.__class__.__mro__ # this __mro__ trick.
+            ):
             if len(v) > 1 and not self.multiple:
                 raise 'Multiple Values', (
                     'multiple values are not allowed for <em>%s</em>'
@@ -272,7 +276,7 @@
     __call__ = render
 
 
-class SQLDTML(HTML):
+class SQLDTML(UntrustedHTML):
     __name__ = 'SQLDTML'
 
     commands = HTML.commands.copy()

Modified: Zope3/trunk/src/zope/documenttemplate/dt_in.py
===================================================================
--- Zope3/trunk/src/zope/documenttemplate/dt_in.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/documenttemplate/dt_in.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -315,10 +315,8 @@
 $Id$
 """
 
-from zope.documenttemplate.dt_util import \
-     ParseError, parse_params, name_param
-from zope.documenttemplate.dt_util import \
-     render_blocks, InstanceDict, ValidationError, Eval
+from zope.documenttemplate.dt_util import ParseError, parse_params, name_param
+from zope.documenttemplate.dt_util import render_blocks, ValidationError, Eval
 
 import re
 from zope.documenttemplate.dt_insv import sequence_variables, opt
@@ -583,7 +581,7 @@
                     if mapping:
                         push(client)
                     else:
-                        push(InstanceDict(client, md))
+                        md._push_instance(client)
 
                     try:
                         append(render(section, md))
@@ -680,7 +678,7 @@
                 if mapping:
                     push(client)
                 else:
-                    push(InstanceDict(client, md))
+                    md._push_instance(client)
 
                 try:
                     append(render(section, md))

Modified: Zope3/trunk/src/zope/documenttemplate/dt_string.py
===================================================================
--- Zope3/trunk/src/zope/documenttemplate/dt_string.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/documenttemplate/dt_string.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -17,8 +17,7 @@
 """
 import re, thread
 
-from zope.documenttemplate.dt_util import \
-     ParseError, InstanceDict, TemplateDict, render_blocks
+from zope.documenttemplate.dt_util import ParseError, render_blocks
 from zope.documenttemplate.dt_var import Var, Call, Comment
 from zope.documenttemplate.dt_return import ReturnTag, DTReturn
 
@@ -44,6 +43,8 @@
 
     """
 
+    from zope.documenttemplate.dt_util import TemplateDict
+
     # Document Templates masquerade as functions:
     class func_code:
         pass
@@ -422,7 +423,7 @@
 
         pushed=None
         try:
-            if mapping.__class__ is TemplateDict:
+            if isinstance(mapping, self.TemplateDict):
                 pushed=0
         except:
             pass
@@ -436,7 +437,7 @@
                 push(self.globals)
                 pushed = pushed+1
         else:
-            md = TemplateDict()
+            md = self.TemplateDict()
             push = md._push
             shared_globals = self.shared_globals
             if shared_globals:
@@ -463,11 +464,11 @@
                 # if client is a tuple, it represents a "path" of clients
                 # which should be pushed onto the md in order.
                 for ob in client:
-                    push(InstanceDict(ob, md)) # Circ. Ref. 8-|
+                    md._push_instance(ob)
                     pushed += 1
             else:
                 # otherwise its just a normal client object.
-                push(InstanceDict(client, md)) # Circ. Ref. 8-|
+                md._push_instance(client)
                 pushed += 1
 
         if self._vars:

Modified: Zope3/trunk/src/zope/documenttemplate/dt_try.py
===================================================================
--- Zope3/trunk/src/zope/documenttemplate/dt_try.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/documenttemplate/dt_try.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -18,9 +18,8 @@
 
 import sys, traceback
 from StringIO import StringIO
-from zope.documenttemplate.dt_util \
-     import ParseError, parse_params, render_blocks
-from zope.documenttemplate.dt_util import InstanceDict
+from zope.documenttemplate.dt_util import ParseError, parse_params
+from zope.documenttemplate.dt_util import render_blocks
 from zope.documenttemplate.dt_return import DTReturn
 
 from types import StringType
@@ -181,7 +180,7 @@
                 error_tb = f.getvalue()
                 ns = md.namespace(error_type=errname, error_value=v,
                     error_tb=error_tb)[0]
-                md._push(InstanceDict(ns,md))
+                md._push_instance(ns)
                 return render_blocks(handler, md)
             finally:
                 md._pop(1)

Modified: Zope3/trunk/src/zope/documenttemplate/dt_util.py
===================================================================
--- Zope3/trunk/src/zope/documenttemplate/dt_util.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/documenttemplate/dt_util.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -19,8 +19,10 @@
 
 from types import ListType, StringType, TupleType
 
-from zope.documenttemplate.pdocumenttemplate import \
-     InstanceDict, TemplateDict, render_blocks
+# These imports are for the use of clients of this module, as this
+# module is the canonical place to get them. 
+from zope.documenttemplate.pdocumenttemplate import TemplateDict, InstanceDict
+from zope.documenttemplate.pdocumenttemplate import render_blocks
 
 
 class ParseError(Exception):
@@ -65,8 +67,8 @@
 
 
     def eval(self, mapping):
-        d={'_vars': mapping,
-           '_': mapping}
+        d={'_vars': mapping._proxied(),
+           '_': mapping._proxied()}
         code = self.code
         for name in code.co_names:
             if not d.has_key(name):
@@ -79,7 +81,9 @@
                     # does need the name, a NameError will occur.
                     pass
 
-        return eval(code, {'__builtins__': None}, d)
+        return eval(code,
+                    {'__builtins__': getattr(mapping, '__builtins__', None)},
+                    d)
 
 
     def __call__(self, **kw):

Modified: Zope3/trunk/src/zope/documenttemplate/dt_with.py
===================================================================
--- Zope3/trunk/src/zope/documenttemplate/dt_with.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/documenttemplate/dt_with.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -35,9 +35,8 @@
 $Id$
 """
 
-from zope.documenttemplate.dt_util import \
-     parse_params, name_param, InstanceDict, render_blocks
-from zope.documenttemplate.dt_util import TemplateDict
+from zope.documenttemplate.dt_util import parse_params, name_param
+from zope.documenttemplate.dt_util import render_blocks
 
 from types import StringTypes, TupleType
 
@@ -70,18 +69,19 @@
         else:
             v = expr(md)
 
-        if not self.mapping:
-            if isinstance(v, TupleType) and len(v) == 1:
-                v = v[0]
-            v = InstanceDict(v, md)
-
         if self.only:
             _md = md
-            md = TemplateDict()
+            md = md.__class__()
             if hasattr(_md, 'validate'):
                 md.validate = _md.validate
 
-        md._push(v)
+        if self.mapping:
+            md._push(v)
+        else:
+            if isinstance(v, TupleType) and len(v) == 1:
+                v = v[0]
+            md._push_instance(v)
+
         try:
             return render_blocks(self.section, md)
         finally:

Modified: Zope3/trunk/src/zope/documenttemplate/pdocumenttemplate.py
===================================================================
--- Zope3/trunk/src/zope/documenttemplate/pdocumenttemplate.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/documenttemplate/pdocumenttemplate.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -54,12 +54,6 @@
 
         inst = self.self
 
-        if key[:1] == '_':
-            if key != '__str__':
-                raise KeyError, key # Don't divuldge private data
-            else:
-                return str(inst)
-
         try:
             r = getattr(inst, key)
         except AttributeError:
@@ -122,6 +116,12 @@
     def _push(self, d):
         return self.dicts.push(d)
 
+    def _push_instance(self, inst):
+        self._push(InstanceDict(inst, self))
+
+    def _proxied(self):
+        return self
+
     def __init__(self):
         m = self.dicts = MultiMapping()
         self._pop = m.pop

Added: Zope3/trunk/src/zope/documenttemplate/untrusted/README.txt
===================================================================
--- Zope3/trunk/src/zope/documenttemplate/untrusted/README.txt	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/documenttemplate/untrusted/README.txt	2004-07-29 04:57:37 UTC (rev 26826)
@@ -0,0 +1,84 @@
+Untrusted Document Templates
+============================
+
+Untrusted document templates implement an untrusted interpreter for
+the DTML language. Untrusted templates protect any data they're given.
+
+  >>> from zope.documenttemplate.untrusted import UntrustedHTML
+
+Consider a sample class, which allows access to attributes f1, f2, and name:
+
+  >>> from zope.security.checker import NamesChecker
+  >>> class C(object):
+  ...     def __init__(self, name, **kw):
+  ...         self.name = name
+  ...         self.__dict__.update(kw)
+  ...     def f1(self):
+  ...         return 'f1 called'
+  ...     def f2(self):
+  ...         return 'f2 called'
+  ...     __Security_checker__ = NamesChecker(['f1', 'f2', 'name'])
+
+We can get at alowed data just fine:
+
+  >>> UntrustedHTML('<dtml-var f1> <dtml-var name>')(C('bob'))
+  'f1 called bob'
+
+But we'll get an error if we try to access an attribute we're not
+alowed to get:
+
+  >>> UntrustedHTML('<dtml-var x>')(C('bob', x=1))
+  Traceback (most recent call last):
+  ...
+  KeyError: 'x'
+
+If we create data inside the template, we'll be allowed to manipulate
+it:
+
+  >>> UntrustedHTML('''
+  ... <dtml-let data="[]">
+  ...    <dtml-call expr="data.append(1)"><dtml-var data>
+  ... </dtml-let>
+  ... ''')()
+  '\n   [1]\n'
+
+but any attributes we get from data we create are proxied, and
+this protected:
+
+  >>> UntrustedHTML('''
+  ... <dtml-let data="[]">
+  ...    <dtml-with data><dtml-with __class__><dtml-var __dict__>
+  ...    </dtml-with></dtml-with>
+  ... </dtml-let>
+  ... ''')()
+  Traceback (most recent call last):
+  ...
+  KeyError: '__dict__'
+
+  >>> UntrustedHTML('''
+  ... <dtml-let data="[]">
+  ...    <dtml-var expr="data.__class__.__dict__">
+  ... </dtml-let>
+  ... ''')()
+  Traceback (most recent call last):
+  ...
+  ForbiddenAttribute: ('__dict__', <type 'list'>)
+
+  >>> UntrustedHTML('''<dtml-var expr="'foo'.__class__.__dict__">''')()
+  Traceback (most recent call last):
+  ...
+  ForbiddenAttribute: ('__dict__', <type 'str'>)
+
+Access is provided to a number of utility functions provided by the
+template dict, but not to hidden functions:
+
+  >>> UntrustedHTML('''<dtml-var expr="_.abs(-1)">''')()
+  '1'
+
+But not to privare attributes:
+
+  >>> UntrustedHTML('''<dtml-var expr="_._pop()">''')()
+  Traceback (most recent call last):
+  ...
+  ForbiddenAttribute: ('_pop', <an UntrustedTemplateDict>)
+  


Property changes on: Zope3/trunk/src/zope/documenttemplate/untrusted/README.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/documenttemplate/untrusted/__init__.py
===================================================================
--- Zope3/trunk/src/zope/documenttemplate/untrusted/__init__.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/documenttemplate/untrusted/__init__.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -0,0 +1 @@
+from zope.documenttemplate.untrusted.untrusted import UntrustedHTML


Property changes on: Zope3/trunk/src/zope/documenttemplate/untrusted/__init__.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/documenttemplate/untrusted/tests.py
===================================================================
--- Zope3/trunk/src/zope/documenttemplate/untrusted/tests.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/documenttemplate/untrusted/tests.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -0,0 +1,28 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# 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.
+#
+##############################################################################
+"""Untrusted python tests
+
+$Id$
+"""
+import unittest
+from zope.testing import doctestunit
+
+def test_suite():
+    return unittest.TestSuite((
+        doctestunit.DocFileSuite('README.txt',),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+


Property changes on: Zope3/trunk/src/zope/documenttemplate/untrusted/tests.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: Zope3/trunk/src/zope/documenttemplate/untrusted/untrusted.py
===================================================================
--- Zope3/trunk/src/zope/documenttemplate/untrusted/untrusted.py	2004-07-29 04:53:03 UTC (rev 26825)
+++ Zope3/trunk/src/zope/documenttemplate/untrusted/untrusted.py	2004-07-29 04:57:37 UTC (rev 26826)
@@ -0,0 +1,67 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Untrusted document template support
+
+$Id$
+"""
+
+from zope.security.checker import ProxyFactory
+from zope.documenttemplate.dt_html import HTML
+from zope.documenttemplate.dt_util import InstanceDict, TemplateDict
+from zope.security.untrustedpython.rcompile import compile
+from zope.security.untrustedpython.builtins import SafeBuiltins
+from zope.security.checker import NamesChecker
+
+class UntrustedInstanceDict(InstanceDict):
+
+    def __getitem__(self, key):
+        return ProxyFactory(InstanceDict.__getitem__(self, key))
+
+class UntrustedTemplateDict(TemplateDict):
+
+    __builtins__ = SafeBuiltins
+
+    __Security_checker__ = NamesChecker([
+        'math', 'random', 'range', 'pow', 'test', 'getattr', 'attr', 'hasattr',
+        'render', 'namespace', 'reorder',
+        'None', 'abs', 'chr', 'divmod', 'float', 'hash', 'hex', 'int',
+        'len', 'max', 'min', 'oct', 'ord', 'round', 'str',
+        ])
+
+    def _push_instance(self, inst):
+        self._push(UntrustedInstanceDict(inst, self))
+
+    def _proxied(self):
+        return ProxyFactory(self)
+
+    def __repr__(self):
+        return '<an UntrustedTemplateDict>'
+    
+
+class UntrustedHTML(HTML):
+    __name__ = 'UntrustedHTML'
+
+    TemplateDict = UntrustedTemplateDict
+
+    def compile_python_expresssion(self, src):
+        return compile(src, getattr(self, '__name__', '<string>'), 'eval')
+    
+    def __call__(self, client=None, mapping={}, **kw):
+        if kw:
+            kw = dict([(k, ProxyFactory(v)) for (k, v) in kw.items()])
+        
+        return HTML.__call__(self,
+                             ProxyFactory(client),
+                             ProxyFactory(mapping),
+                             **kw)


Property changes on: Zope3/trunk/src/zope/documenttemplate/untrusted/untrusted.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Zope3-Checkins mailing list