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