| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510 | 
							- # -*- coding: utf-8 -*-
 
- """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 warnings
 
- from collections import deque
 
- from string import Formatter
 
- from markupsafe import EscapeFormatter
 
- from markupsafe import Markup
 
- from ._compat import abc
 
- from ._compat import PY2
 
- from ._compat import range_type
 
- from ._compat import string_types
 
- from .environment import Environment
 
- from .exceptions import SecurityError
 
- #: maximum number of items a range may produce
 
- MAX_RANGE = 100000
 
- #: attributes of function objects that are considered unsafe.
 
- if PY2:
 
-     UNSAFE_FUNCTION_ATTRIBUTES = {
 
-         "func_closure",
 
-         "func_code",
 
-         "func_dict",
 
-         "func_defaults",
 
-         "func_globals",
 
-     }
 
- else:
 
-     # On versions > python 2 the special attributes on functions are gone,
 
-     # but they remain on methods and generators for whatever reason.
 
-     UNSAFE_FUNCTION_ATTRIBUTES = set()
 
- #: unsafe method attributes.  function attributes are unsafe for methods too
 
- UNSAFE_METHOD_ATTRIBUTES = {"im_class", "im_func", "im_self"}
 
- #: 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"}
 
- # make sure we don't warn in python 2.6 about stuff we don't care about
 
- warnings.filterwarnings(
 
-     "ignore", "the sets module", DeprecationWarning, module=__name__
 
- )
 
- _mutable_set_types = (set,)
 
- _mutable_mapping_types = (dict,)
 
- _mutable_sequence_types = (list,)
 
- # on python 2.x we can register the user collection types
 
- try:
 
-     from UserDict import UserDict, DictMixin
 
-     from UserList import UserList
 
-     _mutable_mapping_types += (UserDict, DictMixin)
 
-     _mutable_set_types += (UserList,)
 
- except ImportError:
 
-     pass
 
- # if sets is still available, register the mutable set from there as well
 
- try:
 
-     from sets import Set
 
-     _mutable_set_types += (Set,)
 
- except ImportError:
 
-     pass
 
- #: register Python 2.6 abstract base classes
 
- _mutable_set_types += (abc.MutableSet,)
 
- _mutable_mapping_types += (abc.MutableMapping,)
 
- _mutable_sequence_types += (abc.MutableSequence,)
 
- _mutable_spec = (
 
-     (
 
-         _mutable_set_types,
 
-         frozenset(
 
-             [
 
-                 "add",
 
-                 "clear",
 
-                 "difference_update",
 
-                 "discard",
 
-                 "pop",
 
-                 "remove",
 
-                 "symmetric_difference_update",
 
-                 "update",
 
-             ]
 
-         ),
 
-     ),
 
-     (
 
-         _mutable_mapping_types,
 
-         frozenset(["clear", "pop", "popitem", "setdefault", "update"]),
 
-     ),
 
-     (
 
-         _mutable_sequence_types,
 
-         frozenset(["append", "reverse", "insert", "sort", "extend", "remove"]),
 
-     ),
 
-     (
 
-         deque,
 
-         frozenset(
 
-             [
 
-                 "append",
 
-                 "appendleft",
 
-                 "clear",
 
-                 "extend",
 
-                 "extendleft",
 
-                 "pop",
 
-                 "popleft",
 
-                 "remove",
 
-                 "rotate",
 
-             ]
 
-         ),
 
-     ),
 
- )
 
- class _MagicFormatMapping(abc.Mapping):
 
-     """This class implements a dummy wrapper to fix a bug in the Python
 
-     standard library for string formatting.
 
-     See https://bugs.python.org/issue13598 for information about why
 
-     this is necessary.
 
-     """
 
-     def __init__(self, args, kwargs):
 
-         self._args = args
 
-         self._kwargs = kwargs
 
-         self._last_index = 0
 
-     def __getitem__(self, key):
 
-         if key == "":
 
-             idx = self._last_index
 
-             self._last_index += 1
 
-             try:
 
-                 return self._args[idx]
 
-             except LookupError:
 
-                 pass
 
-             key = str(idx)
 
-         return self._kwargs[key]
 
-     def __iter__(self):
 
-         return iter(self._kwargs)
 
-     def __len__(self):
 
-         return len(self._kwargs)
 
- def inspect_format_method(callable):
 
-     if not isinstance(
 
-         callable, (types.MethodType, types.BuiltinMethodType)
 
-     ) or callable.__name__ not in ("format", "format_map"):
 
-         return None
 
-     obj = callable.__self__
 
-     if isinstance(obj, string_types):
 
-         return obj
 
- def safe_range(*args):
 
-     """A range that can't generate ranges with a length of more than
 
