~brad-marshall/charms/trusty/apache2-wsgi/fix-haproxy-relations

« back to all changes in this revision

Viewing changes to hooks/lib/jinja2/sandbox.py

  • Committer: Robin Winslow
  • Date: 2014-05-27 14:00:44 UTC
  • Revision ID: robin.winslow@canonical.com-20140527140044-8rpmb3wx4djzwa83
Add all files

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.environment import Environment
 
17
from jinja2.exceptions import SecurityError
 
18
from jinja2._compat import string_types, function_type, method_type, \
 
19
     traceback_type, code_type, frame_type, generator_type, PY2
 
20
 
 
21
 
 
22
#: maximum number of items a range may produce
 
23
MAX_RANGE = 100000
 
24
 
 
25
#: attributes of function objects that are considered unsafe.
 
26
UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
 
27
                                  'func_defaults', 'func_globals'])
 
28
 
 
29
#: unsafe method attributes.  function attributes are unsafe for methods too
 
30
UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
 
31
 
 
32
#: unsafe generator attirbutes.
 
33
UNSAFE_GENERATOR_ATTRIBUTES = set(['gi_frame', 'gi_code'])
 
34
 
 
35
# On versions > python 2 the special attributes on functions are gone,
 
36
# but they remain on methods and generators for whatever reason.
 
37
if not PY2:
 
38
    UNSAFE_FUNCTION_ATTRIBUTES = set()
 
39
 
 
40
import warnings
 
41
 
 
42
# make sure we don't warn in python 2.6 about stuff we don't care about
 
43
warnings.filterwarnings('ignore', 'the sets module', DeprecationWarning,
 
44
                        module='jinja2.sandbox')
 
45
 
 
46
from collections import deque
 
47
 
 
48
_mutable_set_types = (set,)
 
49
_mutable_mapping_types = (dict,)
 
50
_mutable_sequence_types = (list,)
 
51
 
 
52
 
 
53
# on python 2.x we can register the user collection types
 
54
try:
 
55
    from UserDict import UserDict, DictMixin
 
56
    from UserList import UserList
 
57
    _mutable_mapping_types += (UserDict, DictMixin)
 
58
    _mutable_set_types += (UserList,)
 
59
except ImportError:
 
60
    pass
 
61
 
 
62
# if sets is still available, register the mutable set from there as well
 
63
try:
 
64
    from sets import Set
 
65
    _mutable_set_types += (Set,)
 
66
except ImportError:
 
67
    pass
 
68
 
 
69
#: register Python 2.6 abstract base classes
 
70
try:
 
71
    from collections import MutableSet, MutableMapping, MutableSequence
 
72
    _mutable_set_types += (MutableSet,)
 
73
    _mutable_mapping_types += (MutableMapping,)
 
74
    _mutable_sequence_types += (MutableSequence,)
 
75
except ImportError:
 
76
    pass
 
77
 
 
78
_mutable_spec = (
 
79
    (_mutable_set_types, frozenset([
 
80
        'add', 'clear', 'difference_update', 'discard', 'pop', 'remove',
 
81
        'symmetric_difference_update', 'update'
 
82
    ])),
 
83
    (_mutable_mapping_types, frozenset([
 
84
        'clear', 'pop', 'popitem', 'setdefault', 'update'
 
85
    ])),
 
86
    (_mutable_sequence_types, frozenset([
 
87
        'append', 'reverse', 'insert', 'sort', 'extend', 'remove'
 
88
    ])),
 
89
    (deque, frozenset([
 
90
        'append', 'appendleft', 'clear', 'extend', 'extendleft', 'pop',
 
91
        'popleft', 'remove', 'rotate'
 
92
    ]))
 
93
)
 
94
 
 
95
 
 
96
def safe_range(*args):
 
97
    """A range that can't generate ranges with a length of more than
 
98
    MAX_RANGE items.
 
99
    """
 
