jinja2.sandbox

A sandbox layer that ensures unsafe operations cannot be performed. Useful when the template itself comes from an untrusted source.

  1"""A sandbox layer that ensures unsafe operations cannot be performed.
  2Useful when the template itself comes from an untrusted source.
  3"""
  4import operator
  5import types
  6import typing as t
  7from _string import formatter_field_name_split  # type: ignore
  8from collections import abc
  9from collections import deque
 10from string import Formatter
 11
 12from markupsafe import EscapeFormatter
 13from markupsafe import Markup
 14
 15from .environment import Environment
 16from .exceptions import SecurityError
 17from .runtime import Context
 18from .runtime import Undefined
 19
 20F = t.TypeVar("F", bound=t.Callable[..., t.Any])
 21
 22#: maximum number of items a range may produce
 23MAX_RANGE = 100000
 24
 25#: Unsafe function attributes.
 26UNSAFE_FUNCTION_ATTRIBUTES: t.Set[str] = set()
 27
 28#: Unsafe method attributes. Function attributes are unsafe for methods too.
 29UNSAFE_METHOD_ATTRIBUTES: t.Set[str] = set()
 30
 31#: unsafe generator attributes.
 32UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
 33
 34#: unsafe attributes on coroutines
 35UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
 36
 37#: unsafe attributes on async generators
 38UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
 39
 40_mutable_spec: t.Tuple[t.Tuple[t.Type, t.FrozenSet[str]], ...] = (
 41    (
 42        abc.MutableSet,
 43        frozenset(
 44            [
 45                "add",
 46                "clear",
 47                "difference_update",
 48                "discard",
 49                "pop",
 50                "remove",
 51                "symmetric_difference_update",
 52                "update",
 53            ]
 54        ),
 55    ),
 56    (
 57        abc.MutableMapping,
 58        frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
 59    ),
 60    (
 61        abc.MutableSequence,
 62        frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
 63    ),
 64    (
 65        deque,
 66        frozenset(
 67            [
 68                "append",
 69                "appendleft",
 70                "clear",
 71                "extend",
 72                "extendleft",
 73                "pop",
 74                "popleft",
 75                "remove",
 76                "rotate",
 77            ]
 78        ),
 79    ),
 80)
 81
 82
 83def inspect_format_method(callable: t.Callable) -> t.Optional[str]:
 84    if not isinstance(
 85        callable, (types.MethodType, types.BuiltinMethodType)
 86    ) or callable.__name__ not in ("format", "format_map"):
 87        return None
 88
 89    obj = callable.__self__
 90
 91    if isinstance(obj, str):
 92        return obj
 93
 94    return None
 95
 96
 97def safe_range(*args: int) -> range:
 98    """A range that can't generate ranges with a length of more than
 99    MAX_RANGE items.
100    """
101    rng = range(*args)
102
103    if len(rng) > MAX_RANGE:
104        raise OverflowError(
105            "Range too big. The sandbox blocks ranges larger than"
106            f" MAX_RANGE ({MAX_RANGE})."
107        )
108
109    return rng
110
111
112def unsafe(f: F) -> F:
113    """Marks a function or method as unsafe.
114
115    .. code-block: python
116
117        @unsafe
118        def delete(self):
119            pass
120    """
121    f.unsafe_callable = True  # type: ignore
122    return f
123
124
125def is_internal_attribute(obj: t.Any, attr: str) -> bool:
126    """Test if the attribute given is an internal python attribute.  For
127    example this function returns `True` for the `func_code` attribute of
128    python objects.  This is useful if the environment method
129    :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden.
130
131    >>> from jinja2.sandbox import is_internal_attribute
132    >>> is_internal_attribute(str, "mro")
133    True
134    >>> is_internal_attribute(str, "upper")
135    False
136    """
137    if isinstance(obj, types.FunctionType):
138        if attr in UNSAFE_FUNCTION_ATTRIBUTES:
139            return True
140    elif isinstance(obj, types.MethodType):
141        if attr in UNSAFE_FUNCTION_ATTRIBUTES or attr in UNSAFE_METHOD_ATTRIBUTES:
142            return True
143    elif isinstance(obj, type):
144        if attr == "mro":
145            return True
146    elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)):
147        return True
148    elif isinstance(obj, types.GeneratorType):
149        if attr in UNSAFE_GENERATOR_ATTRIBUTES:
150            return True
151    elif hasattr(types, "CoroutineType") and isinstance(obj, types.CoroutineType):
152        if attr in UNSAFE_COROUTINE_ATTRIBUTES:
153            return True
154    elif hasattr(types, "AsyncGeneratorType") and isinstance(
155        obj, types.AsyncGeneratorType
156    ):
157        if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
158            return True
159    return attr.startswith("__")
160
161
162def modifies_known_mutable(obj: t.Any, attr: str) -> bool:
163    """This function checks if an attribute on a builtin mutable object
164    (list, dict, set or deque) or the corresponding ABCs would modify it
165    if called.
166
167    >>> modifies_known_mutable({}, "clear")
168    True
169    >>> modifies_known_mutable({}, "keys")
170    False
171    >>> modifies_known_mutable([], "append")
172    True
173    >>> modifies_known_mutable([], "index")
174    False
175
176    If called with an unsupported object, ``False`` is returned.
177
178    >>> modifies_known_mutable("foo", "upper")
179    False
180    """
181    for typespec, unsafe in _mutable_spec:
182        if isinstance(obj, typespec):
183            return attr in unsafe
184    return False
185
186
187class SandboxedEnvironment(Environment):
188    """The sandboxed environment.  It works like the regular environment but
189    tells the compiler to generate sandboxed code.  Additionally subclasses of
190    this environment may override the methods that tell the runtime what
191    attributes or functions are safe to access.
192
193    If the template tries to access insecure code a :exc:`SecurityError` is
194    raised.  However also other exceptions may occur during the rendering so
195    the caller has to ensure that all exceptions are caught.
196    """
197
198    sandboxed = True
199
200    #: default callback table for the binary operators.  A copy of this is
201    #: available on each instance of a sandboxed environment as
202    #: :attr:`binop_table`
203    default_binop_table: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
204        "+": operator.add,
205        "-": operator.sub,
206        "*": operator.mul,
207        "/": operator.truediv,
208        "//": operator.floordiv,
209        "**": operator.pow,
210        "%": operator.mod,
211    }
212
213    #: default callback table for the unary operators.  A copy of this is
214    #: available on each instance of a sandboxed environment as
215    #: :attr:`unop_table`
216    default_unop_table: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
217        "+": operator.pos,
218        "-": operator.neg,
219    }
220
221    #: a set of binary operators that should be intercepted.  Each operator
222    #: that is added to this set (empty by default) is delegated to the
223    #: :meth:`call_binop` method that will perform the operator.  The default
224    #: operator callback is specified by :attr:`binop_table`.
225    #:
226    #: The following binary operators are interceptable:
227    #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**``
228    #:
229    #: The default operation form the operator table corresponds to the
230    #: builtin function.  Intercepted calls are always slower than the native
231    #: operator call, so make sure only to intercept the ones you are
232    #: interested in.
233    #:
234    #: .. versionadded:: 2.6
235    intercepted_binops: t.FrozenSet[str] = frozenset()
236
237    #: a set of unary operators that should be intercepted.  Each operator
238    #: that is added to this set (empty by default) is delegated to the
239    #: :meth:`call_unop` method that will perform the operator.  The default
240    #: operator callback is specified by :attr:`unop_table`.
241    #:
242    #: The following unary operators are interceptable: ``+``, ``-``
243    #:
244    #: The default operation form the operator table corresponds to the
245    #: builtin function.  Intercepted calls are always slower than the native
246    #: operator call, so make sure only to intercept the ones you are
247    #: interested in.
248    #:
249    #: .. versionadded:: 2.6
250    intercepted_unops: t.FrozenSet[str] = frozenset()
251
252    def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
253        super().__init__(*args, **kwargs)
254        self.globals["range"] = safe_range
255        self.binop_table = self.default_binop_table.copy()
256        self.unop_table = self.default_unop_table.copy()
257
258    def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
259        """The sandboxed environment will call this method to check if the
260        attribute of an object is safe to access.  Per default all attributes
261        starting with an underscore are considered private as well as the
262        special attributes of internal python objects as returned by the
263        :func:`is_internal_attribute` function.
264        """
265        return not (attr.startswith("_") or is_internal_attribute(obj, attr))
266
267    def is_safe_callable(self, obj: t.Any) -> bool:
268        """Check if an object is safely callable. By default callables
269        are considered safe unless decorated with :func:`unsafe`.
270
271        This also recognizes the Django convention of setting
272        ``func.alters_data = True``.
273        """
274        return not (
275            getattr(obj, "unsafe_callable", False) or getattr(obj, "alters_data", False)
276        )
277
278    def call_binop(
279        self, context: Context, operator: str, left: t.Any, right: t.Any
280    ) -> t.Any:
281        """For intercepted binary operator calls (:meth:`intercepted_binops`)
282        this function is executed instead of the builtin operator.  This can
283        be used to fine tune the behavior of certain operators.
284
285        .. versionadded:: 2.6
286        """
287        return self.binop_table[operator](left, right)
288
289    def call_unop(self, context: Context, operator: str, arg: t.Any) -> t.Any:
290        """For intercepted unary operator calls (:meth:`intercepted_unops`)
291        this function is executed instead of the builtin operator.  This can
292        be used to fine tune the behavior of certain operators.
293
294        .. versionadded:: 2.6
295        """
296        return self.unop_table[operator](arg)
297
298    def getitem(
299        self, obj: t.Any, argument: t.Union[str, t.Any]
300    ) -> t.Union[t.Any, Undefined]:
301        """Subscribe an object from sandboxed code."""
302        try:
303            return obj[argument]
304        except (TypeError, LookupError):
305            if isinstance(argument, str):
306                try:
307                    attr = str(argument)
308                except Exception:
309                    pass
310                else:
311                    try:
312                        value = getattr(obj, attr)
313                    except AttributeError:
314                        pass
315                    else:
316                        if self.is_safe_attribute(obj, argument, value):
317                            return value
318                        return self.unsafe_undefined(obj, argument)
319        return self.undefined(obj=obj, name=argument)
320
321    def getattr(self, obj: t.Any, attribute: str) -> t.Union[t.Any, Undefined]:
322        """Subscribe an object from sandboxed code and prefer the
323        attribute.  The attribute passed *must* be a bytestring.
324        """
325        try:
326            value = getattr(obj, attribute)
327        except AttributeError:
328            try:
329                return obj[attribute]
330            except (TypeError, LookupError):
331                pass
332        else:
333            if self.is_safe_attribute(obj, attribute, value):
334                return value
335            return self.unsafe_undefined(obj, attribute)
336        return self.undefined(obj=obj, name=attribute)
337
338    def unsafe_undefined(self, obj: t.Any, attribute: str) -> Undefined:
339        """Return an undefined object for unsafe attributes."""
340        return self.undefined(
341            f"access to attribute {attribute!r} of"
342            f" {type(obj).__name__!r} object is unsafe.",
343            name=attribute,
344            obj=obj,
345            exc=SecurityError,
346        )
347
348    def format_string(
349        self,
350        s: str,
351        args: t.Tuple[t.Any, ...],
352        kwargs: t.Dict[str, t.Any],
353        format_func: t.Optional[t.Callable] = None,
354    ) -> str:
355        """If a format call is detected, then this is routed through this
356        method so that our safety sandbox can be used for it.
357        """
358        formatter: SandboxedFormatter
359        if isinstance(s, Markup):
360            formatter = SandboxedEscapeFormatter(self, escape=s.escape)
361        else:
362            formatter = SandboxedFormatter(self)
363
364        if format_func is not None and format_func.__name__ == "format_map":
365            if len(args) != 1 or kwargs:
366                raise TypeError(
367                    "format_map() takes exactly one argument"
368                    f" {len(args) + (kwargs is not None)} given"
369                )
370
371            kwargs = args[0]
372            args = ()
373
374        rv = formatter.vformat(s, args, kwargs)
375        return type(s)(rv)
376
377    def call(
378        __self,  # noqa: B902
379        __context: Context,
380        __obj: t.Any,
381        *args: t.Any,
382        **kwargs: t.Any,
383    ) -> t.Any:
384        """Call an object from sandboxed code."""
385        fmt = inspect_format_method(__obj)
386        if fmt is not None:
387            return __self.format_string(fmt, args, kwargs, __obj)
388
389        # the double prefixes are to avoid double keyword argument
390        # errors when proxying the call.
391        if not __self.is_safe_callable(__obj):
392            raise SecurityError(f"{__obj!r} is not safely callable")
393        return __context.call(__obj, *args, **kwargs)
394
395
396class ImmutableSandboxedEnvironment(SandboxedEnvironment):
397    """Works exactly like the regular `SandboxedEnvironment` but does not
398    permit modifications on the builtin mutable objects `list`, `set`, and
399    `dict` by using the :func:`modifies_known_mutable` function.
400    """
401
402    def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
403        if not super().is_safe_attribute(obj, attr, value):
404            return False
405
406        return not modifies_known_mutable(obj, attr)
407
408
409class SandboxedFormatter(Formatter):
410    def __init__(self, env: Environment, **kwargs: t.Any) -> None:
411        self._env = env
412        super().__init__(**kwargs)
413
414    def get_field(
415        self, field_name: str, args: t.Sequence[t.Any], kwargs: t.Mapping[str, t.Any]
416    ) -> t.Tuple[t.Any, str]:
417        first, rest = formatter_field_name_split(field_name)
418        obj = self.get_value(first, args, kwargs)
419        for is_attr, i in rest:
420            if is_attr:
421                obj = self._env.getattr(obj, i)
422            else:
423                obj = self._env.getitem(obj, i)
424        return obj, first
425
426
427class SandboxedEscapeFormatter(SandboxedFormatter, EscapeFormatter):
428    pass
MAX_RANGE = 100000
UNSAFE_FUNCTION_ATTRIBUTES: Set[str] = set()
UNSAFE_METHOD_ATTRIBUTES: Set[str] = set()
UNSAFE_GENERATOR_ATTRIBUTES = {'gi_frame', 'gi_code'}
UNSAFE_COROUTINE_ATTRIBUTES = {'cr_frame', 'cr_code'}
UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {'ag_frame', 'ag_code'}
def inspect_format_method(callable: Callable) -> Optional[str]:
84def inspect_format_method(callable: t.Callable) -> t.Optional[str]:
85    if not isinstance(
86        callable, (types.MethodType, types.BuiltinMethodType)
87    ) or callable.__name__ not in ("format", "format_map"):
88        return None
89
90    obj = callable.__self__
91
92    if isinstance(obj, str):
93        return obj
94
95    return None
def safe_range(*args: int) -> range:
 98def safe_range(*args: int) -> range:
 99    """A range that can't generate ranges with a length of more than
100    MAX_RANGE items.
101    """
102    rng = range(*args)
103
104    if len(rng) > MAX_RANGE:
105        raise OverflowError(
106            "Range too big. The sandbox blocks ranges larger than"
107            f" MAX_RANGE ({MAX_RANGE})."
108        )
109
110    return rng

