/
test_safe_formatter.py
282 lines (247 loc) · 11.5 KB
/
test_safe_formatter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
"""This module contains integration tests for AccessControl.safe_formatter."""
import unittest
from Testing.ZopeTestCase import FunctionalTestCase
from zExceptions import Unauthorized
try:
from html import escape
import functools
# We do not want escaped " and ', as PageTemplate neither does it:
escape = functools.partial(escape, quote=False)
except ImportError: # PY2
from cgi import escape
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):
return lambda: context
def hack_pt(pt, context=None):
# hacks to avoid getting error in pt_render.
pt.getPhysicalRoot = noop()
pt._getContext = noop(context)
pt._getContainer = noop(context)
pt.context = context
class UnauthorizedSecurityPolicy:
"""Policy which denies every access."""
def validate(self, *args, **kw):
from AccessControl.unauthorized import Unauthorized
raise Unauthorized('Nothing is allowed!')
class FormatterFunctionalTest(FunctionalTestCase):
maxDiff = None
def test_cook_zope2_page_templates_bad_attr_str(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
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_attr_unicode(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
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><application at ></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><application at ></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 <Application at > 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 <Application at > is Zope</p>')
def test_access_to_private_content_not_allowed_via_any_attribute(self):
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
# If access to _delObject would be allowed, it would still only say
# something like 'bound method _delObject', without actually deleting
# anything, because methods are not executed in str.format, but there
# may be @properties that give an attacker secret info.
pt = ZopePageTemplate(
'mytemplate',
"""<p tal:content="python:'{0._delObject}'.format(context)" />""")
hack_pt(pt, context=self.app)
with self.assertRaises(Unauthorized) as err:
pt.pt_render()
self.assertEqual(
"You are not allowed to access '_delObject' 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())
getSecurityManager()
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)" />"""
)
# Zope 3 templates are always file system templates. So we actually have
# no problems allowing str.format there.
def test_cook_zope3_page_templates_normal(self):
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
pt = ViewPageTemplateFile('normal_zope3_page_template.pt')
hack_pt(pt)
# Need to pass a namespace.
namespace = {'context': self.app}
self.assertEqual(
pt.pt_render(namespace).strip(),
u'<p><application at ></p>\n'
u'<p><APPLICATION AT ></p>')
def test_cook_zope3_page_templates_using_format(self):
from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
pt = ViewPageTemplateFile('using_format_zope3_page_template.pt')
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 <application at > is "
u"<class 'ofs.application.application'></p>\n"
u"<p>CLASS OF <APPLICATION AT > IS "
u"<CLASS 'OFS.APPLICATION.APPLICATION'></p>\n"
u"<p>{'foo': <Folder at /test_folder_1_>} has "
u"foo=<Folder at test_folder_1_></p>\n"
u"<p>{'foo': <Folder at /test_folder_1_>} has "
u"foo=<Folder at test_folder_1_></p>\n"
u"<p>[<Folder at /test_folder_1_>] has "
u"first item <Folder at test_folder_1_></p>\n"
u"<p>[<Folder at /test_folder_1_>] has "
u"first item <Folder at test_folder_1_></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: <Folder at test_folder_1_></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: <Folder at test_folder_1_></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: <Folder at test_folder_1_></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: <Folder at test_folder_1_></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_key_access_is_checked_via_security_manager(self):
self.assert_is_checked_via_security_manager(
"""<p tal:content="python:'{c[0]}'.format(c=[context])" />"""
)