100
    rng = range(*args)
 
101
    if len(rng) > MAX_RANGE:
 
102
        raise OverflowError('range too big, maximum size for range is %d' %
 
103
                            MAX_RANGE)
 
104
    return rng
 
105
 
 
106
 
 
107
def unsafe(f):
 
108
    """Marks a function or method as unsafe.
 
109
 
 
110
    ::
 
111
 
 
112
        @unsafe
 
113
        def delete(self):
 
114
            pass
 
115
    """
 
116
    f.unsafe_callable = True
 
117
    return f
 
118
 
 
119
 
 
120
def is_internal_attribute(obj, attr):
 
121
    """Test if the attribute given is an internal python attribute.  For
 
122
    example this function returns `True` for the `func_code` attribute of
 
123
    python objects.  This is useful if the environment method
 
124
    :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden.
 
125
 
 
126
    >>> from jinja2.sandbox import is_internal_attribute
 
127
    >>> is_internal_attribute(lambda: None, "func_code")
 
128
    True
 
129
    >>> is_internal_attribute((lambda x:x).func_code, 'co_code')
 
130
    True
 
131
    >>> is_internal_attribute(str, "upper")
 
132
    False
 
133
    """
 
134
    if isinstance(obj, function_type):
 
135
        if attr in UNSAFE_FUNCTION_ATTRIBUTES:
 
136
            return True
 
137
    elif isinstance(obj, method_type):
 
138
        if attr in UNSAFE_FUNCTION_ATTRIBUTES or \
 
139
           attr in UNSAFE_METHOD_ATTRIBUTES:
 
140
            return True
 
141
    elif isinstance(obj, type):
 
142
        if attr == 'mro':
 
143
            return True
 
144
    elif isinstance(obj, (code_type, traceback_type, frame_type)):
 
145
        return True
 
146
    elif isinstance(obj, generator_type):
 
147
        if attr in UNSAFE_GENERATOR_ATTRIBUTES:
 
148
            return True
 
149
    return attr.startswith('__')
 
150
 
 
151
 
 
152
def modifies_known_mutable(obj, attr):
 
153
    """This function checks if an attribute on a builtin mutable object
 
154
    (list, dict, set or deque) would modify it if called.  It also supports
 
155
    the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
 
156
    with Python 2.6 onwards the abstract base classes `MutableSet`,
 
157
    `MutableMapping`, and `MutableSequence`.
 
158
 
 
159
    >>> modifies_known_mutable({}, "clear")
 
160
    True
 
161
    >>> modifies_known_mutable({}, "keys")
 
162
    False
 
163
    >>> modifies_known_mutable([], "append")
 
164
    True
 
165
    >>> modifies_known_mutable([], "index")
 
166
    False
 
167
 
 
168
    If called with an unsupported object (such as unicode) `False` is
 
169
    returned.
 
170
 
 
171
    >>> modifies_known_mutable("foo", "upper")
 
172
    False
 
173
    """
 
174
    for typespec, unsafe in _mutable_spec:
 
175
        if isinstance(obj, typespec):
 
176
            return attr in unsafe
 
177
    return False
 
178
 
 
179
 
 
180
class SandboxedEnvironment(Environment):
 
181
    """The sandboxed environment.  It works like the regular environment but
 
182
    tells the compiler to generate sandboxed code.  Additionally subclasses of
 
183
    this environment may override the methods that tell the runtime what
 
184
    attributes or functions are safe to access.
 
185
 
 
186
    If the template tries to access insecure code a :exc:`SecurityError` is
 
187
    raised.  However also other exceptions may occour during the rendering so
 
188
    the caller has to ensure that all exceptions are catched.
 
189
    """
 
190
    sandboxed = True
 
191
 
 
192
    #: default callback table for the binary operators.  A copy of this is
 
193
    #: available on each instance of a sandboxed environment as
 
194
    #: :attr:`binop_table`
 