A range that can't generate ranges with a length of more than MAX_RANGE items.

def unsafe(f: ~F) -> ~F:
113def unsafe(f: F) -> F:
114    """Marks a function or method as unsafe.
115
116    .. code-block: python
117
118        @unsafe
119        def delete(self):
120            pass
121    """
122    f.unsafe_callable = True  # type: ignore
123    return f

Marks a function or method as unsafe.

.. code-block: python

@unsafe
def delete(self):
    pass
def is_internal_attribute(obj: Any, attr: str) -> bool:
126def is_internal_attribute(obj: t.Any, attr: str) -> bool:
127    """Test if the attribute given is an internal python attribute.  For
128    example this function returns `True` for the `func_code` attribute of
129    python objects.  This is useful if the environment method
130    :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden.
131
132    >>> from jinja2.sandbox import is_internal_attribute
133    >>> is_internal_attribute(str, "mro")
134    True
135    >>> is_internal_attribute(str, "upper")
136    False
137    """
138    if isinstance(obj, types.FunctionType):
139        if attr in UNSAFE_FUNCTION_ATTRIBUTES:
140            return True
141    elif isinstance(obj, types.MethodType):
142        if attr in UNSAFE_FUNCTION_ATTRIBUTES or attr in UNSAFE_METHOD_ATTRIBUTES:
143            return True
144    elif isinstance(obj, type):
145        if attr == "mro":
146            return True
147    elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)):
148        return True
149    elif isinstance(obj, types.GeneratorType):
150        if attr in UNSAFE_GENERATOR_ATTRIBUTES:
151            return True
152    elif hasattr(types, "CoroutineType") and isinstance(obj, types.CoroutineType):
153        if attr in UNSAFE_COROUTINE_ATTRIBUTES:
154            return True
155    elif hasattr(types, "AsyncGeneratorType") and isinstance(
156        obj, types.AsyncGeneratorType
157    ):
158        if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
159            return True
160    return attr.startswith("__")

