blob: 749719548af8c69f896cf43da807f354020411ee [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
Armin Ronacherc63243e2008-04-14 22:53:58 +020016from jinja2.runtime import Undefined
17from 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 """
Armin Ronacherc63243e2008-04-14 22:53:58 +020094 rng = xrange(*args)
95 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 Ronacher5cdc1ac2008-05-07 12:17:18 +0200102 """
103 Mark a function or method as unsafe::
104
105 @unsafe
106 def delete(self):
107 pass
108 """
Armin Ronacherc63243e2008-04-14 22:53:58 +0200109 f.unsafe_callable = True
110 return f
111
112
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200113def is_internal_attribute(obj, attr):
114 """Test if the attribute given is an internal python attribute. For
115 example this function returns `True` for the `func_code` attribute of
116 python objects. This is useful if the environment method
117 :meth:`~SandboxedEnvironment.is_safe_attribute` is overriden.
118
119 >>> from jinja2.sandbox import is_internal_attribute
120 >>> is_internal_attribute(lambda: None, "func_code")
121 True
122 >>> is_internal_attribute((lambda x:x).func_code, 'co_code')
123 True
124 >>> is_internal_attribute(str, "upper")
125 False
126 """
127 if isinstance(obj, FunctionType):
Armin Ronacherd71fff02008-05-26 23:57:07 +0200128 if attr in UNSAFE_FUNCTION_ATTRIBUTES:
129 return True
130 elif isinstance(obj, MethodType):
131 if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
132 attr in UNSAFE_METHOD_ATTRIBUTES:
133 return True
134 elif isinstance(obj, type):
135 if attr == 'mro':
136 return True
137 elif isinstance(obj, (CodeType, TracebackType, FrameType)):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200138 return True
Armin Ronacherd71fff02008-05-26 23:57:07 +0200139 elif isinstance(obj, GeneratorType):
140 if attr == 'gi_frame':
141 return True
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200142 return attr.startswith('__')
143
144
Armin Ronacherd71fff02008-05-26 23:57:07 +0200145def modifies_known_mutable(obj, attr):
Armin Ronacher522cad62008-05-17 13:55:37 +0200146 """This function checks if an attribute on a builtin mutable object
Armin Ronacherd71fff02008-05-26 23:57:07 +0200147 (list, dict, set or deque) would modify it if called. It also supports
148 the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
149 with Python 2.6 onwards the abstract base classes `MutableSet`,
150 `MutableMapping`, and `MutableSequence`.
Armin Ronacher522cad62008-05-17 13:55:37 +0200151
Armin Ronacherd71fff02008-05-26 23:57:07 +0200152 >>> modifies_known_mutable({}, "clear")
Armin Ronacher522cad62008-05-17 13:55:37 +0200153 True
Armin Ronacherd71fff02008-05-26 23:57:07 +0200154 >>> modifies_known_mutable({}, "keys")
Armin Ronacher522cad62008-05-17 13:55:37 +0200155 False
Armin Ronacherd71fff02008-05-26 23:57:07 +0200156 >>> modifies_known_mutable([], "append")
Armin Ronacher522cad62008-05-17 13:55:37 +0200157 True
Armin Ronacherd71fff02008-05-26 23:57:07 +0200158 >>> modifies_known_mutable([], "index")
Armin Ronacher522cad62008-05-17 13:55:37 +0200159 False
160
161 If called with an unsupported object (such as unicode) `False` is
162 returned.
163
Armin Ronacherd71fff02008-05-26 23:57:07 +0200164 >>> modifies_known_mutable("foo", "upper")
Armin Ronacher522cad62008-05-17 13:55:37 +0200165 False
166 """
Armin Ronacherd71fff02008-05-26 23:57:07 +0200167 for typespec, unsafe in _mutable_spec:
168 if isinstance(obj, typespec):
169 return attr in unsafe
Armin Ronacher522cad62008-05-17 13:55:37 +0200170 return False
171
172
Armin Ronacherc63243e2008-04-14 22:53:58 +0200173class SandboxedEnvironment(Environment):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200174 """The sandboxed environment. It works like the regular environment but
175 tells the compiler to generate sandboxed code. Additionally subclasses of
176 this environment may override the methods that tell the runtime what
177 attributes or functions are safe to access.
178
179 If the template tries to access insecure code a :exc:`SecurityError` is
180 raised. However also other exceptions may occour during the rendering so
181 the caller has to ensure that all exceptions are catched.
182 """
Armin Ronacherc63243e2008-04-14 22:53:58 +0200183 sandboxed = True
184
185 def __init__(self, *args, **kwargs):
186 Environment.__init__(self, *args, **kwargs)
187 self.globals['range'] = safe_range
188
Armin Ronacher9a822052008-04-17 18:44:07 +0200189 def is_safe_attribute(self, obj, attr, value):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200190 """The sandboxed environment will call this method to check if the
191 attribute of an object is safe to access. Per default all attributes
192 starting with an underscore are considered private as well as the
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200193 special attributes of internal python objects as returned by the
194 :func:`is_internal_attribute` function.
Armin Ronacherc63243e2008-04-14 22:53:58 +0200195 """
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200196 return not (attr.startswith('_') or is_internal_attribute(obj, attr))
Armin Ronacherc63243e2008-04-14 22:53:58 +0200197
198 def is_safe_callable(self, obj):
199 """Check if an object is safely callable. Per default a function is
200 considered safe unless the `unsafe_callable` attribute exists and is
Armin Ronacher7ceced52008-05-03 10:15:31 +0200201 True. Override this method to alter the behavior, but this won't
Armin Ronacherc63243e2008-04-14 22:53:58 +0200202 affect the `unsafe` decorator from this module.
203 """
Armin Ronacher7ceced52008-05-03 10:15:31 +0200204 return not (getattr(obj, 'unsafe_callable', False) or \
205 getattr(obj, 'alters_data', False))
Armin Ronacherc63243e2008-04-14 22:53:58 +0200206
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200207 def getitem(self, obj, argument):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200208 """Subscribe an object from sandboxed code."""
Armin Ronacher9a822052008-04-17 18:44:07 +0200209 try:
210 return obj[argument]
Armin Ronacherc63243e2008-04-14 22:53:58 +0200211 except (TypeError, LookupError):
Armin Ronacherf15f5f72008-05-26 12:21:45 +0200212 if isinstance(argument, basestring):
213 try:
214 attr = str(argument)
215 except:
216 pass
217 else:
218 try:
219 value = getattr(obj, attr)
220 except AttributeError:
221 pass
222 else:
223 if self.is_safe_attribute(obj, argument, value):
224 return value
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200225 return self.unsafe_undefined(obj, argument)
Armin Ronacher9a822052008-04-17 18:44:07 +0200226 return self.undefined(obj=obj, name=argument)
Armin Ronacherc63243e2008-04-14 22:53:58 +0200227
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200228 def getattr(self, obj, attribute):
229 """Subscribe an object from sandboxed code and prefer the
230 attribute. The attribute passed *must* be a bytestring.
231 """
232 try:
233 value = getattr(obj, attribute)
234 except AttributeError:
235 try:
Armin Ronacher9efe0812008-11-02 12:22:00 +0100236 return obj[attribute]
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200237 except (TypeError, LookupError):
238 pass
239 else:
240 if self.is_safe_attribute(obj, attribute, value):
241 return value
242 return self.unsafe_undefined(obj, attribute)
Armin Ronacher9efe0812008-11-02 12:22:00 +0100243 return self.undefined(obj=obj, name=attribute)
Armin Ronacher6dc6f292008-06-12 08:50:07 +0200244
245 def unsafe_undefined(self, obj, attribute):
246 """Return an undefined object for unsafe attributes."""
247 return self.undefined('access to attribute %r of %r '
248 'object is unsafe.' % (
249 attribute,
250 obj.__class__.__name__
251 ), name=attribute, obj=obj, exc=SecurityError)
252
Armin Ronacherfd310492008-05-25 00:16:51 +0200253 def call(__self, __context, __obj, *args, **kwargs):
Armin Ronacherc63243e2008-04-14 22:53:58 +0200254 """Call an object from sandboxed code."""
255 # the double prefixes are to avoid double keyword argument
256 # errors when proxying the call.
257 if not __self.is_safe_callable(__obj):
Armin Ronacher5cdc1ac2008-05-07 12:17:18 +0200258 raise SecurityError('%r is not safely callable' % (__obj,))
Armin Ronacherfd310492008-05-25 00:16:51 +0200259 return __context.call(__obj, *args, **kwargs)
Armin Ronacher522cad62008-05-17 13:55:37 +0200260
261
262class ImmutableSandboxedEnvironment(SandboxedEnvironment):
263 """Works exactly like the regular `SandboxedEnvironment` but does not
264 permit modifications on the builtin mutable objects `list`, `set`, and
Armin Ronacherd71fff02008-05-26 23:57:07 +0200265 `dict` by using the :func:`modifies_known_mutable` function.
Armin Ronacher522cad62008-05-17 13:55:37 +0200266 """
267
268 def is_safe_attribute(self, obj, attr, value):
269 if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
270 return False
Armin Ronacherd71fff02008-05-26 23:57:07 +0200271 return not modifies_known_mutable(obj, attr)