195
    default_binop_table = {
 
196
        '+':        operator.add,
 
197
        '-':        operator.sub,
 
198
        '*':        operator.mul,
 
199
        '/':        operator.truediv,
 
200
        '//':       operator.floordiv,
 
201
        '**':       operator.pow,
 
202
        '%':        operator.mod
 
203
    }
 
204
 
 
205
    #: default callback table for the unary operators.  A copy of this is
 
206
    #: available on each instance of a sandboxed environment as
 
207
    #: :attr:`unop_table`
 
208
    default_unop_table = {
 
209
        '+':        operator.pos,
 
210
        '-':        operator.neg
 
211
    }
 
212
 
 
213
    #: a set of binary operators that should be intercepted.  Each operator
 
214
    #: that is added to this set (empty by default) is delegated to the
 
215
    #: :meth:`call_binop` method that will perform the operator.  The default
 
216
    #: operator callback is specified by :attr:`binop_table`.
 
217
    #:
 
218
    #: The following binary operators are interceptable:
 
219
    #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**``
 
220
    #:
 
221
    #: The default operation form the operator table corresponds to the
 
222
    #: builtin function.  Intercepted calls are always slower than the native
 
223
    #: operator call, so make sure only to intercept the ones you are
 
224
    #: interested in.
 
225
    #:
 
226
    #: .. versionadded:: 2.6
 
227
    intercepted_binops = frozenset()
 
228
 
 
229
    #: a set of unary operators that should be intercepted.  Each operator
 
230
    #: that is added to this set (empty by default) is delegated to the
 
231
    #: :meth:`call_unop` method that will perform the operator.  The default
 
232
    #: operator callback is specified by :attr:`unop_table`.
 
233
    #:
 
234
    #: The following unary operators are interceptable: ``+``, ``-``
 
235
    #:
 
236
    #: The default operation form the operator table corresponds to the
 
237
    #: builtin function.  Intercepted calls are always slower than the native
 
238
    #: operator call, so make sure only to intercept the ones you are
 
239
    #: interested in.
 
240
    #:
 
241
    #: .. versionadded:: 2.6
 
242
    intercepted_unops = frozenset()
 
243
 
 
244
    def intercept_unop(self, operator):
 
245
        """Called during template compilation with the name of a unary
 
246
        operator to check if it should be intercepted at runtime.  If this
 
247
        method returns `True`, :meth:`call_unop` is excuted for this unary
 
248
        operator.  The default implementation of :meth:`call_unop` will use
 
249
        the :attr:`unop_table` dictionary to perform the operator with the
 
250
        same logic as the builtin one.
 
251
 
 
252
        The following unary operators are interceptable: ``+`` and ``-``
 
253
 
 
254
        Intercepted calls are always slower than the native operator call,
 
255
        so make sure only to intercept the ones you are interested in.
 
256
 
 
257
        .. versionadded:: 2.6
 
258
        """
 
259
        return False
 
260
 
 
261
 
 
262
    def __init__(self, *args, **kwargs):
 
263
        Environment.__init__(self, *args, **kwargs)
 
264
        self.globals['range'] = safe_range
 
265
        self.binop_table = self.default_binop_table.copy()
 
266
        self.unop_table = self.default_unop_table.copy()
 
267
 
 
268
    def is_safe_attribute(self, obj, attr, value):
 
269
        """The sandboxed environment will call this method to check if the
 
270
        attribute of an object is safe to access.  Per default all attributes
 
271
        starting with an underscore are considered private as well as the
 
272
        special attributes of internal python objects as returned by the
 
273
        :func:`is_internal_attribute` function.
 
274
        """
 
275
        return not (attr.startswith('_') or is_internal_attribute(obj, attr))
 
276
 
 
277
    def is_safe_callable(self, obj):
 