Test if the attribute given is an internal python attribute. For example this function returns True for the func_code attribute of python objects. This is useful if the environment method ~SandboxedEnvironment.is_safe_attribute() is overridden.

>>> from jinja2.sandbox import is_internal_attribute
>>> is_internal_attribute(str, "mro")
True
>>> is_internal_attribute(str, "upper")
False
def modifies_known_mutable(obj: Any, attr: str) -> bool:
163def modifies_known_mutable(obj: t.Any, attr: str) -> bool:
164    """This function checks if an attribute on a builtin mutable object
165    (list, dict, set or deque) or the corresponding ABCs would modify it
166    if called.
167
168    >>> modifies_known_mutable({}, "clear")
169    True
170    >>> modifies_known_mutable({}, "keys")
171    False
172    >>> modifies_known_mutable([], "append")
173    True
174    >>> modifies_known_mutable([], "index")
175    False
176
177    If called with an unsupported object, ``False`` is returned.
178
179    >>> modifies_known_mutable("foo", "upper")
180    False
181    """
182    for typespec, unsafe in _mutable_spec:
183        if isinstance(obj, typespec):
184            return attr in unsafe
185    return False

This function checks if an attribute on a builtin mutable object (list, dict, set or deque) or the corresponding ABCs would modify it if called.

