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