blob: ed145d5d28ec36d29d584e2d8c8e72c3d7fe0dcd [file] [log] [blame]
Armin Ronacherc63243e2008-04-14 22:53:58 +02001# -*- 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
Armin Ronacher55494e42010-01-22 09:41:48 +010012 :copyright: (c) 2010 by the Jinja Team.
Armin Ronacherc63243e2008-04-14 22:53:58 +020013 :license: BSD.
14"""
Armin Ronacher522cad62008-05-17 13:55:37 +020015import operator
Thomas Waldmann7d295622013-05-18 00:06:22 +020016import six
Armin Ronacherc63243e2008-04-14 22:53:58 +020017from jinja2.environment import Environment
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +020018from jinja2.exceptions import SecurityError
Armin Ronacher9a0078d2008-08-13 18:24:17 +020019from jinja2.utils import FunctionType, MethodType, TracebackType, CodeType, \
20 FrameType, GeneratorType
Armin Ronacherc63243e2008-04-14 22:53:58 +020021
22
23#: maximum number of items a range may produce
24MAX_RANGE = 100000
25
Armin Ronacher76c280b2008-05-04 12:31:48 +020026#: attributes of function objects that are considered unsafe.
27UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
28 'func_defaults', 'func_globals'])
29
30#: unsafe method attributes. function attributes are unsafe for methods too
31UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
32
Armin Ronacher522cad62008-05-17 13:55:37 +020033
Armin Ronacher51097172009-02-19 19:57:01 +010034import warnings
35
36# make sure we don't warn in python 2.6 about stuff we don't care about
37warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning,
38 module='jinja2.sandbox')
39
Armin Ronacherd71fff02008-05-26 23:57:07 +020040from collections import deque
Armin Ronacher790b8a82010-02-10 00:05:46 +010041
Armin Ronacher51097172009-02-19 19:57:01 +010042_mutable_set_types = (set,)
Armin Ronacher790b8a82010-02-10 00:05:46 +010043_mutable_mapping_types = (dict,)
44_mutable_sequence_types = (list,)
45
46
47# on python 2.x we can register the user collection types
48try:
49 from UserDict import UserDict, DictMixin
50 from UserList import UserList
51 _mutable_mapping_types += (UserDict, DictMixin)
52 _mutable_set_types += (UserList,)
53except ImportError:
54 pass
Armin Ronacher522cad62008-05-17 13:55:37 +020055
Armin Ronacher51097172009-02-19 19:57:01 +010056# if sets is still available, register the mutable set from there as well
57try:
58 from sets import Set
59 _mutable_set_types += (Set,)
60except ImportError:
61 pass
62
Armin Ronacherd71fff02008-05-26 23:57:07 +020063#: register Python 2.6 abstract base classes
64try:
65 from collections import MutableSet, MutableMapping, MutableSequence
66 _mutable_set_types += (MutableSet,)
67 _mutable_mapping_types += (MutableMapping,)
68 _mutable_sequence_types += (MutableSequence,)
69except ImportError:
70 pass
71
72_mutable_spec = (
73 (_mutable_set_types, frozenset([
74 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
75 'symmetric_difference_update', 'update'
76 ])),
77 (_mutable_mapping_types, frozenset([
78 'clear', 'pop', 'popitem', 'setdefault', 'update'
79 ])),
80 (_mutable_sequence_types, frozenset([
81 'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
82 ])),
83 (deque, frozenset([
84 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
85 'popleft', 'remove', 'rotate'
86 ]))
87)
Armin Ronacher522cad62008-05-17 13:55:37 +020088
Armin Ronacherc63243e2008-04-14 22:53:58 +020089
90def safe_range(*args):
91 """A range that can't generate ranges with a length of more than
Armin Ronacher7ceced52008-05-03 10:15:31 +020092 MAX_RANGE items.
93 """
Thomas Waldmanne0003552013-05-17 23:52:14 +020094 rng = range(*args)
Armin Ronacherc63243e2008-04-14 22:53:58 +020095 if len(rng) > MAX_RANGE:
Armin Ronacher76c280b2008-05-04 12:31:48 +020096 raise OverflowError('range too big, maximum size for range is %d' %
97 MAX_RANGE)
Armin Ronacherc63243e2008-04-14 22:53:58 +020098 return rng
99
100
101def unsafe(f):
Armin Ronacher53278a32011-01-24 01:16:00 +0100102 """Marks a function or method as unsafe.
103
104 ::
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200105
106 @unsafe
107 def delete(self):
108 pass
109 """
Armin Ronacherc63243e2008-04-14 22:53:58 +0200110 f.unsafe_callable = True
111 return f
112
113
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200114def is_internal_attribute(obj, attr):
115 """Test if the attribute given is an internal python attribute. For
116 example this function returns `True` for the `func_code` attribute of
117 python objects. This is useful if the environment method
Jonas Nockertbdd09dd2013-02-23 20:34:47 +0100118 :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden.
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200119
120 >>> from jinja2.sandbox import is_internal_attribute
121 >>> is_internal_attribute(lambda: None, "func_code")
122 True
123 >>> is_internal_attribute((lambda x:x).func_code, 'co_code')
124 True
125 >>> is_internal_attribute(str, "upper")
126 False
127 """
128 if isinstance(obj, FunctionType):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200129 if attr in UNSAFE_FUNCTION_ATTRIBUTES:
130 return True
131 elif isinstance(obj, MethodType):
132 if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
133 attr in UNSAFE_METHOD_ATTRIBUTES:
134 return True
135 elif isinstance(obj, type):
136 if attr == 'mro':
137 return True
138 elif isinstance(obj, (CodeType, TracebackType, FrameType)):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200139 return True
Armin Ronacherd71fff02008-05-26 23:57:07 +0200140 elif isinstance(obj, GeneratorType):
141 if attr == 'gi_frame':
142 return True
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200143 return attr.startswith('__')
144
145
Armin Ronacherd71fff02008-05-26 23:57:07 +0200146def modifies_known_mutable(obj, attr):
Armin Ronacher522cad62008-05-17 13:55:37 +0200147 """This function checks if an attribute on a builtin mutable object
Armin Ronacherd71fff02008-05-26 23:57:07 +0200148 (list, dict, set or deque) would modify it if called. It also supports
149 the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
150 with Python 2.6 onwards the abstract base classes `MutableSet`,
151 `MutableMapping`, and `MutableSequence`.
Armin Ronacher522cad62008-05-17 13:55:37 +0200152
Armin Ronacherd71fff02008-05-26 23:57:07 +0200153 >>> modifies_known_mutable({}, "clear")
Armin Ronacher522cad62008-05-17 13:55:37 +0200154 True
Armin Ronacherd71fff02008-05-26 23:57:07 +0200155 >>> modifies_known_mutable({}, "keys")
Armin Ronacher522cad62008-05-17 13:55:37 +0200156 False
Armin Ronacherd71fff02008-05-26 23:57:07 +0200157 >>> modifies_known_mutable([], "append")
Armin Ronacher522cad62008-05-17 13:55:37 +0200158 True
Armin Ronacherd71fff02008-05-26 23:57:07 +0200159 >>> modifies_known_mutable([], "index")
Armin Ronacher522cad62008-05-17 13:55:37 +0200160 False
161
162 If called with an unsupported object (such as unicode) `False` is
163 returned.
164
Armin Ronacherd71fff02008-05-26 23:57:07 +0200165 >>> modifies_known_mutable("foo", "upper")
Armin Ronacher522cad62008-05-17 13:55:37 +0200166 False
167 """
Armin Ronacherd71fff02008-05-26 23:57:07 +0200168 for typespec, unsafe in _mutable_spec:
169 if isinstance(obj, typespec):
170 return attr in unsafe
Armin Ronacher522cad62008-05-17 13:55:37 +0200171 return False
172
173
Armin Ronacherc63243e2008-04-14 22:53:58 +0200174class SandboxedEnvironment(Environment):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200175 """The sandboxed environment. It works like the regular environment but
176 tells the compiler to generate sandboxed code. Additionally subclasses of
177 this environment may override the methods that tell the runtime what
178 attributes or functions are safe to access.
179
180 If the template tries to access insecure code a :exc:`SecurityError` is
181 raised. However also other exceptions may occour during the rendering so
182 the caller has to ensure that all exceptions are catched.
183 """
Armin Ronacherc63243e2008-04-14 22:53:58 +0200184 sandboxed = True
185
Armin Ronachera9195382010-11-29 13:21:57 +0100186 #: default callback table for the binary operators. A copy of this is
187 #: available on each instance of a sandboxed environment as
188 #: :attr:`binop_table`
189 default_binop_table = {
190 '+': operator.add,
191 '-': operator.sub,
192 '*': operator.mul,
193 '/': operator.truediv,
194 '//': operator.floordiv,
195 '**': operator.pow,
196 '%': operator.mod
197 }
198
199 #: default callback table for the unary operators. A copy of this is
200 #: available on each instance of a sandboxed environment as
201 #: :attr:`unop_table`
202 default_unop_table = {
203 '+': operator.pos,
204 '-': operator.neg
205 }
206
207 #: a set of binary operators that should be intercepted. Each operator
208 #: that is added to this set (empty by default) is delegated to the
209 #: :meth:`call_binop` method that will perform the operator. The default
210 #: operator callback is specified by :attr:`binop_table`.
211 #:
212 #: The following binary operators are interceptable:
213 #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**``
214 #:
215 #: The default operation form the operator table corresponds to the
216 #: builtin function. Intercepted calls are always slower than the native
217 #: operator call, so make sure only to intercept the ones you are
218 #: interested in.
219 #:
220 #: .. versionadded:: 2.6
221 intercepted_binops = frozenset()
222
223 #: a set of unary operators that should be intercepted. Each operator
224 #: that is added to this set (empty by default) is delegated to the
225 #: :meth:`call_unop` method that will perform the operator. The default
226 #: operator callback is specified by :attr:`unop_table`.
227 #:
228 #: The following unary operators are interceptable: ``+``, ``-``
229 #:
230 #: The default operation form the operator table corresponds to the
231 #: builtin function. Intercepted calls are always slower than the native
232 #: operator call, so make sure only to intercept the ones you are
233 #: interested in.
234 #:
235 #: .. versionadded:: 2.6
236 intercepted_unops = frozenset()
237
238 def intercept_unop(self, operator):
239 """Called during template compilation with the name of a unary
240 operator to check if it should be intercepted at runtime. If this
241 method returns `True`, :meth:`call_unop` is excuted for this unary
242 operator. The default implementation of :meth:`call_unop` will use
243 the :attr:`unop_table` dictionary to perform the operator with the
244 same logic as the builtin one.
245
246 The following unary operators are interceptable: ``+`` and ``-``
247
248 Intercepted calls are always slower than the native operator call,
249 so make sure only to intercept the ones you are interested in.
250
251 .. versionadded:: 2.6
252 """
253 return False
254
255
Armin Ronacherc63243e2008-04-14 22:53:58 +0200256 def __init__(self, *args, **kwargs):
257 Environment.__init__(self, *args, **kwargs)
258 self.globals['range'] = safe_range
Armin Ronachera9195382010-11-29 13:21:57 +0100259 self.binop_table = self.default_binop_table.copy()
260 self.unop_table = self.default_unop_table.copy()
Armin Ronacherc63243e2008-04-14 22:53:58 +0200261
Armin Ronacher9a822052008-04-17 18:44:07 +0200262 def is_safe_attribute(self, obj, attr, value):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200263 """The sandboxed environment will call this method to check if the
264 attribute of an object is safe to access. Per default all attributes
265 starting with an underscore are considered private as well as the
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200266 special attributes of internal python objects as returned by the
267 :func:`is_internal_attribute` function.
Armin Ronacherc63243e2008-04-14 22:53:58 +0200268 """
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200269 return not (attr.startswith('_') or is_internal_attribute(obj, attr))
Armin Ronacherc63243e2008-04-14 22:53:58 +0200270
271 def is_safe_callable(self, obj):
272 """Check if an object is safely callable. Per default a function is
273 considered safe unless the `unsafe_callable` attribute exists and is
Armin Ronacher7ceced52008-05-03 10:15:31 +0200274 True. Override this method to alter the behavior, but this won't
Armin Ronacherc63243e2008-04-14 22:53:58 +0200275 affect the `unsafe` decorator from this module.
276 """
Armin Ronacherd9455c12010-11-29 12:39:11 +0100277 return not (getattr(obj, 'unsafe_callable', False) or
Armin Ronacher7ceced52008-05-03 10:15:31 +0200278 getattr(obj, 'alters_data', False))
Armin Ronacherc63243e2008-04-14 22:53:58 +0200279
Armin Ronachera9195382010-11-29 13:21:57 +0100280 def call_binop(self, context, operator, left, right):
281 """For intercepted binary operator calls (:meth:`intercepted_binops`)
282 this function is executed instead of the builtin operator. This can
283 be used to fine tune the behavior of certain operators.
284
285 .. versionadded:: 2.6
286 """
287 return self.binop_table[operator](left, right)
288
289 def call_unop(self, context, operator, arg):
290 """For intercepted unary operator calls (:meth:`intercepted_unops`)
291 this function is executed instead of the builtin operator. This can
292 be used to fine tune the behavior of certain operators.
293
294 .. versionadded:: 2.6
295 """
296 return self.unop_table[operator](arg)
297
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200298 def getitem(self, obj, argument):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200299 """Subscribe an object from sandboxed code."""
Armin Ronacher9a822052008-04-17 18:44:07 +0200300 try:
301 return obj[argument]
Armin Ronacherc63243e2008-04-14 22:53:58 +0200302 except (TypeError, LookupError):
Thomas Waldmann7d295622013-05-18 00:06:22 +0200303 if isinstance(argument, six.string_types):
Armin Ronacherf15f5f72008-05-26 12:21:45 +0200304 try:
305 attr = str(argument)
Ian Lewisab014bd2010-10-31 20:29:28 +0900306 except Exception:
Armin Ronacherf15f5f72008-05-26 12:21:45 +0200307 pass
308 else:
309 try:
310 value = getattr(obj, attr)
311 except AttributeError:
312 pass
313 else:
314 if self.is_safe_attribute(obj, argument, value):
315 return value
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200316 return self.unsafe_undefined(obj, argument)
Armin Ronacher9a822052008-04-17 18:44:07 +0200317 return self.undefined(obj=obj, name=argument)
Armin Ronacherc63243e2008-04-14 22:53:58 +0200318
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200319 def getattr(self, obj, attribute):
320 """Subscribe an object from sandboxed code and prefer the
321 attribute. The attribute passed *must* be a bytestring.
322 """
323 try:
324 value = getattr(obj, attribute)
325 except AttributeError:
326 try:
Armin Ronacher9efe0812008-11-02 12:22:00 +0100327 return obj[attribute]
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200328 except (TypeError, LookupError):
329 pass
330 else:
331 if self.is_safe_attribute(obj, attribute, value):
332 return value
333 return self.unsafe_undefined(obj, attribute)
Armin Ronacher9efe0812008-11-02 12:22:00 +0100334 return self.undefined(obj=obj, name=attribute)
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200335
336 def unsafe_undefined(self, obj, attribute):
337 """Return an undefined object for unsafe attributes."""
338 return self.undefined('access to attribute %r of %r '
339 'object is unsafe.' % (
340 attribute,
341 obj.__class__.__name__
342 ), name=attribute, obj=obj, exc=SecurityError)
343
Armin Ronacherfd310492008-05-25 00:16:51 +0200344 def call(__self, __context, __obj, *args, **kwargs):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200345 """Call an object from sandboxed code."""
346 # the double prefixes are to avoid double keyword argument
347 # errors when proxying the call.
348 if not __self.is_safe_callable(__obj):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200349 raise SecurityError('%r is not safely callable' % (__obj,))
Armin Ronacherfd310492008-05-25 00:16:51 +0200350 return __context.call(__obj, *args, **kwargs)
Armin Ronacher522cad62008-05-17 13:55:37 +0200351
352
353class ImmutableSandboxedEnvironment(SandboxedEnvironment):
354 """Works exactly like the regular `SandboxedEnvironment` but does not
355 permit modifications on the builtin mutable objects `list`, `set`, and
Armin Ronacherd71fff02008-05-26 23:57:07 +0200356 `dict` by using the :func:`modifies_known_mutable` function.
Armin Ronacher522cad62008-05-17 13:55:37 +0200357 """
358
359 def is_safe_attribute(self, obj, attr, value):
360 if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
361 return False
Armin Ronacherd71fff02008-05-26 23:57:07 +0200362 return not modifies_known_mutable(obj, attr)