>>> modifies_known_mutable({}, "clear")
True
>>> modifies_known_mutable({}, "keys")
False
>>> modifies_known_mutable([], "append")
True
>>> modifies_known_mutable([], "index")
False

If called with an unsupported object, False is returned.

>>> modifies_known_mutable("foo", "upper")
False
class SandboxedEnvironment(jinja2.environment.Environment):
188class SandboxedEnvironment(Environment):
189    """The sandboxed environment.  It works like the regular environment but
190    tells the compiler to generate sandboxed code.  Additionally subclasses of
191    this environment may override the methods that tell the runtime what
192    attributes or functions are safe to access.
193
194    If the template tries to access insecure code a :exc:`SecurityError` is
195    raised.  However also other exceptions may occur during the rendering so
196    the caller has to ensure that all exceptions are caught.
197    """
198
199    sandboxed = True
200
201    #: default callback table for the binary operators.  A copy of this is
202    #: available on each instance of a sandboxed environment as
203    #: :attr:`binop_table`
204    default_binop_table: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
205        "+": operator.add,
206        "-": operator.sub,
207        "*": operator.mul,
208        "/": operator.truediv,
209        "//": operator.floordiv,
210        "**": operator.pow,
211        "%": operator.mod,
212    }
213
214    #: default callback table for the unary operators.  A copy of this is
215    #: available on each instance of a sandboxed environment as
216    #: :attr:`unop_table`
217    default_unop_table: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
218        "+": operator.pos,
219        "-": operator.neg,
220    }
221
222    #: a set of binary operators that should be intercepted.  Each operator
223    #: that is added to this set (empty by default) is delegated to the
224    #: :meth:`call_binop` method that will perform the operator.  The default
225    #: operator callback is specified by :attr:`binop_table`.
226    #:
227    #: The following binary operators are interceptable:
228    #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**``
229    #:
230    #: The default operation form the operator table corresponds to the
231    #: builtin function.  Intercepted calls are always slower than the native
232    #: operator call, so make sure only to intercept the ones you are
233    #: interested in.
234    #:
235    #: .. versionadded:: 2.6
236    intercepted_binops: t.FrozenSet[str] = frozenset()
237
238    #: a set of unary operators that should be intercepted.  Each operator
239    #: that is added to this set (empty by default) is delegated to the
240    #: :meth:`call_unop` method that will perform the operator.  The default
241    #: operator callback is specified by :attr:`unop_table`.
242    #:
243    #: The following unary operators are interceptable: ``+``, ``-``
244    #:
245    #: The default operation form the operator table corresponds to the
246    #: builtin function.  Intercepted calls are always slower than the native
247    #: operator call, so make sure only to intercept the ones you are
248    #: interested in.
249    #:
250    #: .. versionadded:: 2.6
251    intercepted_unops: t.FrozenSet[str] = frozenset()
252
253    def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
254        super().__init__(*args, **kwargs)
255        self.globals["range"] = safe_range
256        self.binop_table = self.default_binop_table.copy()
257        self.unop_table = self.default_unop_table.copy()
258
259    def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
260        """The sandboxed environment will call this method to check if the
261        attribute of an object is safe to access.  Per default all attributes
262        starting with an underscore are considered private as well as the
263        special attributes of internal python objects as returned by the
264        :func:`is_internal_attribute` function.
265        """
266        return not (attr.startswith("_") or is_internal_attribute(obj, attr))
267
268    def is_safe_callable(self, obj: t.Any) -> bool:
269        """Check if an object is safely callable. By default callables
270        are considered safe unless decorated with :func:`unsafe`.
271
272        This also recognizes the Django convention of setting
273        ``func.alters_data = True``.
274        """
275        return not (
276            getattr(obj, "unsafe_callable", False) or getattr(obj, "alters_data", False)
277        )
278
279    def call_binop(
280        self, context: Context, operator: str, left: t.Any, right: t.Any
281    ) -> t.Any:
282        """For intercepted binary operator calls (:meth:`intercepted_binops`)
283        this function is executed instead of the builtin operator.  This can
284        be used to fine tune the behavior of certain operators.
285
286        .. versionadded:: 2.6
287        """
288        return self.binop_table[operator](left, right)
289
290    def call_unop(self, context: Context, operator: str, arg: t.Any) -> t.Any:
291        """For intercepted unary operator calls (:meth:`intercepted_unops`)
292        this function is executed instead of the builtin operator.  This can
293        be used to fine tune the behavior of certain operators.
294
295        .. versionadded:: 2.6
296        """
297        return self.unop_table[operator](arg)
298
299    def getitem(
300        self, obj: t.Any, argument: t.Union[str, t.Any]
301    ) -> t.Union[t.Any, Undefined]:
302        """Subscribe an object from sandboxed code."""
303        try:
304            return obj[argument]
305        except (TypeError, LookupError):
306            if isinstance(argument, str):
307                try:
308                    attr = str(argument)
309                except Exception:
310                    pass
311                else:
312                    try:
313                        value = getattr(obj, attr)
314                    except AttributeError:
315                        pass
316                    else:
317                        if self.is_safe_attribute(obj, argument, value):
318                            return value
319                        return self.unsafe_undefined(obj, argument)
320        return self.undefined(obj=obj, name=argument)
321
322    def getattr(self, obj: t.Any, attribute: str) -> t.Union[t.Any, Undefined]:
323        """Subscribe an object from sandboxed code and prefer the
324        attribute.  The attribute passed *must* be a bytestring.
325        """
326        try:
327            value = getattr(obj, attribute)
328        except AttributeError:
329            try:
330                return obj[attribute]
331            except (TypeError, LookupError):
332                pass
333        else:
334            if self.is_safe_attribute(obj, attribute, value):
335                return value
336            return self.unsafe_undefined(obj, attribute)
337        return self.undefined(obj=obj, name=attribute)
338
339    def unsafe_undefined(self, obj: t.Any, attribute: str) -> Undefined:
340        """Return an undefined object for unsafe attributes."""
341        return self.undefined(
342            f"access to attribute {attribute!r} of"
343            f" {type(obj).__name__!r} object is unsafe.",
344            name=attribute,
345            obj=obj,
346            exc=SecurityError,
347        )
348
349    def format_string(
350        self,
351        s: str,
352        args: t.Tuple[t.Any, ...],
353        kwargs: t.Dict[str, t.Any],
354        format_func: t.Optional[t.Callable] = None,
355    ) -> str:
356        """If a format call is detected, then this is routed through this
357        method so that our safety sandbox can be used for it.
358        """
359        formatter: SandboxedFormatter
360        if isinstance(s, Markup):
361            formatter = SandboxedEscapeFormatter(self, escape=s.escape)
362        else:
363            formatter = SandboxedFormatter(self)
364
365        if format_func is not None and format_func.__name__ == "format_map":
366            if len(args) != 1 or kwargs:
367                raise TypeError(
368                    "format_map() takes exactly one argument"
369                    f" {len(args) + (kwargs is not None)} given"
370                )
371
372            kwargs = args[0]
373            args = ()
374
375        rv = formatter.vformat(s, args, kwargs)
376        return type(s)(rv)
377
378    def call(
379        __self,  # noqa: B902
380        __context: Context,
381        __obj: t.Any,
382        *args: t.Any,
383        **kwargs: t.Any,
384    ) -> t.Any:
385        """Call an object from sandboxed code."""
386        fmt = inspect_format_method(__obj)
387        if fmt is not None:
388            return __self.format_string(fmt, args, kwargs, __obj)
389
390        # the double prefixes are to avoid double keyword argument
391        # errors when proxying the call.
392        if not __self.is_safe_callable(__obj):
393            raise SecurityError(f"{__obj!r} is not safely callable")
394        return __context.call(__obj, *args, **kwargs)

