You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

429 lines
14KB

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