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