• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# -*- coding: utf-8 -*-
2"""A sandbox layer that ensures unsafe operations cannot be performed.
3Useful when the template itself comes from an untrusted source.
4"""
5import operator
6import types
7import warnings
8from collections import deque
9from string import Formatter
10
11from markupsafe import EscapeFormatter
12from markupsafe import Markup
13
14from ._compat import abc
15from ._compat import PY2
16from ._compat import range_type
17from ._compat import string_types
18from .environment import Environment
19from .exceptions import SecurityError
20
21#: maximum number of items a range may produce
22MAX_RANGE = 100000
23
24#: attributes of function objects that are considered unsafe.
25if PY2:
26    UNSAFE_FUNCTION_ATTRIBUTES = {
27        "func_closure",
28        "func_code",
29        "func_dict",
30        "func_defaults",
31        "func_globals",
32    }
33else:
34    # On versions > python 2 the special attributes on functions are gone,
35    # but they remain on methods and generators for whatever reason.
36    UNSAFE_FUNCTION_ATTRIBUTES = set()
37
38#: unsafe method attributes.  function attributes are unsafe for methods too
39UNSAFE_METHOD_ATTRIBUTES = {"im_class", "im_func", "im_self"}
40
41#: unsafe generator attributes.
42UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
43
44#: unsafe attributes on coroutines
45UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
46
47#: unsafe attributes on async generators
48UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
49
50# make sure we don't warn in python 2.6 about stuff we don't care about
51warnings.filterwarnings(
52    "ignore", "the sets module", DeprecationWarning, module=__name__
53)
54
55_mutable_set_types = (set,)
56_mutable_mapping_types = (dict,)
57_mutable_sequence_types = (list,)
58
59# on python 2.x we can register the user collection types
60try:
61    from UserDict import UserDict, DictMixin
62    from UserList import UserList
63
64    _mutable_mapping_types += (UserDict, DictMixin)
65    _mutable_set_types += (UserList,)
66except ImportError:
67    pass
68
69# if sets is still available, register the mutable set from there as well
70try:
71    from sets import Set
72
73    _mutable_set_types += (Set,)
74except ImportError:
75    pass
76
77#: register Python 2.6 abstract base classes
78_mutable_set_types += (abc.MutableSet,)
79_mutable_mapping_types += (abc.MutableMapping,)
80_mutable_sequence_types += (abc.MutableSequence,)
81
82_mutable_spec = (
83    (
84        _mutable_set_types,
85        frozenset(
86            [
87                "add",
88                "clear",
89                "difference_update",
90                "discard",
91                "pop",
92                "remove",
93                "symmetric_difference_update",
94                "update",
95            ]
96        ),
97    ),
98    (
99        _mutable_mapping_types,
100        frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
101    ),
102    (
103        _mutable_sequence_types,
104        frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
105    ),
106    (
107        deque,
108        frozenset(
109            [
110                "append",
111                "appendleft",
112                "clear",
113                "extend",
114                "extendleft",
115                "pop",
116                "popleft",
117                "remove",
118                "rotate",
119            ]
120        ),
121    ),
122)
123
124
125class _MagicFormatMapping(abc.Mapping):
126    """This class implements a dummy wrapper to fix a bug in the Python
127    standard library for string formatting.
128
129    See https://bugs.python.org/issue13598 for information about why
130    this is necessary.
131    """
132
133    def __init__(self, args, kwargs):
134        self._args = args
135        self._kwargs = kwargs
136        self._last_index = 0
137
138    def __getitem__(self, key):
139        if key == "":
140            idx = self._last_index
141            self._last_index += 1
142            try:
143                return self._args[idx]
144            except LookupError:
145                pass
146            key = str(idx)
147        return self._kwargs[key]
148
149    def __iter__(self):
150        return iter(self._kwargs)
151
152    def __len__(self):
153        return len(self._kwargs)
154
155
156def inspect_format_method(callable):
157    if not isinstance(
158        callable, (types.MethodType, types.BuiltinMethodType)
159    ) or callable.__name__ not in ("format", "format_map"):
160        return None
161    obj = callable.__self__
162    if isinstance(obj, string_types):
163        return obj
164
165
166def safe_range(*args):
167    """A range that can't generate ranges with a length of more than
168    MAX_RANGE items.
169    """
170    rng = range_type(*args)
171
172    if len(rng) > MAX_RANGE:
173        raise OverflowError(
174            "Range too big. The sandbox blocks ranges larger than"
175            " MAX_RANGE (%d)." % MAX_RANGE
176        )
177
178    return rng
179
180
181def unsafe(f):
182    """Marks a function or method as unsafe.
183
184    ::
185
186        @unsafe
187        def delete(self):
188            pass
189    """
190    f.unsafe_callable = True
191    return f
192
193
194def is_internal_attribute(obj, attr):
195    """Test if the attribute given is an internal python attribute.  For
196    example this function returns `True` for the `func_code` attribute of
197    python objects.  This is useful if the environment method
198    :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden.
199
200    >>> from jinja2.sandbox import is_internal_attribute
201    >>> is_internal_attribute(str, "mro")
202    True
203    >>> is_internal_attribute(str, "upper")
204    False
205    """
206    if isinstance(obj, types.FunctionType):
207        if attr in UNSAFE_FUNCTION_ATTRIBUTES:
208            return True
209    elif isinstance(obj, types.MethodType):
210        if attr in UNSAFE_FUNCTION_ATTRIBUTES or attr in UNSAFE_METHOD_ATTRIBUTES:
211            return True
212    elif isinstance(obj, type):
213        if attr == "mro":
214            return True
215    elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)):
216        return True
217    elif isinstance(obj, types.GeneratorType):
218        if attr in UNSAFE_GENERATOR_ATTRIBUTES:
219            return True
220    elif hasattr(types, "CoroutineType") and isinstance(obj, types.CoroutineType):
221        if attr in UNSAFE_COROUTINE_ATTRIBUTES:
222            return True
223    elif hasattr(types, "AsyncGeneratorType") and isinstance(
224        obj, types.AsyncGeneratorType
225    ):
226        if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
227            return True
228    return attr.startswith("__")
229
230
231def modifies_known_mutable(obj, attr):
232    """This function checks if an attribute on a builtin mutable object
233    (list, dict, set or deque) would modify it if called.  It also supports
234    the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
235    with Python 2.6 onwards the abstract base classes `MutableSet`,
236    `MutableMapping`, and `MutableSequence`.
237
238    >>> modifies_known_mutable({}, "clear")
239    True
240    >>> modifies_known_mutable({}, "keys")
241    False
242    >>> modifies_known_mutable([], "append")
243    True
244    >>> modifies_known_mutable([], "index")
245    False
246
247    If called with an unsupported object (such as unicode) `False` is
248    returned.
249
250    >>> modifies_known_mutable("foo", "upper")
251    False
252    """
253    for typespec, unsafe in _mutable_spec:
254        if isinstance(obj, typespec):
255            return attr in unsafe
256    return False
257
258
259class SandboxedEnvironment(Environment):
260    """The sandboxed environment.  It works like the regular environment but
261    tells the compiler to generate sandboxed code.  Additionally subclasses of
262    this environment may override the methods that tell the runtime what
263    attributes or functions are safe to access.
264
265    If the template tries to access insecure code a :exc:`SecurityError` is
266    raised.  However also other exceptions may occur during the rendering so
267    the caller has to ensure that all exceptions are caught.
268    """
269
270    sandboxed = True
271
272    #: default callback table for the binary operators.  A copy of this is
273    #: available on each instance of a sandboxed environment as
274    #: :attr:`binop_table`
275    default_binop_table = {
276        "+": operator.add,
277        "-": operator.sub,
278        "*": operator.mul,
279        "/": operator.truediv,
280        "//": operator.floordiv,
281        "**": operator.pow,
282        "%": operator.mod,
283    }
284
285    #: default callback table for the unary operators.  A copy of this is
286    #: available on each instance of a sandboxed environment as
287    #: :attr:`unop_table`
288    default_unop_table = {"+": operator.pos, "-": operator.neg}
289
290    #: a set of binary operators that should be intercepted.  Each operator
291    #: that is added to this set (empty by default) is delegated to the
292    #: :meth:`call_binop` method that will perform the operator.  The default
293    #: operator callback is specified by :attr:`binop_table`.
294    #:
295    #: The following binary operators are interceptable:
296    #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**``
297    #:
298    #: The default operation form the operator table corresponds to the
299    #: builtin function.  Intercepted calls are always slower than the native
300    #: operator call, so make sure only to intercept the ones you are
301    #: interested in.
302    #:
303    #: .. versionadded:: 2.6
304    intercepted_binops = frozenset()
305
306    #: a set of unary operators that should be intercepted.  Each operator
307    #: that is added to this set (empty by default) is delegated to the
308    #: :meth:`call_unop` method that will perform the operator.  The default
309    #: operator callback is specified by :attr:`unop_table`.
310    #:
311    #: The following unary operators are interceptable: ``+``, ``-``
312    #:
313    #: The default operation form the operator table corresponds to the
314    #: builtin function.  Intercepted calls are always slower than the native
315    #: operator call, so make sure only to intercept the ones you are
316    #: interested in.
317    #:
318    #: .. versionadded:: 2.6
319    intercepted_unops = frozenset()
320
321    def intercept_unop(self, operator):
322        """Called during template compilation with the name of a unary
323        operator to check if it should be intercepted at runtime.  If this
324        method returns `True`, :meth:`call_unop` is executed for this unary
325        operator.  The default implementation of :meth:`call_unop` will use
326        the :attr:`unop_table` dictionary to perform the operator with the
327        same logic as the builtin one.
328
329        The following unary operators are interceptable: ``+`` and ``-``
330
331        Intercepted calls are always slower than the native operator call,
332        so make sure only to intercept the ones you are interested in.
333
334        .. versionadded:: 2.6
335        """
336        return False
337
338    def __init__(self, *args, **kwargs):
339        Environment.__init__(self, *args, **kwargs)
340        self.globals["range"] = safe_range
341        self.binop_table = self.default_binop_table.copy()
342        self.unop_table = self.default_unop_table.copy()
343
344    def is_safe_attribute(self, obj, attr, value):
345        """The sandboxed environment will call this method to check if the
346        attribute of an object is safe to access.  Per default all attributes
347        starting with an underscore are considered private as well as the
348        special attributes of internal python objects as returned by the
349        :func:`is_internal_attribute` function.
350        """
351        return not (attr.startswith("_") or is_internal_attribute(obj, attr))
352
353    def is_safe_callable(self, obj):
354        """Check if an object is safely callable.  Per default a function is
355        considered safe unless the `unsafe_callable` attribute exists and is
356        True.  Override this method to alter the behavior, but this won't
357        affect the `unsafe` decorator from this module.
358        """
359        return not (
360            getattr(obj, "unsafe_callable", False) or getattr(obj, "alters_data", False)
361        )
362
363    def call_binop(self, context, operator, left, right):
364        """For intercepted binary operator calls (:meth:`intercepted_binops`)
365        this function is executed instead of the builtin operator.  This can
366        be used to fine tune the behavior of certain operators.
367
368        .. versionadded:: 2.6
369        """
370        return self.binop_table[operator](left, right)
371
372    def call_unop(self, context, operator, arg):
373        """For intercepted unary operator calls (:meth:`intercepted_unops`)
374        this function is executed instead of the builtin operator.  This can
375        be used to fine tune the behavior of certain operators.
376
377        .. versionadded:: 2.6
378        """
379        return self.unop_table[operator](arg)
380
381    def getitem(self, obj, argument):
382        """Subscribe an object from sandboxed code."""
383        try:
384            return obj[argument]
385        except (TypeError, LookupError):
386            if isinstance(argument, string_types):
387                try:
388                    attr = str(argument)
389                except Exception:
390                    pass
391                else:
392                    try:
393                        value = getattr(obj, attr)
394                    except AttributeError:
395                        pass
396                    else:
397                        if self.is_safe_attribute(obj, argument, value):
398                            return value
399                        return self.unsafe_undefined(obj, argument)
400        return self.undefined(obj=obj, name=argument)
401
402    def getattr(self, obj, attribute):
403        """Subscribe an object from sandboxed code and prefer the
404        attribute.  The attribute passed *must* be a bytestring.
405        """
406        try:
407            value = getattr(obj, attribute)
408        except AttributeError:
409            try:
410                return obj[attribute]
411            except (TypeError, LookupError):
412                pass
413        else:
414            if self.is_safe_attribute(obj, attribute, value):
415                return value
416            return self.unsafe_undefined(obj, attribute)
417        return self.undefined(obj=obj, name=attribute)
418
419    def unsafe_undefined(self, obj, attribute):
420        """Return an undefined object for unsafe attributes."""
421        return self.undefined(
422            "access to attribute %r of %r "
423            "object is unsafe." % (attribute, obj.__class__.__name__),
424            name=attribute,
425            obj=obj,
426            exc=SecurityError,
427        )
428
429    def format_string(self, s, args, kwargs, format_func=None):
430        """If a format call is detected, then this is routed through this
431        method so that our safety sandbox can be used for it.
432        """
433        if isinstance(s, Markup):
434            formatter = SandboxedEscapeFormatter(self, s.escape)
435        else:
436            formatter = SandboxedFormatter(self)
437
438        if format_func is not None and format_func.__name__ == "format_map":
439            if len(args) != 1 or kwargs:
440                raise TypeError(
441                    "format_map() takes exactly one argument %d given"
442                    % (len(args) + (kwargs is not None))
443                )
444
445            kwargs = args[0]
446            args = None
447
448        kwargs = _MagicFormatMapping(args, kwargs)
449        rv = formatter.vformat(s, args, kwargs)
450        return type(s)(rv)
451
452    def call(__self, __context, __obj, *args, **kwargs):  # noqa: B902
453        """Call an object from sandboxed code."""
454        fmt = inspect_format_method(__obj)
455        if fmt is not None:
456            return __self.format_string(fmt, args, kwargs, __obj)
457
458        # the double prefixes are to avoid double keyword argument
459        # errors when proxying the call.
460        if not __self.is_safe_callable(__obj):
461            raise SecurityError("%r is not safely callable" % (__obj,))
462        return __context.call(__obj, *args, **kwargs)
463
464
465class ImmutableSandboxedEnvironment(SandboxedEnvironment):
466    """Works exactly like the regular `SandboxedEnvironment` but does not
467    permit modifications on the builtin mutable objects `list`, `set`, and
468    `dict` by using the :func:`modifies_known_mutable` function.
469    """
470
471    def is_safe_attribute(self, obj, attr, value):
472        if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
473            return False
474        return not modifies_known_mutable(obj, attr)
475
476
477# This really is not a public API apparently.
478try:
479    from _string import formatter_field_name_split
480except ImportError:
481
482    def formatter_field_name_split(field_name):
483        return field_name._formatter_field_name_split()
484
485
486class SandboxedFormatterMixin(object):
487    def __init__(self, env):
488        self._env = env
489
490    def get_field(self, field_name, args, kwargs):
491        first, rest = formatter_field_name_split(field_name)
492        obj = self.get_value(first, args, kwargs)
493        for is_attr, i in rest:
494            if is_attr:
495                obj = self._env.getattr(obj, i)
496            else:
497                obj = self._env.getitem(obj, i)
498        return obj, first
499
500
501class SandboxedFormatter(SandboxedFormatterMixin, Formatter):
502    def __init__(self, env):
503        SandboxedFormatterMixin.__init__(self, env)
504        Formatter.__init__(self)
505
506
507class SandboxedEscapeFormatter(SandboxedFormatterMixin, EscapeFormatter):
508    def __init__(self, env, escape):
509        SandboxedFormatterMixin.__init__(self, env)
510        EscapeFormatter.__init__(self, escape)
511