The sandboxed environment. It works like the regular environment but tells the compiler to generate sandboxed code. Additionally subclasses of this environment may override the methods that tell the runtime what attributes or functions are safe to access.

If the template tries to access insecure code a SecurityError is raised. However also other exceptions may occur during the rendering so the caller has to ensure that all exceptions are caught.

SandboxedEnvironment(*args: Any, **kwargs: Any)
253    def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
254        super().__init__(*args, **kwargs)
255        self.globals["range"] = safe_range
256        self.binop_table = self.default_binop_table.copy()
257        self.unop_table = self.default_unop_table.copy()
sandboxed = True
default_binop_table: Dict[str, Callable[[Any, Any], Any]] = {'+': <built-in function add>, '-': <built-in function sub>, '*': <built-in function mul>, '/': <built-in function truediv>, '//': <built-in function floordiv>, '**': <built-in function pow>, '%': <built-in function mod>}
default_unop_table: Dict[str, Callable[[Any], Any]] = {'+': <built-in function pos>, '-': <built-in function neg>}
intercepted_binops: FrozenSet[str] = frozenset()
intercepted_unops: FrozenSet[str] = frozenset()
binop_table
unop_table
def is_safe_attribute(self, obj: Any, attr: str, value: Any) -> bool:
259    def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
260        """The sandboxed environment will call this method to check if the
261        attribute of an object is safe to access.  Per default all attributes
262        starting with an underscore are considered private as well as the
263        special attributes of internal python objects as returned by the
264        :func:`is_internal_attribute` function.
265        """
266        return not (attr.startswith("_") or is_internal_attribute(obj, attr))

