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