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

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

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

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

Return an undefined object for unsafe attributes.

def wrap_str_format(self, value: Any) -> Optional[Callable[..., str]]:
345    def wrap_str_format(self, value: t.Any) -> t.Optional[t.Callable[..., str]]:
346        """If the given value is a ``str.format`` or ``str.format_map`` method,
347        return a new function than handles sandboxing. This is done at access
348        rather than in :meth:`call`, so that calls made without ``call`` are
349        also sandboxed.
350        """
351        if not isinstance(
352            value, (types.MethodType, types.BuiltinMethodType)
353        ) or value.__name__ not in ("format", "format_map"):
354            return None
355
356        f_self: t.Any = value.__self__
357
358        if not isinstance(f_self, str):
359            return None
360
361        str_type: t.Type[str] = type(f_self)
362        is_format_map = value.__name__ == "format_map"
363        formatter: SandboxedFormatter
364
365        if isinstance(f_self, Markup):
366            formatter = SandboxedEscapeFormatter(self, escape=f_self.escape)
367        else:
368            formatter = SandboxedFormatter(self)
369
370        vformat = formatter.vformat
371
372        def wrapper(*args: t.Any, **kwargs: t.Any) -> str:
373            if is_format_map:
374                if kwargs:
375                    raise TypeError("format_map() takes no keyword arguments")
376
377                if len(args) != 1:
378                    raise TypeError(
379                        f"format_map() takes exactly one argument ({len(args)} given)"
380                    )
381
382                kwargs = args[0]
383                args = ()
384
385            return str_type(vformat(f_self, args, kwargs))
386
387        return update_wrapper(wrapper, value)

If the given value is a str.format or str.format_map method, return a new function than handles sandboxing. This is done at access rather than in call(), so that calls made without call are also sandboxed.

def call( _SandboxedEnvironment__self, _SandboxedEnvironment__context: jinja2.runtime.Context, _SandboxedEnvironment__obj: Any, *args: Any, **kwargs: Any) -> Any:
389    def call(
390        __self,  # noqa: B902
391        __context: Context,
392        __obj: t.Any,
393        *args: t.Any,
394        **kwargs: t.Any,
395    ) -> t.Any:
396        """Call an object from sandboxed code."""
397
398        # the double prefixes are to avoid double keyword argument
399        # errors when proxying the call.
400        if not __self.is_safe_callable(__obj):
401            raise SecurityError(f"{__obj!r} is not safely callable")
402        return __context.call(__obj, *args, **kwargs)

Call an object from sandboxed code.

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