The sandboxed environment will call this method to check if the attribute of an object is safe to access. Per default all attributes starting with an underscore are considered private as well as the special attributes of internal python objects as returned by the is_internal_attribute() function.

def is_safe_callable(self, obj: Any) -> bool:
268    def is_safe_callable(self, obj: t.Any) -> bool:
269        """Check if an object is safely callable. By default callables
270        are considered safe unless decorated with :func:`unsafe`.
271
272        This also recognizes the Django convention of setting
273        ``func.alters_data = True``.
274        """
275        return not (
276            getattr(obj, "unsafe_callable", False) or getattr(obj, "alters_data", False)
277        )

Check if an object is safely callable. By default callables are considered safe unless decorated with unsafe().

This also recognizes the Django convention of setting func.alters_data = True.

def call_binop( self, context: jinja2.runtime.Context, operator: str, left: Any, right: Any) -> Any:
279    def call_binop(
280        self, context: Context, operator: str, left: t.Any, right: t.Any
281    ) -> t.Any:
282        """For intercepted binary operator calls (:meth:`intercepted_binops`)
283        this function is executed instead of the builtin operator.  This can
284        be used to fine tune the behavior of certain operators.
285
286        .. versionadded:: 2.6
287        """
288        return self.binop_table[operator](left, right)

