[Zope3-checkins] SVN: Zope3/branches/ZopeX3-3.0/ Merged from trunk:
Support for untrusted page templates.
Fred L. Drake, Jr.
fred at zope.com
Fri Jul 23 15:03:57 EDT 2004
Log message for revision 26720:
Merged from trunk: Support for untrusted page templates.
This merge contains a large number of patches from the Zope 3 trunk.
These changes collectively cause page templates loaded from the file
system to behave as trusted code (similar to what all page templates
did previously, except for a few things), and page templates loaded
from the database to be treated as untrusted code (so security
declarations are honored during traversal and Python code execution).
The following revisions are included in this merge:
26175 - make python: expressions that don't compile raise right exception
26637 - re-factor the base module importer for path:modules/
26639 - explain the untrusted path:modules/
26640 - clean up some boilerplate
26642 - separate the execution engines for zope.app.pagetemplate
26657 - add tests for trusted and untrusted path:modules/... expressions
26679 - separate python expression compilation to allow override
26688 - implemented restricted Python interpreter for TALES expressions
26689 - add dependency on zope.restrictedpython
26704 - implement untrusted traversal for page templates
26719 - add note about page template security changes
Changed:
U Zope3/branches/ZopeX3-3.0/doc/CHANGES.txt
A Zope3/branches/ZopeX3-3.0/src/RestrictedPython/
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/Eval.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/Guards.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/Limits.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/MutatingWalker.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/PrintCollector.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/RCompile.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/RestrictionMutator.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/SafeMapping.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/SelectCompiler.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/Utilities.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/__init__.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/tests/__init__.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/tests/before_and_after.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/tests/class.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/tests/lambda.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/tests/restricted_module.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/tests/security_in_syntax.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/tests/testRestrictions.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/tests/unpack.py
_U Zope3/branches/ZopeX3-3.0/src/RestrictedPython/tests/verify.py
U Zope3/branches/ZopeX3-3.0/src/zope/app/DEPENDENCIES.cfg
U Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/engine.py
U Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/metaconfigure.py
U Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/tests/test_engine.py
A Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/tests/trusted.py
U Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/viewpagetemplatefile.py
U Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/pagetemplateresource.py
U Zope3/branches/ZopeX3-3.0/src/zope/app/traversing/ftests/test_vhosting.py
U Zope3/branches/ZopeX3-3.0/src/zope/app/zptpage/tests/test_zptpage.py
A Zope3/branches/ZopeX3-3.0/src/zope/restrictedpython/
_U Zope3/branches/ZopeX3-3.0/src/zope/restrictedpython/README.txt
_U Zope3/branches/ZopeX3-3.0/src/zope/restrictedpython/__init__.py
_U Zope3/branches/ZopeX3-3.0/src/zope/restrictedpython/mutator.py
_U Zope3/branches/ZopeX3-3.0/src/zope/restrictedpython/rcompile.py
_U Zope3/branches/ZopeX3-3.0/src/zope/restrictedpython/tests.py
U Zope3/branches/ZopeX3-3.0/src/zope/tales/expressions.py
U Zope3/branches/ZopeX3-3.0/src/zope/tales/pythonexpr.py
U Zope3/branches/ZopeX3-3.0/src/zope/tales/tests/test_expressions.py
-=-
Modified: Zope3/branches/ZopeX3-3.0/doc/CHANGES.txt
===================================================================
--- Zope3/branches/ZopeX3-3.0/doc/CHANGES.txt 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/doc/CHANGES.txt 2004-07-23 19:03:56 UTC (rev 26720)
@@ -10,7 +10,10 @@
New features
- New Features
+ - Separated the trusted and untrusted behaviors of page
+ templates in the application server. File-system-based
+ templates are trusted, and database-based templates are
+ untrusted.
- Improved using documentation files as tests. (Needed to test
new security policy):
Copied: Zope3/branches/ZopeX3-3.0/src/RestrictedPython (from rev 26688, Zope3/trunk/src/RestrictedPython)
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/DEPENDENCIES.cfg
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/DEPENDENCIES.cfg 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/DEPENDENCIES.cfg 2004-07-23 19:03:56 UTC (rev 26720)
@@ -17,6 +17,7 @@
zope.pagetemplate
zope.proxy
zope.publisher
+zope.restrictedpython
zope.schema
zope.security
zope.server
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/engine.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/engine.py 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/engine.py 2004-07-23 19:03:56 UTC (rev 26720)
@@ -18,39 +18,81 @@
$Id$
"""
import sys
-from types import StringTypes
+from zope.interface import implements
+
from zope.tales.expressions import PathExpr, StringExpr, NotExpr, DeferExpr
+from zope.tales.expressions import SimpleModuleImporter
from zope.tales.pythonexpr import PythonExpr
from zope.tales.tales import ExpressionEngine, Context
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
from zope.app import zapi
from zope.app.i18n import ZopeMessageIDFactory as _
-from zope.app.traversing.adapters import Traverser
-from zope.app.traversing.interfaces import IPathAdapter
+from zope.app.traversing.adapters import Traverser, traversePathElement
+from zope.app.traversing.interfaces import IPathAdapter, ITraversable
class InlineCodeError(Exception):
pass
+
def zopeTraverser(object, path_items, econtext):
"""Traverses a sequence of names, first trying attributes then items.
"""
- traverser = Traverser(object)
- return traverser.traverse(path_items,
- request=getattr(econtext, 'request', None))
+ request = getattr(econtext, 'request', None)
+ path_items = list(path_items)
+ path_items.reverse()
+ while path_items:
+ name = path_items.pop()
+ object = traversePathElement(object, name, path_items,
+ request=request)
+ object = ProxyFactory(object)
+ return object
class ZopePathExpr(PathExpr):
def __init__(self, name, expr, engine):
super(ZopePathExpr, self).__init__(name, expr, engine, zopeTraverser)
+
+def trustedZopeTraverser(object, path_items, econtext):
+ """Traverses a sequence of names, first trying attributes then items.
+ """
+ traverser = Traverser(object)
+ return traverser.traverse(path_items,
+ request=getattr(econtext, 'request', None))
+
+class TrustedZopePathExpr(PathExpr):
+
+ def __init__(self, name, expr, engine):
+ super(TrustedZopePathExpr, self).__init__(name, expr, engine,
+ trustedZopeTraverser)
+
+
+# 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):
@@ -58,17 +100,18 @@
vars = self._bind_used_names(econtext, RestrictedBuiltins)
return eval(self._code, vars)
-class ZopeContext(Context):
+ def _compile(self, text, filename):
+ return rcompile.compile(text, filename, 'eval')
- def setContext(self, name, value):
- # Hook to allow subclasses to do things like adding security proxies
- Context.setContext(self, name, ProxyFactory(value))
+class ZopeContextBase(Context):
+ """Base class for both trusted and untrusted evaluation contexts."""
+
def evaluateText(self, expr):
text = self.evaluate(expr)
if text is self.getDefault() or text is None:
return text
- if isinstance(text, StringTypes):
+ if isinstance(text, basestring):
# text could be a proxied/wrapped object
return text
return unicode(text)
@@ -102,7 +145,7 @@
error = _('No interpreter named "${lang_name}" was found.')
error.mapping = {'lang_name': lang}
raise InlineCodeError, error
-
+
globals = self.vars.copy()
result = interpreter.evaluateRawCode(code, globals)
# Add possibly new global variables.
@@ -113,6 +156,18 @@
return result
+class ZopeContext(ZopeContextBase):
+ """Evaluation context for untrusted programs."""
+
+ def setContext(self, name, value):
+ # Hook to allow subclasses to do things like adding security proxies
+ Context.setContext(self, name, ProxyFactory(value))
+
+
+class TrustedZopeContext(ZopeContextBase):
+ """Evaluation context for trusted programs."""
+
+
class AdapterNamespaces(object):
"""Simulate tales function namespaces with adapter lookup.
@@ -143,7 +198,7 @@
Cleanup:
-
+
>>> tearDown()
"""
@@ -158,12 +213,104 @@
return zapi.getAdapter(object, IPathAdapter, name)
except ComponentLookupError:
raise KeyError, name
-
+
self.namespaces[name] = namespace
return namespace
+
class ZopeEngine(ExpressionEngine):
+ """Untrusted expression engine.
+ This engine does not allow modules to be imported; only modules
+ already available may be accessed::
+
+ >>> modname = 'zope.app.pagetemplate.tests.trusted'
+ >>> engine = _Engine()
+ >>> context = engine.getContext(engine.getBaseNames())
+
+ >>> modname in sys.modules
+ False
+ >>> context.evaluate('modules/' + modname)
+ Traceback (most recent call last):
+ ...
+ KeyError: 'zope.app.pagetemplate.tests.trusted'
+
+ (The use of KeyError is an unfortunate implementation detail; I
+ think this should be a NotFoundError.)
+
+ Modules which have already been imported by trusted code are
+ available, wrapped in security proxies::
+
+ >>> m = context.evaluate('modules/sys')
+ >>> m.__name__
+ 'sys'
+ >>> m._getframe
+ Traceback (most recent call last):
+ ...
+ 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'>
+
+ General path expressions provide objects that are wrapped in
+ security proxies as well::
+
+ >>> from zope.app.container.sample import SampleContainer
+ >>> from zope.app.tests.placelesssetup import setUp, tearDown
+ >>> from zope.security.checker import NamesChecker, defineChecker
+
+ >>> class Container(SampleContainer):
+ ... implements(ITraversable)
+ ... def traverse(self, name, further_path):
+ ... return self[name]
+
+ >>> setUp()
+ >>> defineChecker(Container, NamesChecker(['traverse']))
+ >>> d = engine.getBaseNames()
+ >>> foo = Container()
+ >>> foo.__name__ = 'foo'
+ >>> d['foo'] = ProxyFactory(foo)
+ >>> foo['bar'] = bar = Container()
+ >>> bar.__name__ = 'bar'
+ >>> bar.__parent__ = foo
+ >>> bar['baz'] = baz = Container()
+ >>> baz.__name__ = 'baz'
+ >>> baz.__parent__ = bar
+ >>> context = engine.getContext(d)
+
+ >>> o1 = context.evaluate('foo/bar')
+ >>> o1.__name__
+ 'bar'
+ >>> type(o1)
+ <type 'zope.security._proxy._Proxy'>
+
+ >>> o2 = context.evaluate('foo/bar/baz')
+ >>> o2.__name__
+ 'baz'
+ >>> type(o2)
+ <type 'zope.security._proxy._Proxy'>
+ >>> o3 = o2.__parent__
+ >>> type(o3)
+ <type 'zope.security._proxy._Proxy'>
+ >>> o1 == o3
+ True
+
+ >>> o1 is o2
+ False
+
+ >>> tearDown()
+
+ """
+
+ _create_context = ZopeContext
+
def __init__(self):
ExpressionEngine.__init__(self)
self.namespaces = AdapterNamespaces()
@@ -175,7 +322,7 @@
else:
namespace = __namespace
- context = ZopeContext(self, namespace)
+ context = self._create_context(self, namespace)
# Put request into context so path traversal can find it
if 'request' in namespace:
@@ -187,22 +334,93 @@
return context
+
+class TrustedZopeEngine(ZopeEngine):
+ """Trusted expression engine.
+
+ This engine allows modules to be imported::
+
+ >>> modname = 'zope.app.pagetemplate.tests.trusted'
+ >>> engine = _TrustedEngine()
+ >>> context = engine.getContext(engine.getBaseNames())
+
+ >>> modname in sys.modules
+ False
+ >>> m = context.evaluate('modules/' + modname)
+ >>> m.__name__ == modname
+ True
+ >>> modname in sys.modules
+ True
+
+ Since this is trusted code, we can look at whatever is in the
+ module, not just __name__ or what's declared in a security
+ assertion::
+
+ >>> m.x
+ 42
+
+ Clean up after ourselves::
+
+ >>> del sys.modules[modname]
+
+ """
+
+ _create_context = TrustedZopeContext
+
+
+class TraversableModuleImporter(SimpleModuleImporter):
+
+ implements(ITraversable)
+
+ def traverse(self, name, further_path):
+ try:
+ return self[name]
+ except KeyError:
+ raise NotFoundError(name)
+
+
def _Engine(engine=None):
if engine is None:
engine = ZopeEngine()
-
- for pt in ZopePathExpr._default_type_names:
- engine.registerType(pt, ZopePathExpr)
+ engine = _create_base_engine(engine, ZopePathExpr)
+ engine.registerType('python', ZopePythonExpr)
+
+ # Using a proxy around sys.modules allows page templates to use
+ # modules for which security declarations have been made, but
+ # disallows execution of any import-time code for modules, which
+ # should not be allowed to happen during rendering.
+ engine.registerBaseName('modules', ProxyFactory(sys.modules))
+
+ return engine
+
+def _TrustedEngine(engine=None):
+ if engine is None:
+ engine = TrustedZopeEngine()
+ engine = _create_base_engine(engine, TrustedZopePathExpr)
+ engine.registerType('python', PythonExpr)
+ engine.registerBaseName('modules', TraversableModuleImporter())
+ return engine
+
+def _create_base_engine(engine, pathtype):
+ for pt in pathtype._default_type_names:
+ engine.registerType(pt, pathtype)
engine.registerType('string', StringExpr)
- engine.registerType('python', ZopePythonExpr)
engine.registerType('not', NotExpr)
engine.registerType('defer', DeferExpr)
- engine.registerBaseName('modules', ProxyFactory(sys.modules))
return engine
+
Engine = _Engine()
+TrustedEngine = _TrustedEngine()
+
class AppPT(object):
def pt_getEngine(self):
return Engine
+
+
+class TrustedAppPT(object):
+
+ def pt_getEngine(self):
+ return TrustedEngine
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/metaconfigure.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/metaconfigure.py 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/metaconfigure.py 2004-07-23 19:03:56 UTC (rev 26720)
@@ -17,6 +17,7 @@
$Id$
"""
from zope.app.pagetemplate.engine import Engine, _Engine
+from zope.app.pagetemplate.engine import TrustedEngine, _TrustedEngine
from zope.testing.cleanup import addCleanUp
from zope.interface import Interface
from zope.configuration.fields import GlobalObject
@@ -42,13 +43,19 @@
def expressiontype(_context, name, handler):
_context.action(
discriminator = ("tales:expressiontype", name),
- callable = Engine.registerType,
+ callable = registerType,
args = (name, handler)
)
+def registerType(name, handler):
+ Engine.registerType(name, handler)
+ TrustedEngine.registerType(name, handler)
+
def clear():
Engine.__init__()
_Engine(Engine)
+ TrustedEngine.__init__()
+ _TrustedEngine(TrustedEngine)
addCleanUp(clear)
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/tests/test_engine.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/tests/test_engine.py 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/tests/test_engine.py 2004-07-23 19:03:56 UTC (rev 26720)
@@ -19,9 +19,7 @@
from zope.testing.doctestunit import DocTestSuite
def test_suite():
- return unittest.TestSuite((
- DocTestSuite('zope.app.pagetemplate.engine'),
- ))
+ return DocTestSuite('zope.app.pagetemplate.engine')
if __name__ == '__main__':
unittest.main(defaultTest='test_suite')
Copied: Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/tests/trusted.py (from rev 26657, Zope3/trunk/src/zope/app/pagetemplate/tests/trusted.py)
Property changes on: Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/tests/trusted.py
___________________________________________________________________
Name: svn:mime-type
+ text/x-python
Name: svn:eol-style
+ native
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/viewpagetemplatefile.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/viewpagetemplatefile.py 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/pagetemplate/viewpagetemplatefile.py 2004-07-23 19:03:56 UTC (rev 26720)
@@ -20,9 +20,9 @@
from zope.pagetemplate.pagetemplatefile import PageTemplateFile
from zope.component import getView
-from zope.app.pagetemplate.engine import AppPT
+from zope.app.pagetemplate.engine import TrustedAppPT
-class ViewPageTemplateFile(AppPT, PageTemplateFile):
+class ViewPageTemplateFile(TrustedAppPT, PageTemplateFile):
"""Page Templates used as methods of views defined as Python classes.
"""
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/pagetemplateresource.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/pagetemplateresource.py 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/publisher/pagetemplateresource.py 2004-07-23 19:03:56 UTC (rev 26720)
@@ -17,9 +17,9 @@
"""
from zope.pagetemplate.pagetemplatefile import PageTemplateFile
-from zope.app.pagetemplate.engine import AppPT
+from zope.app.pagetemplate.engine import TrustedAppPT
-class PageTemplate(AppPT, PageTemplateFile):
+class PageTemplate(TrustedAppPT, PageTemplateFile):
"""
Resource that is a page template
"""
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/traversing/ftests/test_vhosting.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/traversing/ftests/test_vhosting.py 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/traversing/ftests/test_vhosting.py 2004-07-23 19:03:56 UTC (rev 26720)
@@ -23,7 +23,7 @@
from transaction import get_transaction
from zope.app.publisher.browser.resource import Resource
from zope.app.traversing.api import traverse
-from zope.security.checker import defineChecker, NoProxy
+from zope.security.checker import defineChecker, NamesChecker, NoProxy
from zope.app.container.contained import Contained
from zope.app.zptpage.zptpage import ZPTPage
@@ -128,6 +128,7 @@
def test_resources(self):
ztapi.browserResource('quux', Resource)
+ defineChecker(Resource, NamesChecker(['__call__']))
self.addPage('/foo/bar/pt',
u'<span tal:replace="context/++resource++quux" />')
self.verify('/foo/bar/pt', 'http://localhost/@@/quux\n')
Modified: Zope3/branches/ZopeX3-3.0/src/zope/app/zptpage/tests/test_zptpage.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/app/zptpage/tests/test_zptpage.py 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/app/zptpage/tests/test_zptpage.py 2004-07-23 19:03:56 UTC (rev 26720)
@@ -99,6 +99,8 @@
return 'None'
return name
+ defineChecker(AU, NamesChecker(['__str__']))
+
from zope.app.traversing.namespace import view
ztapi.provideNamespaceHandler('view', view)
ztapi.browserView(IZPTPage, 'name', AU)
Copied: Zope3/branches/ZopeX3-3.0/src/zope/restrictedpython (from rev 26688, Zope3/trunk/src/zope/restrictedpython)
Modified: Zope3/branches/ZopeX3-3.0/src/zope/tales/expressions.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/tales/expressions.py 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/tales/expressions.py 2004-07-23 19:03:56 UTC (rev 26720)
@@ -16,7 +16,6 @@
$Id$
"""
import re
-from types import StringTypes, TupleType
from zope.interface import implements
from zope.tales.tales import CompilerError
@@ -96,7 +95,7 @@
# check for initial function
raise CompilerError(
'Namespace function specified in first subpath element')
- elif isinstance(first,StringTypes):
+ elif isinstance(first, basestring):
# check for initial ?
raise CompilerError(
'Dynamic name specified in first subpath element')
@@ -122,13 +121,13 @@
ob = ob()
for element in compiled_path:
- if isinstance(element,TupleType):
+ if isinstance(element, tuple):
ob = self._traverser(ob, element, econtext)
- elif isinstance(element,StringTypes):
+ elif isinstance(element, basestring):
val = vars[element]
# If the value isn't a string, assume it's a sequence
# of path names.
- if isinstance(val,StringTypes):
+ if isinstance(val, basestring):
val = (val,)
ob = self._traverser(ob, val, econtext)
elif callable(element):
@@ -303,8 +302,12 @@
"""Minimal module importer with no security."""
def __getitem__(self, module):
- mod = __import__(module)
+ mod = self._get_toplevel_module(module)
path = module.split('.')
for name in path[1:]:
mod = getattr(mod, name)
return mod
+
+ def _get_toplevel_module(self, module):
+ # This can be overridden to add security proxies.
+ return __import__(module)
Modified: Zope3/branches/ZopeX3-3.0/src/zope/tales/pythonexpr.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/tales/pythonexpr.py 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/tales/pythonexpr.py 2004-07-23 19:03:56 UTC (rev 26720)
@@ -20,10 +20,16 @@
def __init__(self, name, expr, engine):
text = ' '.join(expr.splitlines()).strip()
self.text = text
- # The next line can legally raise SyntaxError.
- self._code = code = compile(text, '<string>', 'eval')
+ try:
+ code = self._compile(text, '<string>')
+ except SyntaxError, e:
+ raise engine.getCompilerError()(str(e))
+ self._code = code
self._varnames = code.co_names
+ def _compile(self, text, filename):
+ return compile(text, filename, 'eval')
+
def _bind_used_names(self, econtext, builtins):
# Bind template variables
names = {}
Modified: Zope3/branches/ZopeX3-3.0/src/zope/tales/tests/test_expressions.py
===================================================================
--- Zope3/branches/ZopeX3-3.0/src/zope/tales/tests/test_expressions.py 2004-07-23 18:57:48 UTC (rev 26719)
+++ Zope3/branches/ZopeX3-3.0/src/zope/tales/tests/test_expressions.py 2004-07-23 19:03:56 UTC (rev 26720)
@@ -131,7 +131,11 @@
context=self.context
self.assertEqual(expr(context), 4)
+ def testPythonErrorRaisesCompilerError(self):
+ self.assertRaises(self.engine.getCompilerError(),
+ self.engine.compile, 'python: splat.0')
+
class FunctionTests(ExpressionTestBase):
def setUp(self):
More information about the Zope3-Checkins
mailing list