-     MAX_RANGE items.
 
-     """
 
-     rng = range_type(*args)
 
-     if len(rng) > MAX_RANGE:
 
-         raise OverflowError(
 
-             "Range too big. The sandbox blocks ranges larger than"
 
-             " MAX_RANGE (%d)." % MAX_RANGE
 
-         )
 
-     return rng
 
- def unsafe(f):
 
-     """Marks a function or method as unsafe.
 
-     ::
 
-         @unsafe
 
-         def delete(self):
 
-             pass
 
-     """
 
-     f.unsafe_callable = True
 
-     return f
 
- def is_internal_attribute(obj, attr):
 
-     """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, attr):
 
-     """This function checks if an attribute on a builtin mutable object
 
-     (list, dict, set or deque) would modify it if called.  It also supports
 
-     the "user"-versions of the objects (`sets.Set`, `UserDict.*` etc.) and
 
-     with Python 2.6 onwards the abstract base classes `MutableSet`,
 
-     `MutableMapping`, and `MutableSequence`.
 
-     >>> 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 (such as unicode) `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 = {
 
-         "+": 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 = {"+": 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 = 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 = frozenset()
 
-     def intercept_unop(self, operator):
 
-         """Called during template compilation with the name of a unary
 
-         operator to check if it should be intercepted at runtime.  If this
 
-         method returns `True`, :meth:`call_unop` is executed for this unary
 
-         operator.  The default implementation of :meth:`call_unop` will use
 
-         the :attr:`unop_table` dictionary to perform the operator with the
 
-         same logic as the builtin one.
 
-         The following unary operators are interceptable: ``+`` and ``-``
 
-         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
 
-         """
 
-         return False
 
-     def __init__(self, *args, **kwargs):
 
-         Environment.__init__(self, *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, attr, value):
 
-         """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):
 
-         """Check if an object is safely callable.  Per default a function is
 
-         considered safe unless the `unsafe_callable` attribute exists and is
 
-         True.  Override this method to alter the behavior, but this won't
 
-         affect the `unsafe` decorator from this module.
 
-         """
 
-         return not (
 
-             getattr(obj, "unsafe_callable", False) or getattr(obj, "alters_data", False)
 
-         )
 
-     def call_binop(self, context, operator, left, right):
 
-         """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, operator, arg):
 
-         """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, argument):
 
-         """Subscribe an object from sandboxed code."""
 
-         try:
 
-             return obj[argument]
 
-         except (TypeError, LookupError):
 
-             if isinstance(argument, string_types):
 
-                 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, attribute):
 
-         """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, attribute):
 
-         """Return an undefined object for unsafe attributes."""
 
-         return self.undefined(
 
-             "access to attribute %r of %r "
 
-             "object is unsafe." % (attribute, obj.__class__.__name__),
 
-             name=attribute,
 
-             obj=obj,
 
-             exc=SecurityError,
 
-         )
 
-     def format_string(self, s, args, kwargs, format_func=None):
 
-         """If a format call is detected, then this is routed through this
 
-         method so that our safety sandbox can be used for it.
 
-         """
 
-         if isinstance(s, Markup):
 
-             formatter = SandboxedEscapeFormatter(self, 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 %d given"
 
-                     % (len(args) + (kwargs is not None))
 
-                 )
 
-             kwargs = args[0]
 
-             args = None
 
-         kwargs = _MagicFormatMapping(args, kwargs)
 
-         rv = formatter.vformat(s, args, kwargs)
 
-         return type(s)(rv)
 
-     def call(__self, __context, __obj, *args, **kwargs):  # noqa: B902
 
-         """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("%r is not safely callable" % (__obj,))
 
-         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, attr, value):
 
-         if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
 
-             return False
 
-         return not modifies_known_mutable(obj, attr)
 
- # This really is not a public API apparently.
 
- try:
 
-     from _string import formatter_field_name_split
 
- except ImportError:
 
-     def formatter_field_name_split(field_name):
 
-         return field_name._formatter_field_name_split()
 
- class SandboxedFormatterMixin(object):
 
-     def __init__(self, env):
 
-         self._env = env
 
-     def get_field(self, field_name, args, kwargs):
 
-         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 SandboxedFormatter(SandboxedFormatterMixin, Formatter):
 
-     def __init__(self, env):
 
-         SandboxedFormatterMixin.__init__(self, env)
 
-         Formatter.__init__(self)
 
- class SandboxedEscapeFormatter(SandboxedFormatterMixin, EscapeFormatter):
 
-     def __init__(self, env, escape):
 
-         SandboxedFormatterMixin.__init__(self, env)
 
-         EscapeFormatter.__init__(self, escape)
 
 
  |