278
        """Check if an object is safely callable.  Per default a function is
 
279
        considered safe unless the `unsafe_callable` attribute exists and is
 
280
        True.  Override this method to alter the behavior, but this won't
 
281
        affect the `unsafe` decorator from this module.
 
282
        """
 
283
        return not (getattr(obj, 'unsafe_callable', False) or
 
284
                    getattr(obj, 'alters_data', False))
 
285
 
 
286
    def call_binop(self, context, operator, left, right):
 
287
        """For intercepted binary operator calls (:meth:`intercepted_binops`)
 
288
        this function is executed instead of the builtin operator.  This can
 
289
        be used to fine tune the behavior of certain operators.
 
290
 
 
291
        .. versionadded:: 2.6
 
292
        """
 
293
        return self.binop_table[operator](left, right)
 
294
 
 
295
    def call_unop(self, context, operator, arg):
 
296
        """For intercepted unary operator calls (:meth:`intercepted_unops`)
 
297
        this function is executed instead of the builtin operator.  This can
 
298
        be used to fine tune the behavior of certain operators.
 
299
 
 
300
        .. versionadded:: 2.6
 
301
        """
 
302
        return self.unop_table[operator](arg)
 
303
 
 
304
    def getitem(self, obj, argument):
 
305
        """Subscribe an object from sandboxed code."""
 
306
        try:
 
307
            return obj[argument]
 
308
        except (TypeError, LookupError):
 
309
            if isinstance(argument, string_types):
 
310
                try:
 
311
                    attr = str(argument)
 
312
                except Exception:
 
313
                    pass
 
314
                else:
 
315
                    try:
 
316
                        value = getattr(obj, attr)
 
317
                    except AttributeError:
 
318
                        pass
 
319
                    else:
 
320
                        if self.is_safe_attribute(obj, argument, value):
 
321
                            return value
 
322
                        return self.unsafe_undefined(obj, argument)
 
323
        return self.undefined(obj=obj, name=argument)
 
324
 
 
325
    def getattr(self, obj, attribute):
 
326
        """Subscribe an object from sandboxed code and prefer the
 
327
        attribute.  The attribute passed *must* be a bytestring.
 
328
        """
 
329
        try:
 
330
            value = getattr(obj, attribute)
 
331
        except AttributeError:
 
332
            try:
 
333
                return obj[attribute]
 
334
            except (TypeError, LookupError):
 
335
                pass
 
336
        else:
 
337
            if self.is_safe_attribute(obj, attribute, value):
 
338
                return value
 
339
            return self.unsafe_undefined(obj, attribute)
 
340
        return self.undefined(obj=obj, name=attribute)
 
341
 
 
342
    def unsafe_undefined(self, obj, attribute):
 
343
        """Return an undefined object for unsafe attributes."""
 
344
        return self.undefined('access to attribute %r of %r '
 
345
                              'object is unsafe.' % (
 
346
            attribute,
 
347
            obj.__class__.__name__
 
348
        ), name=attribute, obj=obj, exc=SecurityError)
 
349
 
 
350
    def call(__self, __context, __obj, *args, **kwargs):
 
351
        """Call an object from sandboxed code."""
 
352
        # the double prefixes are to avoid double keyword argument
 
353
        # errors when proxying the call.
 
354
        if not __self.is_safe_callable(__obj):
 
355
            raise SecurityError('%r is not safely callable' % (__obj,))
 
356
        return __context.call(__obj, *args, **kwargs)
 
357
 
 
358
 
 
359
class ImmutableSandboxedEnvironment(SandboxedEnvironment):
 
360
    """Works exactly like the regular `SandboxedEnvironment` but does not
 
361
    permit modifications on the builtin mutable objects `list`, `set`, and
 
362
    `dict` by using the :func:`modifies_known_mutable` function.
 
363
    """
 
364
 
 
365
    def is_safe_attribute(self, obj, attr, value):
 
366
        if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
 
367
            return False
 
368
        return not modifies_known_mutable(obj, attr)