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