blob: 20de3695b7faef5a60381c03b2594defa6a7cc62 [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
12 :copyright: Copyright 2008 by Armin Ronacher.
13 :license: BSD.
14"""
Armin Ronacher522cad62008-05-17 13:55:37 +020015import operator
Armin Ronacherb5f522c2008-05-04 18:25:02 +020016from types import FunctionType, MethodType, TracebackType, CodeType, \
17 FrameType, GeneratorType
Armin Ronacherc63243e2008-04-14 22:53:58 +020018from jinja2.runtime import Undefined
19from jinja2.environment import Environment
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +020020from jinja2.exceptions import SecurityError
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 Ronacherd71fff02008-05-26 23:57:07 +020034from collections import deque
35from sets import Set, ImmutableSet
36from UserDict import UserDict, DictMixin
37from UserList import UserList
38_mutable_set_types = (ImmutableSet, Set, set)
39_mutable_mapping_types = (UserDict, DictMixin, dict)
40_mutable_sequence_types = (UserList, list)
Armin Ronacher522cad62008-05-17 13:55:37 +020041
Armin Ronacherd71fff02008-05-26 23:57:07 +020042#: register Python 2.6 abstract base classes
43try:
44 from collections import MutableSet, MutableMapping, MutableSequence
45 _mutable_set_types += (MutableSet,)
46 _mutable_mapping_types += (MutableMapping,)
47 _mutable_sequence_types += (MutableSequence,)
48except ImportError:
49 pass
50
51_mutable_spec = (
52 (_mutable_set_types, frozenset([
53 'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
54 'symmetric_difference_update', 'update'
55 ])),
56 (_mutable_mapping_types, frozenset([
57 'clear', 'pop', 'popitem', 'setdefault', 'update'
58 ])),
59 (_mutable_sequence_types, frozenset([
60 'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
61 ])),
62 (deque, frozenset([
63 'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
64 'popleft', 'remove', 'rotate'
65 ]))
66)
Armin Ronacher522cad62008-05-17 13:55:37 +020067
Armin Ronacherc63243e2008-04-14 22:53:58 +020068
69def safe_range(*args):
70 """A range that can't generate ranges with a length of more than
Armin Ronacher7ceced52008-05-03 10:15:31 +020071 MAX_RANGE items.
72 """
Armin Ronacherc63243e2008-04-14 22:53:58 +020073 rng = xrange(*args)
74 if len(rng) > MAX_RANGE:
Armin Ronacher76c280b2008-05-04 12:31:48 +020075 raise OverflowError('range too big, maximum size for range is %d' %
76 MAX_RANGE)
Armin Ronacherc63243e2008-04-14 22:53:58 +020077 return rng
78
79
80def unsafe(f):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +020081 """
82 Mark a function or method as unsafe::
83
84 @unsafe
85 def delete(self):
86 pass
87 """
Armin Ronacherc63243e2008-04-14 22:53:58 +020088 f.unsafe_callable = True
89 return f
90
91
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +020092def is_internal_attribute(obj, attr):
93 """Test if the attribute given is an internal python attribute. For
94 example this function returns `True` for the `func_code` attribute of
95 python objects. This is useful if the environment method
96 :meth:`~SandboxedEnvironment.is_safe_attribute` is overriden.
97
98 >>> from jinja2.sandbox import is_internal_attribute
99 >>> is_internal_attribute(lambda: None, "func_code")
100 True
101 >>> is_internal_attribute((lambda x:x).func_code, 'co_code')
102 True
103 >>> is_internal_attribute(str, "upper")
104 False
105 """
106 if isinstance(obj, FunctionType):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200107 if attr in UNSAFE_FUNCTION_ATTRIBUTES:
108 return True
109 elif isinstance(obj, MethodType):
110 if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
111 attr in UNSAFE_METHOD_ATTRIBUTES:
112 return True
113 elif isinstance(obj, type):
114 if attr == 'mro':
115 return True
116 elif isinstance(obj, (CodeType, TracebackType, FrameType)):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200117 return True
Armin Ronacherd71fff02008-05-26 23:57:07 +0200118 elif isinstance(obj, GeneratorType):
119 if attr == 'gi_frame':
120 return True
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200121 return attr.startswith('__')
122
123
Armin Ronacherd71fff02008-05-26 23:57:07 +0200124def modifies_known_mutable(obj, attr):
Armin Ronacher522cad62008-05-17 13:55:37 +0200125 """This function checks if an attribute on a builtin mutable object
Armin Ronacherd71fff02008-05-26 23:57:07 +0200126 (list, dict, set or deque) would modify it if called. It also supports
127 the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
128 with Python 2.6 onwards the abstract base classes `MutableSet`,
129 `MutableMapping`, and `MutableSequence`.
Armin Ronacher522cad62008-05-17 13:55:37 +0200130
Armin Ronacherd71fff02008-05-26 23:57:07 +0200131 >>> modifies_known_mutable({}, "clear")
Armin Ronacher522cad62008-05-17 13:55:37 +0200132 True
Armin Ronacherd71fff02008-05-26 23:57:07 +0200133 >>> modifies_known_mutable({}, "keys")
Armin Ronacher522cad62008-05-17 13:55:37 +0200134 False
Armin Ronacherd71fff02008-05-26 23:57:07 +0200135 >>> modifies_known_mutable([], "append")
Armin Ronacher522cad62008-05-17 13:55:37 +0200136 True
Armin Ronacherd71fff02008-05-26 23:57:07 +0200137 >>> modifies_known_mutable([], "index")
Armin Ronacher522cad62008-05-17 13:55:37 +0200138 False
139
140 If called with an unsupported object (such as unicode) `False` is
141 returned.
142
Armin Ronacherd71fff02008-05-26 23:57:07 +0200143 >>> modifies_known_mutable("foo", "upper")
Armin Ronacher522cad62008-05-17 13:55:37 +0200144 False
145 """
Armin Ronacherd71fff02008-05-26 23:57:07 +0200146 for typespec, unsafe in _mutable_spec:
147 if isinstance(obj, typespec):
148 return attr in unsafe
Armin Ronacher522cad62008-05-17 13:55:37 +0200149 return False
150
151
Armin Ronacherc63243e2008-04-14 22:53:58 +0200152class SandboxedEnvironment(Environment):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200153 """The sandboxed environment. It works like the regular environment but
154 tells the compiler to generate sandboxed code. Additionally subclasses of
155 this environment may override the methods that tell the runtime what
156 attributes or functions are safe to access.
157
158 If the template tries to access insecure code a :exc:`SecurityError` is
159 raised. However also other exceptions may occour during the rendering so
160 the caller has to ensure that all exceptions are catched.
161 """
Armin Ronacherc63243e2008-04-14 22:53:58 +0200162 sandboxed = True
163
164 def __init__(self, *args, **kwargs):
165 Environment.__init__(self, *args, **kwargs)
166 self.globals['range'] = safe_range
167
Armin Ronacher9a822052008-04-17 18:44:07 +0200168 def is_safe_attribute(self, obj, attr, value):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200169 """The sandboxed environment will call this method to check if the
170 attribute of an object is safe to access. Per default all attributes
171 starting with an underscore are considered private as well as the
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200172 special attributes of internal python objects as returned by the
173 :func:`is_internal_attribute` function.
Armin Ronacherc63243e2008-04-14 22:53:58 +0200174 """
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200175 return not (attr.startswith('_') or is_internal_attribute(obj, attr))
Armin Ronacherc63243e2008-04-14 22:53:58 +0200176
177 def is_safe_callable(self, obj):
178 """Check if an object is safely callable. Per default a function is
179 considered safe unless the `unsafe_callable` attribute exists and is
Armin Ronacher7ceced52008-05-03 10:15:31 +0200180 True. Override this method to alter the behavior, but this won't
Armin Ronacherc63243e2008-04-14 22:53:58 +0200181 affect the `unsafe` decorator from this module.
182 """
Armin Ronacher7ceced52008-05-03 10:15:31 +0200183 return not (getattr(obj, 'unsafe_callable', False) or \
184 getattr(obj, 'alters_data', False))
Armin Ronacherc63243e2008-04-14 22:53:58 +0200185
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200186 def getitem(self, obj, argument):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200187 """Subscribe an object from sandboxed code."""
Armin Ronacher9a822052008-04-17 18:44:07 +0200188 try:
189 return obj[argument]
Armin Ronacherc63243e2008-04-14 22:53:58 +0200190 except (TypeError, LookupError):
Armin Ronacherf15f5f72008-05-26 12:21:45 +0200191 if isinstance(argument, basestring):
192 try:
193 attr = str(argument)
194 except:
195 pass
196 else:
197 try:
198 value = getattr(obj, attr)
199 except AttributeError:
200 pass
201 else:
202 if self.is_safe_attribute(obj, argument, value):
203 return value
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200204 return self.unsafe_undefined(obj, argument)
Armin Ronacher9a822052008-04-17 18:44:07 +0200205 return self.undefined(obj=obj, name=argument)
Armin Ronacherc63243e2008-04-14 22:53:58 +0200206
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200207 def getattr(self, obj, attribute):
208 """Subscribe an object from sandboxed code and prefer the
209 attribute. The attribute passed *must* be a bytestring.
210 """
211 try:
212 value = getattr(obj, attribute)
213 except AttributeError:
214 try:
215 return obj[argument]
216 except (TypeError, LookupError):
217 pass
218 else:
219 if self.is_safe_attribute(obj, attribute, value):
220 return value
221 return self.unsafe_undefined(obj, attribute)
222 return self.undefined(obj=obj, name=argument)
223
224 def unsafe_undefined(self, obj, attribute):
225 """Return an undefined object for unsafe attributes."""
226 return self.undefined('access to attribute %r of %r '
227 'object is unsafe.' % (
228 attribute,
229 obj.__class__.__name__
230 ), name=attribute, obj=obj, exc=SecurityError)
231
Armin Ronacherfd310492008-05-25 00:16:51 +0200232 def call(__self, __context, __obj, *args, **kwargs):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200233 """Call an object from sandboxed code."""
234 # the double prefixes are to avoid double keyword argument
235 # errors when proxying the call.
236 if not __self.is_safe_callable(__obj):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200237 raise SecurityError('%r is not safely callable' % (__obj,))
Armin Ronacherfd310492008-05-25 00:16:51 +0200238 return __context.call(__obj, *args, **kwargs)
Armin Ronacher522cad62008-05-17 13:55:37 +0200239
240
241class ImmutableSandboxedEnvironment(SandboxedEnvironment):
242 """Works exactly like the regular `SandboxedEnvironment` but does not
243 permit modifications on the builtin mutable objects `list`, `set`, and
Armin Ronacherd71fff02008-05-26 23:57:07 +0200244 `dict` by using the :func:`modifies_known_mutable` function.
Armin Ronacher522cad62008-05-17 13:55:37 +0200245 """
246
247 def is_safe_attribute(self, obj, attr, value):
248 if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
249 return False
Armin Ronacherd71fff02008-05-26 23:57:07 +0200250 return not modifies_known_mutable(obj, attr)