Broken overlong line.
[jinja2.git] / jinja2 / sandbox.py
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) 2009 by the Jinja Team.
13     :license: BSD.
14 """
15 import operator
16 from jinja2.runtime import Undefined
17 from jinja2.environment import Environment
18 from jinja2.exceptions import SecurityError
19 from jinja2.utils import FunctionType, MethodType, TracebackType, CodeType, \
20      FrameType, GeneratorType
21
22
23 #: maximum number of items a range may produce
24 MAX_RANGE = 100000
25
26 #: attributes of function objects that are considered unsafe.
27 UNSAFE_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
31 UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
32
33
34 import warnings
35
36 # make sure we don't warn in python 2.6 about stuff we don't care about
37 warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning,
38                         module='jinja2.sandbox')
39
40
41 from collections import deque
42 from UserDict import UserDict, DictMixin
43 from UserList import UserList
44 _mutable_set_types = (set,)
45 _mutable_mapping_types = (UserDict, DictMixin, dict)
46 _mutable_sequence_types = (UserList, list)
47
48 # if sets is still available, register the mutable set from there as well
49 try:
50     from sets import Set
51     _mutable_set_types += (Set,)
52 except ImportError:
53     pass
54
55 #: register Python 2.6 abstract base classes
56 try:
57     from collections import MutableSet, MutableMapping, MutableSequence
58     _mutable_set_types += (MutableSet,)
59     _mutable_mapping_types += (MutableMapping,)
60     _mutable_sequence_types += (MutableSequence,)
61 except ImportError:
62     pass
63
64 _mutable_spec = (
65     (_mutable_set_types, frozenset([
66         'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
67         'symmetric_difference_update', 'update'
68     ])),
69     (_mutable_mapping_types, frozenset([
70         'clear', 'pop', 'popitem', 'setdefault', 'update'
71     ])),
72     (_mutable_sequence_types, frozenset([
73         'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
74     ])),
75     (deque, frozenset([
76         'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
77         'popleft', 'remove', 'rotate'
78     ]))
79 )
80
81
82 def safe_range(*args):
83     """A range that can't generate ranges with a length of more than
84     MAX_RANGE items.
85     """
86     rng = xrange(*args)
87     if len(rng) > MAX_RANGE:
88         raise OverflowError('range too big, maximum size for range is %d' %
89                             MAX_RANGE)
90     return rng
91
92
93 def unsafe(f):
94     """
95     Mark a function or method as unsafe::
96
97         @unsafe
98         def delete(self):
99             pass
100     """
101     f.unsafe_callable = True
102     return f
103
104
105 def is_internal_attribute(obj, attr):
106     """Test if the attribute given is an internal python attribute.  For
107     example this function returns `True` for the `func_code` attribute of
108     python objects.  This is useful if the environment method
109     :meth:`~SandboxedEnvironment.is_safe_attribute` is overriden.
110
111     >>> from jinja2.sandbox import is_internal_attribute
112     >>> is_internal_attribute(lambda: None, "func_code")
113     True
114     >>> is_internal_attribute((lambda x:x).func_code, 'co_code')
115     True
116     >>> is_internal_attribute(str, "upper")
117     False
118     """
119     if isinstance(obj, FunctionType):
120         if attr in UNSAFE_FUNCTION_ATTRIBUTES:
121             return True
122     elif isinstance(obj, MethodType):
123         if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
124            attr in UNSAFE_METHOD_ATTRIBUTES:
125             return True
126     elif isinstance(obj, type):
127         if attr == 'mro':
128             return True
129     elif isinstance(obj, (CodeType, TracebackType, FrameType)):
130         return True
131     elif isinstance(obj, GeneratorType):
132         if attr == 'gi_frame':
133             return True
134     return attr.startswith('__')
135
136
137 def modifies_known_mutable(obj, attr):
138     """This function checks if an attribute on a builtin mutable object
139     (list, dict, set or deque) would modify it if called.  It also supports
140     the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
141     with Python 2.6 onwards the abstract base classes `MutableSet`,
142     `MutableMapping`, and `MutableSequence`.
143
144     >>> modifies_known_mutable({}, "clear")
145     True
146     >>> modifies_known_mutable({}, "keys")
147     False
148     >>> modifies_known_mutable([], "append")
149     True
150     >>> modifies_known_mutable([], "index")
151     False
152
153     If called with an unsupported object (such as unicode) `False` is
154     returned.
155
156     >>> modifies_known_mutable("foo", "upper")
157     False
158     """
159     for typespec, unsafe in _mutable_spec:
160         if isinstance(obj, typespec):
161             return attr in unsafe
162     return False
163
164
165 class SandboxedEnvironment(Environment):
166     """The sandboxed environment.  It works like the regular environment but
167     tells the compiler to generate sandboxed code.  Additionally subclasses of
168     this environment may override the methods that tell the runtime what
169     attributes or functions are safe to access.
170
171     If the template tries to access insecure code a :exc:`SecurityError` is
172     raised.  However also other exceptions may occour during the rendering so
173     the caller has to ensure that all exceptions are catched.
174     """
175     sandboxed = True
176
177     def __init__(self, *args, **kwargs):
178         Environment.__init__(self, *args, **kwargs)
179         self.globals['range'] = safe_range
180
181     def is_safe_attribute(self, obj, attr, value):
182         """The sandboxed environment will call this method to check if the
183         attribute of an object is safe to access.  Per default all attributes
184         starting with an underscore are considered private as well as the
185         special attributes of internal python objects as returned by the
186         :func:`is_internal_attribute` function.
187         """
188         return not (attr.startswith('_') or is_internal_attribute(obj, attr))
189
190     def is_safe_callable(self, obj):
191         """Check if an object is safely callable.  Per default a function is
192         considered safe unless the `unsafe_callable` attribute exists and is
193         True.  Override this method to alter the behavior, but this won't
194         affect the `unsafe` decorator from this module.
195         """
196         return not (getattr(obj, 'unsafe_callable', False) or \
197                     getattr(obj, 'alters_data', False))
198
199     def getitem(self, obj, argument):
200         """Subscribe an object from sandboxed code."""
201         try:
202             return obj[argument]
203         except (TypeError, LookupError):
204             if isinstance(argument, basestring):
205                 try:
206                     attr = str(argument)
207                 except:
208                     pass
209                 else:
210                     try:
211                         value = getattr(obj, attr)
212                     except AttributeError:
213                         pass
214                     else:
215                         if self.is_safe_attribute(obj, argument, value):
216                             return value
217                         return self.unsafe_undefined(obj, argument)
218         return self.undefined(obj=obj, name=argument)
219
220     def getattr(self, obj, attribute):
221         """Subscribe an object from sandboxed code and prefer the
222         attribute.  The attribute passed *must* be a bytestring.
223         """
224         try:
225             value = getattr(obj, attribute)
226         except AttributeError:
227             try:
228                 return obj[attribute]
229             except (TypeError, LookupError):
230                 pass
231         else:
232             if self.is_safe_attribute(obj, attribute, value):
233                 return value
234             return self.unsafe_undefined(obj, attribute)
235         return self.undefined(obj=obj, name=attribute)
236
237     def unsafe_undefined(self, obj, attribute):
238         """Return an undefined object for unsafe attributes."""
239         return self.undefined('access to attribute %r of %r '
240                               'object is unsafe.' % (
241             attribute,
242             obj.__class__.__name__
243         ), name=attribute, obj=obj, exc=SecurityError)
244
245     def call(__self, __context, __obj, *args, **kwargs):
246         """Call an object from sandboxed code."""
247         # the double prefixes are to avoid double keyword argument
248         # errors when proxying the call.
249         if not __self.is_safe_callable(__obj):
250             raise SecurityError('%r is not safely callable' % (__obj,))
251         return __context.call(__obj, *args, **kwargs)
252
253
254 class ImmutableSandboxedEnvironment(SandboxedEnvironment):
255     """Works exactly like the regular `SandboxedEnvironment` but does not
256     permit modifications on the builtin mutable objects `list`, `set`, and
257     `dict` by using the :func:`modifies_known_mutable` function.
258     """
259
260     def is_safe_attribute(self, obj, attr, value):
261         if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
262             return False
263         return not modifies_known_mutable(obj, attr)