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