For intercepted binary operator calls (intercepted_binops()) this function is executed instead of the builtin operator. This can be used to fine tune the behavior of certain operators.

New in version 2.6.

def call_unop(self, context: jinja2.runtime.Context, operator: str, arg: Any) -> Any:
290    def call_unop(self, context: Context, operator: str, arg: t.Any) -> t.Any:
291        """For intercepted unary operator calls (:meth:`intercepted_unops`)
292        this function is executed instead of the builtin operator.  This can
293        be used to fine tune the behavior of certain operators.
294
295        .. versionadded:: 2.6
296        """
297        return self.unop_table[operator](arg)

For intercepted unary operator calls (intercepted_unops()) this function is executed instead of the builtin operator. This can be used to fine tune the behavior of certain operators.

New in version 2.6.

def getitem( self, obj: Any, argument: Union[str, Any]) -> Union[Any, jinja2.runtime.Undefined]:
299    def getitem(
300        self, obj: t.Any, argument: t.Union[str, t.Any]
301    ) -> t.Union[t.Any, Undefined]:
302        """Subscribe an object from sandboxed code."""
303        try:
304            return obj[argument]
305        except (TypeError, LookupError):
306            if isinstance(argument, str):
307                try:
308                    attr = str(argument)
309                except Exception:
310                    pass
311                else:
312                    try:
313                        value = getattr(obj, attr)
314                    except AttributeError:
315                        pass
316                    else:
317                        if self.is_safe_attribute(obj, argument, value):
318                            return value
319                        return self.unsafe_undefined(obj, argument)
320        return self.undefined(obj=obj, name=argument)

Subscribe an object from sandboxed code.

def getattr(self, obj: Any, attribute: str) -> Union[Any, jinja2.runtime.Undefined]:
322    def getattr(self, obj: t.Any, attribute: str) -> t.Union[t.Any, Undefined]:
323        """Subscribe an object from sandboxed code and prefer the
324        attribute.  The attribute passed *must* be a bytestring.
325        """
326        try:
327            value = getattr(obj, attribute)
328        except AttributeError:
329            try:
330                return obj[attribute]
331            except (TypeError, LookupError):
332                pass
333        else:
334            if self.is_safe_attribute(obj, attribute, value):
335                return value
336            return self.unsafe_undefined(obj, attribute)
337        return self.undefined(obj=obj, name=attribute)

Subscribe an object from sandboxed code and prefer the attribute. The attribute passed must be a bytestring.

def unsafe_undefined(self, obj: Any, attribute: str) -> jinja2.runtime.Undefined:
339    def unsafe_undefined(self, obj: t.Any, attribute: str) -> Undefined:
340        """Return an undefined object for unsafe attributes."""
341        return self.undefined(
342            f"access to attribute {attribute!r} of"
343            f" {type(obj).__name__!r} object is unsafe.",
344            name=attribute,
345            obj=obj,
346            exc=SecurityError,
347        )

Return an undefined object for unsafe attributes.

