summaryrefslogtreecommitdiff
path: root/venv/lib/python3.11/site-packages/jinja2/sandbox.py
diff options
context:
space:
mode:
Diffstat (limited to 'venv/lib/python3.11/site-packages/jinja2/sandbox.py')
-rw-r--r--venv/lib/python3.11/site-packages/jinja2/sandbox.py428
1 files changed, 0 insertions, 428 deletions
diff --git a/venv/lib/python3.11/site-packages/jinja2/sandbox.py b/venv/lib/python3.11/site-packages/jinja2/sandbox.py
deleted file mode 100644
index 06d7414..0000000
--- a/venv/lib/python3.11/site-packages/jinja2/sandbox.py
+++ /dev/null
@@ -1,428 +0,0 @@
-"""A sandbox layer that ensures unsafe operations cannot be performed.
-Useful when the template itself comes from an untrusted source.
-"""
-import operator
-import types
-import typing as t
-from _string import formatter_field_name_split # type: ignore
-from collections import abc
-from collections import deque
-from string import Formatter
-
-from markupsafe import EscapeFormatter
-from markupsafe import Markup
-
-from .environment import Environment
-from .exceptions import SecurityError
-from .runtime import Context
-from .runtime import Undefined
-
-F = t.TypeVar("F", bound=t.Callable[..., t.Any])
-
-#: maximum number of items a range may produce
-MAX_RANGE = 100000
-
-#: Unsafe function attributes.
-UNSAFE_FUNCTION_ATTRIBUTES: t.Set[str] = set()
-
-#: Unsafe method attributes. Function attributes are unsafe for methods too.
-UNSAFE_METHOD_ATTRIBUTES: t.Set[str] = set()
-
-#: unsafe generator attributes.
-UNSAFE_GENERATOR_ATTRIBUTES = {"gi_frame", "gi_code"}
-
-#: unsafe attributes on coroutines
-UNSAFE_COROUTINE_ATTRIBUTES = {"cr_frame", "cr_code"}
-
-#: unsafe attributes on async generators
-UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = {"ag_code", "ag_frame"}
-
-_mutable_spec: t.Tuple[t.Tuple[t.Type, t.FrozenSet[str]], ...] = (
- (
- abc.MutableSet,
- frozenset(
- [
- "add",
- "clear",
- "difference_update",
- "discard",
- "pop",
- "remove",
- "symmetric_difference_update",
- "update",
- ]
- ),
- ),
- (
- abc.MutableMapping,
- frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
- ),
- (
- abc.MutableSequence,
- frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
- ),
- (
- deque,
- frozenset(
- [
- "append",
- "appendleft",
- "clear",
- "extend",
- "extendleft",
- "pop",
- "popleft",
- "remove",
- "rotate",
- ]
- ),
- ),
-)
-
-
-def inspect_format_method(callable: t.Callable) -> t.Optional[str]:
- if not isinstance(
- callable, (types.MethodType, types.BuiltinMethodType)
- ) or callable.__name__ not in ("format", "format_map"):
- return None
-
- obj = callable.__self__
-
- if isinstance(obj, str):
- return obj
-
- return None
-
-
-def safe_range(*args: int) -> range:
- """A range that can't generate ranges with a length of more than
- MAX_RANGE items.
- """
- rng = range(*args)
-
- if len(rng) > MAX_RANGE:
- raise OverflowError(
- "Range too big. The sandbox blocks ranges larger than"
- f" MAX_RANGE ({MAX_RANGE})."
- )
-
- return rng
-
-
-def unsafe(f: F) -> F:
- """Marks a function or method as unsafe.
-
- .. code-block: python
-
- @unsafe
- def delete(self):
- pass
- """
- f.unsafe_callable = True # type: ignore
- return f
-
-
-def is_internal_attribute(obj: t.Any, attr: str) -> bool:
- """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
- :meth:`~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
- """
- if isinstance(obj, types.FunctionType):
- if attr in UNSAFE_FUNCTION_ATTRIBUTES:
- return True
- elif isinstance(obj, types.MethodType):
- if attr in UNSAFE_FUNCTION_ATTRIBUTES or attr in UNSAFE_METHOD_ATTRIBUTES:
- return True
- elif isinstance(obj, type):
- if attr == "mro":
- return True
- elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)):
- return True
- elif isinstance(obj, types.GeneratorType):
- if attr in UNSAFE_GENERATOR_ATTRIBUTES:
- return True
- elif hasattr(types, "CoroutineType") and isinstance(obj, types.CoroutineType):
- if attr in UNSAFE_COROUTINE_ATTRIBUTES:
- return True
- elif hasattr(types, "AsyncGeneratorType") and isinstance(
- obj, types.AsyncGeneratorType
- ):
- if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
- return True
- return attr.startswith("__")
-
-
-def modifies_known_mutable(obj: t.Any, attr: str) -> bool:
- """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
- """
- for typespec, unsafe in _mutable_spec:
- if isinstance(obj, typespec):
- return attr in unsafe
- return False
-
-
-class SandboxedEnvironment(Environment):
- """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 :exc:`SecurityError` is
- raised. However also other exceptions may occur during the rendering so
- the caller has to ensure that all exceptions are caught.
- """
-
- sandboxed = True
-
- #: default callback table for the binary operators. A copy of this is
- #: available on each instance of a sandboxed environment as
- #: :attr:`binop_table`
- default_binop_table: t.Dict[str, t.Callable[[t.Any, t.Any], t.Any]] = {
- "+": operator.add,
- "-": operator.sub,
- "*": operator.mul,
- "/": operator.truediv,
- "//": operator.floordiv,
- "**": operator.pow,
- "%": operator.mod,
- }
-
- #: default callback table for the unary operators. A copy of this is
- #: available on each instance of a sandboxed environment as
- #: :attr:`unop_table`
- default_unop_table: t.Dict[str, t.Callable[[t.Any], t.Any]] = {
- "+": operator.pos,
- "-": operator.neg,
- }
-
- #: a set of binary operators that should be intercepted. Each operator
- #: that is added to this set (empty by default) is delegated to the
- #: :meth:`call_binop` method that will perform the operator. The default
- #: operator callback is specified by :attr:`binop_table`.
- #:
- #: The following binary operators are interceptable:
- #: ``//``, ``%``, ``+``, ``*``, ``-``, ``/``, and ``**``
- #:
- #: The default operation form the operator table corresponds to the
- #: builtin function. Intercepted calls are always slower than the native
- #: operator call, so make sure only to intercept the ones you are
- #: interested in.
- #:
- #: .. versionadded:: 2.6
- intercepted_binops: t.FrozenSet[str] = frozenset()
-
- #: a set of unary operators that should be intercepted. Each operator
- #: that is added to this set (empty by default) is delegated to the
- #: :meth:`call_unop` method that will perform the operator. The default
- #: operator callback is specified by :attr:`unop_table`.
- #:
- #: The following unary operators are interceptable: ``+``, ``-``
- #:
- #: The default operation form the operator table corresponds to the
- #: builtin function. Intercepted calls are always slower than the native
- #: operator call, so make sure only to intercept the ones you are
- #: interested in.
- #:
- #: .. versionadded:: 2.6
- intercepted_unops: t.FrozenSet[str] = frozenset()
-
- def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
- super().__init__(*args, **kwargs)
- self.globals["range"] = safe_range
- self.binop_table = self.default_binop_table.copy()
- self.unop_table = self.default_unop_table.copy()
-
- def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
- """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
- :func:`is_internal_attribute` function.
- """
- return not (attr.startswith("_") or is_internal_attribute(obj, attr))
-
- def is_safe_callable(self, obj: t.Any) -> bool:
- """Check if an object is safely callable. By default callables
- are considered safe unless decorated with :func:`unsafe`.
-
- This also recognizes the Django convention of setting
- ``func.alters_data = True``.
- """
- return not (
- getattr(obj, "unsafe_callable", False) or getattr(obj, "alters_data", False)
- )
-
- def call_binop(
- self, context: Context, operator: str, left: t.Any, right: t.Any
- ) -> t.Any:
- """For intercepted binary operator calls (:meth:`intercepted_binops`)
- this function is executed instead of the builtin operator. This can
- be used to fine tune the behavior of certain operators.
-
- .. versionadded:: 2.6
- """
- return self.binop_table[operator](left, right)
-
- def call_unop(self, context: Context, operator: str, arg: t.Any) -> t.Any:
- """For intercepted unary operator calls (:meth:`intercepted_unops`)
- this function is executed instead of the builtin operator. This can
- be used to fine tune the behavior of certain operators.
-
- .. versionadded:: 2.6
- """
- return self.unop_table[operator](arg)
-
- def getitem(
- self, obj: t.Any, argument: t.Union[str, t.Any]
- ) -> t.Union[t.Any, Undefined]:
- """Subscribe an object from sandboxed code."""
- try:
- return obj[argument]
- except (TypeError, LookupError):
- if isinstance(argument, str):
- try:
- attr = str(argument)
- except Exception:
- pass
- else:
- try:
- value = getattr(obj, attr)
- except AttributeError:
- pass
- else:
- if self.is_safe_attribute(obj, argument, value):
- return value
- return self.unsafe_undefined(obj, argument)
- return self.undefined(obj=obj, name=argument)
-
- def getattr(self, obj: t.Any, attribute: str) -> t.Union[t.Any, Undefined]:
- """Subscribe an object from sandboxed code and prefer the
- attribute. The attribute passed *must* be a bytestring.
- """
- try:
- value = getattr(obj, attribute)
- except AttributeError:
- try:
- return obj[attribute]
- except (TypeError, LookupError):
- pass
- else:
- if self.is_safe_attribute(obj, attribute, value):
- return value
- return self.unsafe_undefined(obj, attribute)
- return self.undefined(obj=obj, name=attribute)
-
- def unsafe_undefined(self, obj: t.Any, attribute: str) -> Undefined:
- """Return an undefined object for unsafe attributes."""
- return self.undefined(
- f"access to attribute {attribute!r} of"
- f" {type(obj).__name__!r} object is unsafe.",
- name=attribute,
- obj=obj,
- exc=SecurityError,
- )
-
- def format_string(
- self,
- s: str,
- args: t.Tuple[t.Any, ...],
- kwargs: t.Dict[str, t.Any],
- format_func: t.Optional[t.Callable] = None,
- ) -> str:
- """If a format call is detected, then this is routed through this
- method so that our safety sandbox can be used for it.
- """
- formatter: SandboxedFormatter
- if isinstance(s, Markup):
- formatter = SandboxedEscapeFormatter(self, escape=s.escape)
- else:
- formatter = SandboxedFormatter(self)
-
- if format_func is not None and format_func.__name__ == "format_map":
- if len(args) != 1 or kwargs:
- raise TypeError(
- "format_map() takes exactly one argument"
- f" {len(args) + (kwargs is not None)} given"
- )
-
- kwargs = args[0]
- args = ()
-
- rv = formatter.vformat(s, args, kwargs)
- return type(s)(rv)
-
- def call(
- __self, # noqa: B902
- __context: Context,
- __obj: t.Any,
- *args: t.Any,
- **kwargs: t.Any,
- ) -> t.Any:
- """Call an object from sandboxed code."""
- fmt = inspect_format_method(__obj)
- if fmt is not None:
- return __self.format_string(fmt, args, kwargs, __obj)
-
- # the double prefixes are to avoid double keyword argument
- # errors when proxying the call.
- if not __self.is_safe_callable(__obj):
- raise SecurityError(f"{__obj!r} is not safely callable")
- return __context.call(__obj, *args, **kwargs)
-
-
-class ImmutableSandboxedEnvironment(SandboxedEnvironment):
- """Works exactly like the regular `SandboxedEnvironment` but does not
- permit modifications on the builtin mutable objects `list`, `set`, and
- `dict` by using the :func:`modifies_known_mutable` function.
- """
-
- def is_safe_attribute(self, obj: t.Any, attr: str, value: t.Any) -> bool:
- if not super().is_safe_attribute(obj, attr, value):
- return False
-
- return not modifies_known_mutable(obj, attr)
-
-
-class SandboxedFormatter(Formatter):
- def __init__(self, env: Environment, **kwargs: t.Any) -> None:
- self._env = env
- super().__init__(**kwargs)
-
- def get_field(
- self, field_name: str, args: t.Sequence[t.Any], kwargs: t.Mapping[str, t.Any]
- ) -> t.Tuple[t.Any, str]:
- first, rest = formatter_field_name_split(field_name)
- obj = self.get_value(first, args, kwargs)
- for is_attr, i in rest:
- if is_attr:
- obj = self._env.getattr(obj, i)
- else:
- obj = self._env.getitem(obj, i)
- return obj, first
-
-
-class SandboxedEscapeFormatter(SandboxedFormatter, EscapeFormatter):
- pass