Skip to content

Commit

Permalink
Merge pull request #241 from zopefoundation/test-str-format-key-item-…
Browse files Browse the repository at this point in the history
…access-master

Test that `str.format` checks security for accessed keys and items. [master]
  • Loading branch information
mauritsvanrees committed Jan 25, 2018
2 parents 5379998 + 2afa2ec commit d1ebd9a
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGES.rst
Expand Up @@ -14,6 +14,10 @@ https://github.com/zopefoundation/Zope/blob/4.0a6/CHANGES.rst
Bugfixes
++++++++

- Test that ``str.format`` checks security for accessed keys and items.
The real fix is in the AccessControl package, version 4.0b1.
Part of PloneHotfix20171128.

- Made Redirect unavailable as url. Part of PloneHotfix20171128.

- Fixed DocumentTemplate version 3.0b2 fixes #179 (ZMI navtree error).
Expand Down
189 changes: 178 additions & 11 deletions src/Zope2/App/tests/test_safe_formatter.py
Expand Up @@ -6,11 +6,33 @@
import unittest


BAD_STR = """<p tal:content="python:'{0}: {0.__class__}'.format(context)" />"""
BAD_UNICODE = """\
<p tal:content="python:u'{0}: {0.__class__}'.format(context)" />"""
GOOD_STR = """<p tal:content="python:('%s' % context).lower()" />"""
GOOD_UNICODE = u"""<p tal:content="python:('%s' % context).lower()" />"""
BAD_ATTR_STR = """
<p tal:content="python:'class of {0} is {0.__class__}'.format(context)" />
"""
BAD_ATTR_UNICODE = """
<p tal:content="python:u'class of {0} is {0.__class__}'.format(context)" />
"""
BAD_KEY_STR = """
<p tal:content="python:'access by key: {0[test_folder_1_]}'.format(context)" />
"""
BAD_KEY_UNICODE = """
<p tal:content="python:u'access by key: {0[test_folder_1_]}'.format(context)" />
"""
BAD_ITEM_STR = """
<p tal:content="python:'access by item: {0[0]}'.format(context)" />
"""
BAD_ITEM_UNICODE = """
<p tal:content="python:u'access by item: {0[0]}'.format(context)" />
"""
GOOD_STR = '<p tal:content="python:(\'%s\' % context).lower()" />'
GOOD_UNICODE = '<p tal:content="python:(\'%s\' % context).lower()" />'
# Attribute access is not completely forbidden, it is simply checked.
GOOD_FORMAT_ATTR_STR = """
<p tal:content="python:'title of {0} is {0.title}'.format(context)" />
"""
GOOD_FORMAT_ATTR_UNICODE = """
<p tal:content="python:u'title of {0} is {0.title}'.format(context)" />
"""


def noop(context=None):
Expand All @@ -25,40 +47,81 @@ def hack_pt(pt, context=None):
pt.context = context


class FormatterTest(unittest.TestCase):
class UnauthorizedSecurityPolicy:
"""Policy which denies every access."""

def validate(self, *args, **kw):
from AccessControl.unauthorized import Unauthorized
raise Unauthorized('Nothing is allowed!')


def test_cook_zope2_page_templates_bad_str(self):
class FormatterFunctionalTest(FunctionalTestCase):

