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"""
  4
  5import operator
  6import types
  7import typing as t
  8from collections import abc
  9from collections import deque
 10from string import Formatter
 11
 12from _string import formatter_field_name_split  # type: ignore
 13from markupsafe import EscapeFormatter
 14from markupsafe import Markup
 15
 16from .environment import Environment
 17from .exceptions import SecurityError
 18from .runtime import Context
 19from .runtime import Undefined
 20
 21F = t.TypeVar("F", bound=t.Callable[..., t.Any])
 22
 23#: maximum number of items a range may produce
 24MAX_RANGE = 100000
 25
 26#: Unsafe function attributes.
 27UNSAFE_FUNCTION_ATTRIBUTES: t.Set[str] = set()
 28
 29#: Unsafe method attributes. Function attributes are unsafe for methods too.
 30UNSAFE_METHOD_ATTRIBUTES: t.Set[str] = set()
 31
 32#: unsafe generator attributes.
 33UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
 34
 35#: unsafe attributes on coroutines
 36UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
 37
 38#: unsafe attributes on async generators
 39UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
 40
 41_mutable_spec: t.Tuple[t.Tuple[t.Type[t.Any], t.FrozenSet[str]], ...] = (
 42    (
 43        abc.MutableSet,
 44        frozenset(
 45            [
 46                "add",
 47                "clear",
 48                "difference_update",
 49                "discard",
 50                "pop",
 51                "remove",
 52                "symmetric_difference_update",
 53                "update",
 54            ]
 55        ),
 56    ),
 57    (
 58        abc.MutableMapping,
 59        frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
 60    ),
 61    (
 62        abc.MutableSequence,
 63        frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
 64    ),
 65    (
 66        deque,
 67        frozenset(
 68            [
 69                "append",
 70                "appendleft",
 71                "clear",
 72                "extend",
 73                "extendleft",
 74                "pop",
 75                "popleft",
 76                "remove",
 77                "rotate",
 78            ]
 79        ),
 80    ),
 81)
 82
 83
 84def inspect_format_method(callable: t.Callable[..., t.Any]) -> 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
 96
 97
 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
111
112
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
124
125
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("__")
161
162
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
186
187
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[..., t.Any]] = 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)
395
396
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)
408
409
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
426
427
428class SandboxedEscapeFormatter(SandboxedFormatter, EscapeFormatter):
429    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_code', 'cr_frame'}
UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {'ag_frame', 'ag_code'}
def inspect_format_method(callable: Callable[..., Any]) -> Optional[str]:
85def inspect_format_method(callable: t.Callable[..., t.Any]) -> t.Optional[str]:
86    if not isinstance(
87        callable, (types.MethodType, types.BuiltinMethodType)
88    ) or callable.__name__ not in ("format", "format_map"):
89        return None
90
91    obj = callable.__self__
92
93    if isinstance(obj, str):
94        return obj
95
96    return None
def safe_range(*args: int) -> range:
 99def safe_range(*args: int) -> range:
100    """A range that can't generate ranges with a length of more than
101    MAX_RANGE items.
102    """
103    rng = range(*args)
104
105    if len(rng) > MAX_RANGE:
106        raise OverflowError(
107            "Range too big. The sandbox blocks ranges larger than"
108            f" MAX_RANGE ({MAX_RANGE})."
109        )
110
111    return rng

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

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

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:
280    def call_binop(
281        self, context: Context, operator: str, left: t.Any, right: t.Any
282    ) -> t.Any:
283        """For intercepted binary operator calls (:meth:`intercepted_binops`)
284        this function is executed instead of the builtin operator.  This can
285        be used to fine tune the behavior of certain operators.
286
287        .. versionadded:: 2.6
288        """
289        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:
291    def call_unop(self, context: Context, operator: str, arg: t.Any) -> t.Any:
292        """For intercepted unary operator calls (:meth:`intercepted_unops`)
293        this function is executed instead of the builtin operator.  This can
294        be used to fine tune the behavior of certain operators.
295
296        .. versionadded:: 2.6
297        """
298        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]:
300    def getitem(
301        self, obj: t.Any, argument: t.Union[str, t.Any]
302    ) -> t.Union[t.Any, Undefined]:
303        """Subscribe an object from sandboxed code."""
304        try:
305            return obj[argument]
306        except (TypeError, LookupError):
307            if isinstance(argument, str):
308                try:
309                    attr = str(argument)
310                except Exception:
311                    pass
312                else:
313                    try:
314                        value = getattr(obj, attr)
315                    except AttributeError:
316                        pass
317                    else:
318                        if self.is_safe_attribute(obj, argument, value):
319                            return value
320                        return self.unsafe_undefined(obj, argument)
321        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]:
323    def getattr(self, obj: t.Any, attribute: str) -> t.Union[t.Any, Undefined]:
324        """Subscribe an object from sandboxed code and prefer the
325        attribute.  The attribute passed *must* be a bytestring.
326        """
327        try:
328            value = getattr(obj, attribute)
329        except AttributeError:
330            try:
331                return obj[attribute]
332            except (TypeError, LookupError):
333                pass
334        else:
335            if self.is_safe_attribute(obj, attribute, value):
336                return value
337            return self.unsafe_undefined(obj, attribute)
338        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:
340    def unsafe_undefined(self, obj: t.Any, attribute: str) -> Undefined:
341        """Return an undefined object for unsafe attributes."""
342        return self.undefined(
343            f"access to attribute {attribute!r} of"
344            f" {type(obj).__name__!r} object is unsafe.",
345            name=attribute,
346            obj=obj,
347            exc=SecurityError,
348        )

Return an undefined object for unsafe attributes.

def format_string( self, s: str, args: Tuple[Any, ...], kwargs: Dict[str, Any], format_func: Optional[Callable[..., Any]] = None) -> str:
350    def format_string(
351        self,
352        s: str,
353        args: t.Tuple[t.Any, ...],
354        kwargs: t.Dict[str, t.Any],
355        format_func: t.Optional[t.Callable[..., t.Any]] = None,
356    ) -> str:
357        """If a format call is detected, then this is routed through this
358        method so that our safety sandbox can be used for it.
359        """
360        formatter: SandboxedFormatter
361        if isinstance(s, Markup):
362            formatter = SandboxedEscapeFormatter(self, escape=s.escape)
363        else:
364            formatter = SandboxedFormatter(self)
365
366        if format_func is not None and format_func.__name__ == "format_map":
367            if len(args) != 1 or kwargs:
368                raise TypeError(
369                    "format_map() takes exactly one argument"
370                    f" {len(args) + (kwargs is not None)} given"
371                )
372
373            kwargs = args[0]
374            args = ()
375
376        rv = formatter.vformat(s, args, kwargs)
377        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:
379    def call(
380        __self,  # noqa: B902
381        __context: Context,
382        __obj: t.Any,
383        *args: t.Any,
384        **kwargs: t.Any,
385    ) -> t.Any:
386        """Call an object from sandboxed code."""
387        fmt = inspect_format_method(__obj)
388        if fmt is not None:
389            return __self.format_string(fmt, args, kwargs, __obj)
390
391        # the double prefixes are to avoid double keyword argument
392        # errors when proxying the call.
393        if not __self.is_safe_callable(__obj):
394            raise SecurityError(f"{__obj!r} is not safely callable")
395        return __context.call(__obj, *args, **kwargs)

Call an object from sandboxed code.

class ImmutableSandboxedEnvironment(SandboxedEnvironment):
398class ImmutableSandboxedEnvironment(SandboxedEnvironment):
399    """Works exactly like the regular `SandboxedEnvironment` but does not
400    permit modifications on the builtin mutable objects `list`, `set`, and
401    `dict` by using the :func:`modifies_known_mutable` function.
402    """
403
404    def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
405        if not super().is_safe_attribute(obj, attr, value):
406            return False
407
408        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:
404    def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
405        if not super().is_safe_attribute(obj, attr, value):
406            return False
407
408        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):
411class SandboxedFormatter(Formatter):
412    def __init__(self, env: Environment, **kwargs: t.Any) -> None:
413        self._env = env
414        super().__init__(**kwargs)
415
416    def get_field(
417        self, field_name: str, args: t.Sequence[t.Any], kwargs: t.Mapping[str, t.Any]
418    ) -> t.Tuple[t.Any, str]:
419        first, rest = formatter_field_name_split(field_name)
420        obj = self.get_value(first, args, kwargs)
421        for is_attr, i in rest:
422            if is_attr:
423                obj = self._env.getattr(obj, i)
424            else:
425                obj = self._env.getitem(obj, i)
426        return obj, first
SandboxedFormatter(env: jinja2.environment.Environment, **kwargs: Any)
412    def __init__(self, env: Environment, **kwargs: t.Any) -> None:
413        self._env = env
414        super().__init__(**kwargs)
def get_field( self, field_name: str, args: Sequence[Any], kwargs: Mapping[str, Any]) -> Tuple[Any, str]:
416    def get_field(
417        self, field_name: str, args: t.Sequence[t.Any], kwargs: t.Mapping[str, t.Any]
418    ) -> t.Tuple[t.Any, str]:
419        first, rest = formatter_field_name_split(field_name)
420        obj = self.get_value(first, args, kwargs)
421        for is_attr, i in rest:
422            if is_attr:
423                obj = self._env.getattr(obj, i)
424            else:
425                obj = self._env.getitem(obj, i)
426        return obj, first
Inherited Members
string.Formatter
format
vformat
get_value
check_unused_args
format_field
convert_field
parse
class SandboxedEscapeFormatter(SandboxedFormatter, markupsafe.EscapeFormatter):
429class SandboxedEscapeFormatter(SandboxedFormatter, EscapeFormatter):
430    pass
Inherited Members
SandboxedFormatter
SandboxedFormatter
get_field
markupsafe.EscapeFormatter
escape
format_field
string.Formatter
format
vformat
get_value
check_unused_args
convert_field
parse