def format_string( self, s: str, args: Tuple[Any, ...], kwargs: Dict[str, Any], format_func: Optional[Callable] = None) -> str:
349    def format_string(
350        self,
351        s: str,
352        args: t.Tuple[t.Any, ...],
353        kwargs: t.Dict[str, t.Any],
354        format_func: t.Optional[t.Callable] = None,
355    ) -> str:
356        """If a format call is detected, then this is routed through this
357        method so that our safety sandbox can be used for it.
358        """
359        formatter: SandboxedFormatter
360        if isinstance(s, Markup):
361            formatter = SandboxedEscapeFormatter(self, escape=s.escape)
362        else:
363            formatter = SandboxedFormatter(self)
364
365        if format_func is not None and format_func.__name__ == "format_map":
366            if len(args) != 1 or kwargs:
367                raise TypeError(
368                    "format_map() takes exactly one argument"
369                    f" {len(args) + (kwargs is not None)} given"
370                )
371
372            kwargs = args[0]
373            args = ()
374
375        rv = formatter.vformat(s, args, kwargs)
376        return type(s)(rv)

If a format call is detected, then this is routed through this method so that our safety sandbox can be used for it.

def call( _SandboxedEnvironment__self, _SandboxedEnvironment__context: jinja2.runtime.Context, _SandboxedEnvironment__obj: Any, *args: Any, **kwargs: Any) -> Any:
378    def call(
379        __self,  # noqa: B902
380        __context: Context,
381        __obj: t.Any,
382        *args: t.Any,
383        **kwargs: t.Any,
384    ) -> t.Any:
385        """Call an object from sandboxed code."""
386        fmt = inspect_format_method(__obj)
387        if fmt is not None:
388            return __self.format_string(fmt, args, kwargs, __obj)
389
390        # the double prefixes are to avoid double keyword argument
391        # errors when proxying the call.
392        if not __self.is_safe_callable(__obj):
393            raise SecurityError(f"{__obj!r} is not safely callable")
394        return __context.call(__obj, *args, **kwargs)

Call an object from sandboxed code.

class ImmutableSandboxedEnvironment(SandboxedEnvironment):
397class ImmutableSandboxedEnvironment(SandboxedEnvironment):
398    """Works exactly like the regular `SandboxedEnvironment` but does not
399    permit modifications on the builtin mutable objects `list`, `set`, and
400    `dict` by using the :func:`modifies_known_mutable` function.
401    """
402
403    def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
404        if not super().is_safe_attribute(obj, attr, value):
405            return False
406
407        return not modifies_known_mutable(obj, attr)

Works exactly like the regular SandboxedEnvironment but does not permit modifications on the builtin mutable objects list, set, and dict by using the modifies_known_mutable() function.

def is_safe_attribute(self, obj: Any, attr: str, value: Any) -> bool:
403    def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
404        if not super().is_safe_attribute(obj, attr, value):
405            return False
406
407        return not modifies_known_mutable(obj, attr)

The sandboxed environment will call this method to check if the attribute of an object is safe to access. Per default all attributes starting with an underscore are considered private as well as the special attributes of internal python objects as returned by the is_internal_attribute() function.

class SandboxedFormatter(string.Formatter):
410class SandboxedFormatter(Formatter):
411    def __init__(self, env: Environment, **kwargs: t.Any) -> None:
412        self._env = env
413        super().__init__(**kwargs)
414
415    def get_field(
416        self, field_name: str, args: t.Sequence[t.Any], kwargs: t.Mapping[str, t.Any]
417    ) -> t.Tuple[t.Any, str]:
418        first, rest = formatter_field_name_split(field_name)
419        obj = self.get_value(first, args, kwargs)
420        for is_attr, i in rest:
421            if is_attr:
422                obj = self._env.getattr(obj, i)
423            else:
424                obj = self._env.getitem(obj, i)
425        return obj, first
SandboxedFormatter(env: jinja2.environment.Environment, **kwargs: Any)
411    def __init__(self, env: Environment, **kwargs: t.Any) -> None:
412        self._env = env
413        super().__init__(**kwargs)
def get_field( self, field_name: str, args: Sequence[Any], kwargs: Mapping[str, Any]) -> Tuple[Any, str]:
415    def get_field(
416        self, field_name: str, args: t.Sequence[t.Any], kwargs: t.Mapping[str, t.Any]
417    ) -> t.Tuple[t.Any, str]:
418        first, rest = formatter_field_name_split(field_name)
419        obj = self.get_value(first, args, kwargs)
420        for is_attr, i in rest:
421            if is_attr:
422                obj = self._env.getattr(obj, i)
423            else:
424                obj = self._env.getitem(obj, i)
425        return obj, first
Inherited Members
string.Formatter
format
vformat
get_value
check_unused_args
format_field
convert_field
parse
class SandboxedEscapeFormatter(SandboxedFormatter, markupsafe.EscapeFormatter):
428class SandboxedEscapeFormatter(SandboxedFormatter, EscapeFormatter):
429    pass
Inherited Members
SandboxedFormatter
SandboxedFormatter
get_field
markupsafe.EscapeFormatter
escape
format_field
string.Formatter
format
vformat
get_value
check_unused_args
convert_field
parse