def test_cook_zope2_page_templates_bad_attr_str(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
pt = ZopePageTemplate('mytemplate', BAD_STR)
pt = ZopePageTemplate('mytemplate', BAD_ATTR_STR)
hack_pt(pt)
with self.assertRaises(Unauthorized) as err:
pt.pt_render()
self.assertEqual(
"You are not allowed to access '__class__' in this context",
str(err.exception))
hack_pt(pt, context=self.app)
with self.assertRaises(Unauthorized) as err:
pt.pt_render()
self.assertEqual(
"You are not allowed to access '__class__' in this context",
str(err.exception))

def test_cook_zope2_page_templates_bad_unicode(self):
def test_cook_zope2_page_templates_bad_attr_unicode(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
pt = ZopePageTemplate('mytemplate', BAD_UNICODE)
pt = ZopePageTemplate('mytemplate', BAD_ATTR_UNICODE)
hack_pt(pt)
with self.assertRaises(Unauthorized) as err:
pt.pt_render()
self.assertEqual(
"You are not allowed to access '__class__' in this context",
str(err.exception))
hack_pt(pt, context=self.app)
with self.assertRaises(Unauthorized) as err:
pt.pt_render()
self.assertEqual(
"You are not allowed to access '__class__' in this context",
str(err.exception))

def test_cook_zope2_page_templates_good_str(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
pt = ZopePageTemplate('mytemplate', GOOD_STR)
hack_pt(pt)
self.assertEqual(pt.pt_render().strip(), '<p>none</p>')
hack_pt(pt, context=self.app)
self.assertEqual(
pt.pt_render().strip(), '<p>&lt;application at &gt;</p>')

def test_cook_zope2_page_templates_good_unicode(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
pt = ZopePageTemplate('mytemplate', GOOD_UNICODE)
hack_pt(pt)
self.assertEqual(pt.pt_render().strip(), '<p>none</p>')
hack_pt(pt, self.app)
self.assertEqual(
pt.pt_render().strip(), '<p>&lt;application at &gt;</p>')

def test_cook_zope2_page_templates_good_format_attr_str(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
pt = ZopePageTemplate('mytemplate', GOOD_FORMAT_ATTR_STR)
hack_pt(pt, self.app)
self.assertEqual(
pt.pt_render().strip(),
'<p>title of &lt;Application at &gt; is Zope</p>')

def test_cook_zope2_page_templates_good_format_attr_unicode(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
pt = ZopePageTemplate('mytemplate', GOOD_FORMAT_ATTR_UNICODE)
hack_pt(pt, self.app)
self.assertEqual(
pt.pt_render().strip(),
'<p>title of &lt;Application at &gt; is Zope</p>')

class UnauthorizedSecurityPolicy(object):
"""Policy which denies every access."""
Expand Down Expand Up @@ -133,9 +196,113 @@ def test_cook_zope3_page_templates_using_format(self):
hack_pt(pt)
# Need to pass a namespace.
namespace = {'context': self.app}
# Even when one of the accessed items requires a role that we do
# not have, we get no Unauthorized, because this is a filesystem
# template.
self.app.test_folder_1_.__roles__ = ['Manager']
self.assertEqual(
pt.pt_render(namespace).strip(),
u"<p>class of &lt;application at &gt; is "
u"&lt;class 'ofs.application.application'&gt;</p>\n"
u"<p>CLASS OF &lt;APPLICATION AT &gt; IS "
u"&lt;CLASS 'OFS.APPLICATION.APPLICATION'&gt;</p>")
u"&lt;CLASS 'OFS.APPLICATION.APPLICATION'&gt;</p>\n"
u"<p>{'foo': &lt;Folder at /test_folder_1_&gt;} has "
u"foo=&lt;Folder at test_folder_1_&gt;</p>\n"
u"<p>{'foo': &lt;Folder at /test_folder_1_&gt;} has "
u"foo=&lt;Folder at test_folder_1_&gt;</p>\n"
u"<p>[&lt;Folder at /test_folder_1_&gt;] has "
u"first item &lt;Folder at test_folder_1_&gt;</p>\n"
u"<p>[&lt;Folder at /test_folder_1_&gt;] has "
u"first item &lt;Folder at test_folder_1_&gt;</p>"
)

def test_cook_zope2_page_templates_bad_key_str(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
pt = ZopePageTemplate('mytemplate', BAD_KEY_STR)
hack_pt(pt, self.app)
self.assertEqual(
pt.pt_render(),
'<p>access by key: &lt;Folder at test_folder_1_&gt;</p>')
self.app.test_folder_1_.__roles__ = ['Manager']
with self.assertRaises(Unauthorized) as err:
pt.pt_render()
self.assertEqual(
"You are not allowed to access 'test_folder_1_' in this context",
str(err.exception))

def test_cook_zope2_page_templates_bad_key_unicode(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
pt = ZopePageTemplate('mytemplate', BAD_KEY_UNICODE)
hack_pt(pt, self.app)
self.assertEqual(
pt.pt_render(),
'<p>access by key: &lt;Folder at test_folder_1_&gt;</p>')
self.app.test_folder_1_.__roles__ = ['Manager']
with self.assertRaises(Unauthorized) as err:
pt.pt_render()
self.assertEqual(
"You are not allowed to access 'test_folder_1_' in this context",
str(err.exception))

def test_cook_zope2_page_templates_bad_item_str(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
self.app.testlist = [self.app.test_folder_1_]
pt = ZopePageTemplate('mytemplate', BAD_ITEM_STR)
hack_pt(pt, self.app.testlist)
self.assertEqual(
pt.pt_render(),
'<p>access by item: &lt;Folder at test_folder_1_&gt;</p>')
self.app.test_folder_1_.__roles__ = ['Manager']
with self.assertRaises(Unauthorized) as err:
pt.pt_render()
self.assertEqual(
"You are not allowed to access 'test_folder_1_' in this context",
str(err.exception))

def test_cook_zope2_page_templates_bad_item_unicode(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
self.app.testlist = [self.app.test_folder_1_]
pt = ZopePageTemplate('mytemplate', BAD_ITEM_UNICODE)
hack_pt(pt, self.app.testlist)
self.assertEqual(
pt.pt_render(),
'<p>access by item: &lt;Folder at test_folder_1_&gt;</p>')
self.app.test_folder_1_.__roles__ = ['Manager']
with self.assertRaises(Unauthorized) as err:
pt.pt_render()
self.assertEqual(
"You are not allowed to access 'test_folder_1_' in this context",
str(err.exception))

def assert_is_checked_via_security_manager(self, pt_content):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
from AccessControl.SecurityManager import setSecurityPolicy
from AccessControl.SecurityManagement import noSecurityManager
from AccessControl.SecurityManagement import getSecurityManager

pt = ZopePageTemplate('mytemplate', pt_content)
noSecurityManager()
old_security_policy = setSecurityPolicy(UnauthorizedSecurityPolicy())
try:
hack_pt(pt, context=self.app)
with self.assertRaises(Unauthorized) as err:
pt.pt_render()
self.assertEqual(
'Nothing is allowed!',
str(err.exception))
finally:
setSecurityPolicy(old_security_policy)

def test_getattr_access_is_checked_via_security_manager(self):
self.assert_is_checked_via_security_manager(
"""<p tal:content="python:'{0.acl_users}'.format(context)" />""")

def test_getitem_access_is_checked_via_security_manager(self):
self.assert_is_checked_via_security_manager(
"""<p tal:content="python:'{c[acl_users]}'.format(c=context)" />"""
)

def test_key_access_is_checked_via_security_manager(self):
self.assert_is_checked_via_security_manager(
"""<p tal:content="python:'{c[0]}'.format(c=[context])" />"""
)
4 changes: 4 additions & 0 deletions src/Zope2/App/tests/using_format_zope3_page_template.pt
@@ -1,2 +1,6 @@
<p tal:content="python:'class of {0} is {0.__class__}'.format(context).lower()" />
<p tal:content="python:u'class of {0} is {0.__class__}'.format(context).upper()" />
<p tal:content="python:'{0} has foo={0[foo]}'.format({'foo': context.test_folder_1_})" />
<p tal:content="python:u'{0} has foo={0[foo]}'.format({'foo': context.test_folder_1_})" />
<p tal:content="python:'{0} has first item {0[0]}'.format([context.test_folder_1_])" />
<p tal:content="python:u'{0} has first item {0[0]}'.format([context.test_folder_1_])" />

0 comments on commit d1ebd9a

Please sign in to comment.