sandbox.py 15 KB

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