OLD | NEW |
| (Empty) |
1 # -*- coding: utf-8 -*- | |
2 """ | |
3 jinja2.sandbox | |
4 ~~~~~~~~~~~~~~ | |
5 | |
6 Adds a sandbox layer to Jinja as it was the default behavior in the old | |
7 Jinja 1 releases. This sandbox is slightly different from Jinja 1 as the | |
8 default behavior is easier to use. | |
9 | |
10 The behavior can be changed by subclassing the environment. | |
11 | |
12 :copyright: (c) 2010 by the Jinja Team. | |
13 :license: BSD. | |
14 """ | |
15 import operator | |
16 from jinja2.environment import Environment | |
17 from jinja2.exceptions import SecurityError | |
18 from jinja2._compat import string_types, function_type, method_type, \ | |
19 traceback_type, code_type, frame_type, generator_type, PY2 | |
20 | |
21 | |
22 #: maximum number of items a range may produce | |
23 MAX_RANGE = 100000 | |
24 | |
25 #: attributes of function objects that are considered unsafe. | |
26 UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', | |
27 'func_defaults', 'func_globals']) | |
28 | |
29 #: unsafe method attributes. function attributes are unsafe for methods too | |
30 UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self']) | |
31 | |
32 #: unsafe generator attirbutes. | |
33 UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code']) | |
34 | |
35 # On versions > python 2 the special attributes on functions are gone, | |
36 # but they remain on methods and generators for whatever reason. | |
37 if not PY2: | |
38 UNSAFE_FUNCTION_ATTRIBUTES = set() | |
39 | |
40 import warnings | |
41 | |
42 # make sure we don't warn in python 2.6 about stuff we don't care about | |
43 warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning, | |
44 module='jinja2.sandbox') | |
45 | |
46 from collections import deque | |
47 | |
48 _mutable_set_types = (set,) | |
49 _mutable_mapping_types = (dict,) | |
50 _mutable_sequence_types = (list,) | |
51 | |
52 | |
53 # on python 2.x we can register the user collection types | |
54 try: | |
55 from UserDict import UserDict, DictMixin | |
56 from UserList import UserList | |
57 _mutable_mapping_types += (UserDict, DictMixin) | |
58 _mutable_set_types += (UserList,) | |
59 except ImportError: | |
60 pass | |
61 | |
62 # if sets is still available, register the mutable set from there as well | |
63 try: | |
64 from sets import Set | |
65 _mutable_set_types += (Set,) | |
66 except ImportError: | |
67 pass | |
68 | |
69 #: register Python 2.6 abstract base classes | |
70 try: | |
71 from collections import MutableSet, MutableMapping, MutableSequence | |
72 _mutable_set_types += (MutableSet,) | |
73 _mutable_mapping_types += (MutableMapping,) | |
74 _mutable_sequence_types += (MutableSequence,) | |
75 except ImportError: | |
76 pass | |
77 | |
78 _mutable_spec = ( | |
79 (_mutable_set_types, frozenset([ | |
80 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove', | |
81 'symmetric_difference_update', 'update' | |
82 ])), | |
83 (_mutable_mapping_types, frozenset([ | |
84 'clear', 'pop', 'popitem', 'setdefault', 'update' | |
85 ])), | |
86 (_mutable_sequence_types, frozenset([ | |
87 'append', 'reverse', 'insert', 'sort', 'extend', 'remove' | |
88 ])), | |
89 (deque, frozenset([ | |
90 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop', | |
91 'popleft', 'remove', 'rotate' | |
92 ])) | |
93 ) | |
94 | |
95 | |
96 def safe_range(*args): | |
97 """A range that can't generate ranges with a length of more than | |
98 MAX_RANGE items. | |
99 """ | |
100 rng = range(*args) | |
101 if len(rng) > MAX_RANGE: | |
102 raise OverflowError('range too big, maximum size for range is %d' % | |
103 MAX_RANGE) | |
104 return rng | |
105 | |
106 | |
107 def unsafe(f): | |
108 """Marks a function or method as unsafe. | |
109 | |
110 :: | |
111 | |
112 @unsafe | |
113 def delete(self): | |
114 pass | |
115 """ | |
116 f.unsafe_callable = True | |
117 return f | |
118 | |
119 | |
120 def is_internal_attribute(obj, attr): | |
121 """Test if the attribute given is an internal python attribute. For | |
122 example this function returns `True` for the `func_code` attribute of | |
123 python objects. This is useful if the environment method | |
124 :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden. | |
125 | |
126 >>> from jinja2.sandbox import is_internal_attribute | |
127 >>> is_internal_attribute(lambda: None, "func_code") | |
128 True | |
129 >>> is_internal_attribute((lambda x:x).func_code, 'co_code') | |
130 True | |
131 >>> is_internal_attribute(str, "upper") | |
132 False | |
133 """ | |
134 if isinstance(obj, function_type): | |
135 if attr in UNSAFE_FUNCTION_ATTRIBUTES: | |
136 return True | |
137 elif isinstance(obj, method_type): | |
138 if attr in UNSAFE_FUNCTION_ATTRIBUTES or \ | |
139 attr in UNSAFE_METHOD_ATTRIBUTES: | |
140 return True | |
141 elif isinstance(obj, type): | |
142 if attr == 'mro': | |
143 return True | |
144 elif isinstance(obj, (code_type, traceback_type, frame_type)): | |
145 return True | |
146 elif isinstance(obj, generator_type): | |
147 if attr in UNSAFE_GENERATOR_ATTRIBUTES: | |
148 return True | |
149 return attr.startswith('__') | |
150 | |
151 | |
152 def modifies_known_mutable(obj, attr): | |
153 """This function checks if an attribute on a builtin mutable object | |
154 (list, dict, set or deque) would modify it if called. It also supports | |
155 the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and | |
156 with Python 2.6 onwards the abstract base classes `MutableSet`, | |
157 `MutableMapping`, and `MutableSequence`. | |
158 | |
159 >>> modifies_known_mutable({}, "clear") | |
160 True | |
161 >>> modifies_known_mutable({}, "keys") | |
162 False | |
163 >>> modifies_known_mutable([], "append") | |
164 True | |
165 >>> modifies_known_mutable([], "index") | |
166 False | |
167 | |
168 If called with an unsupported object (such as unicode) `False` is | |
169 returned. | |
170 | |
171 >>> modifies_known_mutable("foo", "upper") | |
172 False | |
173 """ | |
174 for typespec, unsafe in _mutable_spec: | |
175 if isinstance(obj, typespec): | |
176 return attr in unsafe | |
177 return False | |
178 | |
179 | |
180 class SandboxedEnvironment(Environment): | |
181 """The sandboxed environment. It works like the regular environment but | |
182 tells the compiler to generate sandboxed code. Additionally subclasses of | |
183 this environment may override the methods that tell the runtime what | |
184 attributes or functions are safe to access. | |
185 | |
186 If the template tries to access insecure code a :exc:`SecurityError` is | |
187 raised. However also other exceptions may occour during the rendering so | |
188 the caller has to ensure that all exceptions are catched. | |
189 """ | |
190 sandboxed = True | |
191 | |
192 #: default callback table for the binary operators. A copy of this is | |
193 #: available on each instance of a sandboxed environment as | |
194 #: :attr:`binop_table` | |
195 default_binop_table = { | |
196 '+': operator.add, | |
197 '-': operator.sub, | |
198 '*': operator.mul, | |
199 '/': operator.truediv, | |
200 '//': operator.floordiv, | |
201 '**': operator.pow, | |
202 '%': operator.mod | |
203 } | |
204 | |
205 #: default callback table for the unary operators. A copy of this is | |
206 #: available on each instance of a sandboxed environment as | |
207 #: :attr:`unop_table` | |
208 default_unop_table = { | |
209 '+': operator.pos, | |
210 '-': operator.neg | |
211 } | |
212 | |
213 #: a set of binary operators that should be intercepted. Each operator | |
214 #: that is added to this set (empty by default) is delegated to the | |
215 #: :meth:`call_binop` method that will perform the operator. The default | |
216 #: operator callback is specified by :attr:`binop_table`. | |
217 #: | |
218 #: The following binary operators are interceptable: | |
219 #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**`` | |
220 #: | |
221 #: The default operation form the operator table corresponds to the | |
222 #: builtin function. Intercepted calls are always slower than the native | |
223 #: operator call, so make sure only to intercept the ones you are | |
224 #: interested in. | |
225 #: | |
226 #: .. versionadded:: 2.6 | |
227 intercepted_binops = frozenset() | |
228 | |
229 #: a set of unary operators that should be intercepted. Each operator | |
230 #: that is added to this set (empty by default) is delegated to the | |
231 #: :meth:`call_unop` method that will perform the operator. The default | |
232 #: operator callback is specified by :attr:`unop_table`. | |
233 #: | |
234 #: The following unary operators are interceptable: ``+``, ``-`` | |
235 #: | |
236 #: The default operation form the operator table corresponds to the | |
237 #: builtin function. Intercepted calls are always slower than the native | |
238 #: operator call, so make sure only to intercept the ones you are | |
239 #: interested in. | |
240 #: | |
241 #: .. versionadded:: 2.6 | |
242 intercepted_unops = frozenset() | |
243 | |
244 def intercept_unop(self, operator): | |
245 """Called during template compilation with the name of a unary | |
246 operator to check if it should be intercepted at runtime. If this | |
247 method returns `True`, :meth:`call_unop` is excuted for this unary | |
248 operator. The default implementation of :meth:`call_unop` will use | |
249 the :attr:`unop_table` dictionary to perform the operator with the | |
250 same logic as the builtin one. | |
251 | |
252 The following unary operators are interceptable: ``+`` and ``-`` | |
253 | |
254 Intercepted calls are always slower than the native operator call, | |
255 so make sure only to intercept the ones you are interested in. | |
256 | |
257 .. versionadded:: 2.6 | |
258 """ | |
259 return False | |
260 | |
261 | |
262 def __init__(self, *args, **kwargs): | |
263 Environment.__init__(self, *args, **kwargs) | |
264 self.globals['range'] = safe_range | |
265 self.binop_table = self.default_binop_table.copy() | |
266 self.unop_table = self.default_unop_table.copy() | |
267 | |
268 def is_safe_attribute(self, obj, attr, value): | |
269 """The sandboxed environment will call this method to check if the | |
270 attribute of an object is safe to access. Per default all attributes | |
271 starting with an underscore are considered private as well as the | |
272 special attributes of internal python objects as returned by the | |
273 :func:`is_internal_attribute` function. | |
274 """ | |
275 return not (attr.startswith('_') or is_internal_attribute(obj, attr)) | |
276 | |
277 def is_safe_callable(self, obj): | |
278 """Check if an object is safely callable. Per default a function is | |
279 considered safe unless the `unsafe_callable` attribute exists and is | |
280 True. Override this method to alter the behavior, but this won't | |
281 affect the `unsafe` decorator from this module. | |
282 """ | |
283 return not (getattr(obj, 'unsafe_callable', False) or | |
284 getattr(obj, 'alters_data', False)) | |
285 | |
286 def call_binop(self, context, operator, left, right): | |
287 """For intercepted binary operator calls (:meth:`intercepted_binops`) | |
288 this function is executed instead of the builtin operator. This can | |
289 be used to fine tune the behavior of certain operators. | |
290 | |
291 .. versionadded:: 2.6 | |
292 """ | |
293 return self.binop_table[operator](left, right) | |
294 | |
295 def call_unop(self, context, operator, arg): | |
296 """For intercepted unary operator calls (:meth:`intercepted_unops`) | |
297 this function is executed instead of the builtin operator. This can | |
298 be used to fine tune the behavior of certain operators. | |
299 | |
300 .. versionadded:: 2.6 | |
301 """ | |
302 return self.unop_table[operator](arg) | |
303 | |
304 def getitem(self, obj, argument): | |
305 """Subscribe an object from sandboxed code.""" | |
306 try: | |
307 return obj[argument] | |
308 except (TypeError, LookupError): | |
309 if isinstance(argument, string_types): | |
310 try: | |
311 attr = str(argument) | |
312 except Exception: | |
313 pass | |
314 else: | |
315 try: | |
316 value = getattr(obj, attr) | |
317 except AttributeError: | |
318 pass | |
319 else: | |
320 if self.is_safe_attribute(obj, argument, value): | |
321 return value | |
322 return self.unsafe_undefined(obj, argument) | |
323 return self.undefined(obj=obj, name=argument) | |
324 | |
325 def getattr(self, obj, attribute): | |
326 """Subscribe an object from sandboxed code and prefer the | |
327 attribute. The attribute passed *must* be a bytestring. | |
328 """ | |
329 try: | |
330 value = getattr(obj, attribute) | |
331 except AttributeError: | |
332 try: | |
333 return obj[attribute] | |
334 except (TypeError, LookupError): | |
335 pass | |
336 else: | |
337 if self.is_safe_attribute(obj, attribute, value): | |
338 return value | |
339 return self.unsafe_undefined(obj, attribute) | |
340 return self.undefined(obj=obj, name=attribute) | |
341 | |
342 def unsafe_undefined(self, obj, attribute): | |
343 """Return an undefined object for unsafe attributes.""" | |
344 return self.undefined('access to attribute %r of %r ' | |
345 'object is unsafe.' % ( | |
346 attribute, | |
347 obj.__class__.__name__ | |
348 ), name=attribute, obj=obj, exc=SecurityError) | |
349 | |
350 def call(__self, __context, __obj, *args, **kwargs): | |
351 """Call an object from sandboxed code.""" | |
352 # the double prefixes are to avoid double keyword argument | |
353 # errors when proxying the call. | |
354 if not __self.is_safe_callable(__obj): | |
355 raise SecurityError('%r is not safely callable' % (__obj,)) | |
356 return __context.call(__obj, *args, **kwargs) | |
357 | |
358 | |
359 class ImmutableSandboxedEnvironment(SandboxedEnvironment): | |
360 """Works exactly like the regular `SandboxedEnvironment` but does not | |
361 permit modifications on the builtin mutable objects `list`, `set`, and | |
362 `dict` by using the :func:`modifies_known_mutable` function. | |
363 """ | |
364 | |
365 def is_safe_attribute(self, obj, attr, value): | |
366 if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value): | |
367 return False | |
368 return not modifies_known_mutable(obj, attr) | |
OLD | NEW |