lower_mode_b: handle BadB
[libfirm] / scripts / 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) 2010 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 from collections import deque
41
42 _mutable_set_types = (set,)
43 _mutable_mapping_types = (dict,)
44 _mutable_sequence_types = (list,)
45
46
47 # on python 2.x we can register the user collection types
48 try:
49     from UserDict import UserDict, DictMixin
50     from UserList import UserList
51     _mutable_mapping_types += (UserDict, DictMixin)
52     _mutable_set_types += (UserList,)
53 except ImportError:
54     pass
55
56 # if sets is still available, register the mutable set from there as well
57 try:
58     from sets import Set
59     _mutable_set_types += (Set,)
60 except ImportError:
61     pass
62
63 #: register Python 2.6 abstract base classes
64 try:
65     from collections import MutableSet, MutableMapping, MutableSequence
66     _mutable_set_types += (MutableSet,)
67     _mutable_mapping_types += (MutableMapping,)
68     _mutable_sequence_types += (MutableSequence,)
69 except 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 )
88
89
90 def safe_range(*args):
91     """A range that can't generate ranges with a length of more than
92     MAX_RANGE items.
93     """
94     rng = xrange(*args)
95     if len(rng) > MAX_RANGE:
96         raise OverflowError('range too big, maximum size for range is %d' %
97                             MAX_RANGE)
98     return rng
99
100
101 def unsafe(f):
102     """
103     Mark a function or method as unsafe::
104
105         @unsafe
106         def delete(self):
107             pass
108     """
109     f.unsafe_callable = True
110     return f
111
112
113 def 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):
128         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)):
138         return True
139     elif isinstance(obj, GeneratorType):
140         if attr == 'gi_frame':
141             return True
142     return attr.startswith('__')
143
144
145 def modifies_known_mutable(obj, attr):
146     """This function checks if an attribute on a builtin mutable object
147     (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`.
151
152     >>> modifies_known_mutable({}, "clear")
153     True
154     >>> modifies_known_mutable({}, "keys")
155     False
156     >>> modifies_known_mutable([], "append")
157     True
158     >>> modifies_known_mutable([], "index")
159     False
160
161     If called with an unsupported object (such as unicode) `False` is
162     returned.
163
164     >>> modifies_known_mutable("foo", "upper")
165     False
166     """
167     for typespec, unsafe in _mutable_spec:
168         if isinstance(obj, typespec):
169             return attr in unsafe
170     return False
171
172
173 class SandboxedEnvironment(Environment):
174     """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     """
183     sandboxed = True
184
185     def __init__(self, *args, **kwargs):
186         Environment.__init__(self, *args, **kwargs)
187         self.globals['range'] = safe_range
188
189     def is_safe_attribute(self, obj, attr, value):
190         """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
193         special attributes of internal python objects as returned by the
194         :func:`is_internal_attribute` function.
195         """
196         return not (attr.startswith('_') or is_internal_attribute(obj, attr))
197
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
201         True.  Override this method to alter the behavior, but this won't
202         affect the `unsafe` decorator from this module.
203         """
204         return not (getattr(obj, 'unsafe_callable', False) or \
205                     getattr(obj, 'alters_data', False))
206
207     def getitem(self, obj, argument):
208         """Subscribe an object from sandboxed code."""
209         try:
210             return obj[argument]
211         except (TypeError, LookupError):
212             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
225                         return self.unsafe_undefined(obj, argument)
226         return self.undefined(obj=obj, name=argument)
227
228     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:
236                 return obj[attribute]
237             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)
243         return self.undefined(obj=obj, name=attribute)
244
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
253     def call(__self, __context, __obj, *args, **kwargs):
254         """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):
258             raise SecurityError('%r is not safely callable' % (__obj,))
259         return __context.call(__obj, *args, **kwargs)
260
261
262 class ImmutableSandboxedEnvironment(SandboxedEnvironment):
263     """Works exactly like the regular `SandboxedEnvironment` but does not
264     permit modifications on the builtin mutable objects `list`, `set`, and
265     `dict` by using the :func:`modifies_known_mutable` function.
266     """
267
268     def is_safe_attribute(self, obj, attr, value):
269         if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
270             return False
271         return not modifies_known_mutable(obj, attr)