Commit 2e153b46 authored by Evan Simpson's avatar Evan Simpson

Fix Collector #581, with unit test.

Tidy up inconsistency use of getattr/guarded_getattr.
parent baa8fbb1
...@@ -6,6 +6,12 @@ Zope Changes ...@@ -6,6 +6,12 @@ Zope Changes
Bugs Fixed Bugs Fixed
- Collector #581: TALES Path traversal should not special-case a blank
string in the second element position. It now skips directly
to item access when a path element is blank or has a leading '_'.
- Fixed inconsistent attribute access in TALES Paths.
- Collector #587: fixed wrong migration to string methods in - Collector #587: fixed wrong migration to string methods in
DTMLMethod.py DTMLMethod.py
......
...@@ -17,7 +17,7 @@ Page Template-specific implementation of TALES, with handlers ...@@ -17,7 +17,7 @@ Page Template-specific implementation of TALES, with handlers
for Python expressions, string literals, and paths. for Python expressions, string literals, and paths.
""" """
__version__='$Revision: 1.41 $'[11:-2] __version__='$Revision: 1.42 $'[11:-2]
import re, sys import re, sys
from TALES import Engine, CompilerError, _valid_name, NAME_RE, \ from TALES import Engine, CompilerError, _valid_name, NAME_RE, \
...@@ -291,13 +291,6 @@ def restrictedTraverse(object, path, securityManager, ...@@ -291,13 +291,6 @@ def restrictedTraverse(object, path, securityManager,
get=getattr, has=hasattr, N=None, M=[], get=getattr, has=hasattr, N=None, M=[],
TupleType=type(()) ): TupleType=type(()) ):
if not path[0]:
# If the path starts with an empty string, go to the root first.
object = object.getPhysicalRoot()
if not securityManager.validateValue(object):
raise Unauthorized
path.pop(0)
REQUEST = {'path': path} REQUEST = {'path': path}
REQUEST['TraversalRequestNameStack'] = path = path[:] # Copy! REQUEST['TraversalRequestNameStack'] = path = path[:] # Copy!
path.reverse() path.reverse()
...@@ -310,9 +303,14 @@ def restrictedTraverse(object, path, securityManager, ...@@ -310,9 +303,14 @@ def restrictedTraverse(object, path, securityManager,
object = object(*name) object = object(*name)
continue continue
if name[0] == '_': if not name or name[0] == '_':
# Never allowed in a URL. # Skip directly to item access
raise AttributeError, name o = object[name]
# Check access to the item.
if not validate(object, object, name, o):
raise Unauthorized, name
object = o
continue
if name=='..': if name=='..':
o = get(object, 'aq_parent', M) o = get(object, 'aq_parent', M)
...@@ -333,8 +331,7 @@ def restrictedTraverse(object, path, securityManager, ...@@ -333,8 +331,7 @@ def restrictedTraverse(object, path, securityManager,
container = aq_parent(aq_inner(o)) container = aq_parent(aq_inner(o))
elif has(o, 'im_self'): elif has(o, 'im_self'):
container = o.im_self container = o.im_self
elif (has(get(object, 'aq_base', object), name) elif (has(aq_base(object), name) and get(object, name) == o):
and get(object, name) == o):
container = object container = object
if not validate(object, container, name, o): if not validate(object, container, name, o):
raise Unauthorized, name raise Unauthorized, name
...@@ -354,14 +351,14 @@ def restrictedTraverse(object, path, securityManager, ...@@ -354,14 +351,14 @@ def restrictedTraverse(object, path, securityManager,
# Try to re-raise the original attribute error. # Try to re-raise the original attribute error.
# XXX I think this only happens with # XXX I think this only happens with
# ExtensionClass instances. # ExtensionClass instances.
get(object, name) guarded_getattr(object, name)
raise raise
except TypeError, exc: except TypeError, exc:
if str(exc).find('unsubscriptable') >= 0: if str(exc).find('unsubscriptable') >= 0:
# The object does not support the item interface. # The object does not support the item interface.
# Try to re-raise the original attribute error. # Try to re-raise the original attribute error.
# XXX This is sooooo ugly. # XXX This is sooooo ugly.
get(object, name) guarded_getattr(object, name)
raise raise
else: else:
# Check access to the item. # Check access to the item.
......
...@@ -4,9 +4,20 @@ from Products.PageTemplates import Expressions ...@@ -4,9 +4,20 @@ from Products.PageTemplates import Expressions
class ExpressionTests(unittest.TestCase): class ExpressionTests(unittest.TestCase):
def setUp(self):
self.e = e = Expressions.getEngine()
self.ec = e.getContext(
one = 1,
d = {'one': 1, 'b': 'b', '': 'blank', '_': 'under'},
blank = '',
)
def tearDown(self):
del self.e, self.ec
def testCompile(self): def testCompile(self):
'''Test expression compilation''' '''Test expression compilation'''
e = Expressions.getEngine() e = self.e
for p in ('x', 'x/y', 'x/y/z'): for p in ('x', 'x/y', 'x/y/z'):
e.compile(p) e.compile(p)
e.compile('path:a|b|c/d/e') e.compile('path:a|b|c/d/e')
...@@ -16,6 +27,22 @@ class ExpressionTests(unittest.TestCase): ...@@ -16,6 +27,22 @@ class ExpressionTests(unittest.TestCase):
e.compile('python: 2 + 2') e.compile('python: 2 + 2')
e.compile('python: 2 \n+\n 2\n') e.compile('python: 2 \n+\n 2\n')
def testSimpleEval(self):
'''Test simple expression evaluation'''
ec = self.ec
assert ec.evaluate('one') == 1
assert ec.evaluate('d/one') == 1
assert ec.evaluate('d/b') == 'b'
def testEval1(self):
'''Test advanced expression evaluation 1'''
ec = self.ec
assert ec.evaluate('x | nothing') is None
assert ec.evaluate('d/') == 'blank'
assert ec.evaluate('d/_') == 'under'
assert ec.evaluate('d/ | nothing') == 'blank'
assert ec.evaluate('d/?blank') == 'blank'
def test_suite(): def test_suite():
return unittest.makeSuite(ExpressionTests) return unittest.makeSuite(ExpressionTests)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment