Blame SOURCES/python-jinja2-fix-CVE-2016-10745.patch

889252
diff --git a/jinja2/_compat.py b/jinja2/_compat.py
889252
new file mode 100644
889252
index 0000000..4dbf6ea
889252
--- /dev/null
889252
+++ b/jinja2/_compat.py
889252
@@ -0,0 +1,105 @@
889252
+# -*- coding: utf-8 -*-
889252
+"""
889252
+    jinja2._compat
889252
+    ~~~~~~~~~~~~~~
889252
+
889252
+    Some py2/py3 compatibility support based on a stripped down
889252
+    version of six so we don't have to depend on a specific version
889252
+    of it.
889252
+
889252
+    :copyright: Copyright 2013 by the Jinja team, see AUTHORS.
889252
+    :license: BSD, see LICENSE for details.
889252
+"""
889252
+import sys
889252
+
889252
+PY2 = sys.version_info[0] == 2
889252
+PYPY = hasattr(sys, 'pypy_translation_info')
889252
+_identity = lambda x: x
889252
+
889252
+
889252
+if not PY2:
889252
+    unichr = chr
889252
+    range_type = range
889252
+    text_type = str
889252
+    string_types = (str,)
889252
+    integer_types = (int,)
889252
+
889252
+    iterkeys = lambda d: iter(d.keys())
889252
+    itervalues = lambda d: iter(d.values())
889252
+    iteritems = lambda d: iter(d.items())
889252
+
889252
+    import pickle
889252
+    from io import BytesIO, StringIO
889252
+    NativeStringIO = StringIO
889252
+
889252
+    def reraise(tp, value, tb=None):
889252
+        if value.__traceback__ is not tb:
889252
+            raise value.with_traceback(tb)
889252
+        raise value
889252
+
889252
+    ifilter = filter
889252
+    imap = map
889252
+    izip = zip
889252
+    intern = sys.intern
889252
+
889252
+    implements_iterator = _identity
889252
+    implements_to_string = _identity
889252
+    encode_filename = _identity
889252
+
889252
+else:
889252
+    unichr = unichr
889252
+    text_type = unicode
889252
+    range_type = xrange
889252
+    string_types = (str, unicode)
889252
+    integer_types = (int, long)
889252
+
889252
+    iterkeys = lambda d: d.iterkeys()
889252
+    itervalues = lambda d: d.itervalues()
889252
+    iteritems = lambda d: d.iteritems()
889252
+
889252
+    import cPickle as pickle
889252
+    from cStringIO import StringIO as BytesIO, StringIO
889252
+    NativeStringIO = BytesIO
889252
+
889252
+    exec('def reraise(tp, value, tb=None):\n raise tp, value, tb')
889252
+
889252
+    from itertools import imap, izip, ifilter
889252
+    intern = intern
889252
+
889252
+    def implements_iterator(cls):
889252
+        cls.next = cls.__next__
889252
+        del cls.__next__
889252
+        return cls
889252
+
889252
+    def implements_to_string(cls):
889252
+        cls.__unicode__ = cls.__str__
889252
+        cls.__str__ = lambda x: x.__unicode__().encode('utf-8')
889252
+        return cls
889252
+
889252
+    def encode_filename(filename):
889252
+        if isinstance(filename, unicode):
889252
+            return filename.encode('utf-8')
889252
+        return filename
889252
+
889252
+
889252
+def with_metaclass(meta, *bases):
889252
+    """Create a base class with a metaclass."""
889252
+    # This requires a bit of explanation: the basic idea is to make a
889252
+    # dummy metaclass for one level of class instantiation that replaces
889252
+    # itself with the actual metaclass.
889252
+    class metaclass(type):
889252
+        def __new__(cls, name, this_bases, d):
889252
+            return meta(name, bases, d)
889252
+    return type.__new__(metaclass, 'temporary_class', (), {})
889252
+
889252
+
889252
+try:
889252
+    from urllib.parse import quote_from_bytes as url_quote
889252
+except ImportError:
889252
+    from urllib import quote as url_quote
889252
+
889252
+
889252
+try:
889252
+    from collections import abc
889252
+except ImportError:
889252
+    import collections as abc
889252
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
889252
index f9da1da..addee6a 100644
889252
--- a/jinja2/nodes.py
889252
+++ b/jinja2/nodes.py
889252
@@ -595,7 +595,7 @@ class Call(Expr):
889252
 
889252
     def as_const(self, eval_ctx=None):
889252
         eval_ctx = get_eval_context(self, eval_ctx)
889252
-        if eval_ctx.volatile:
889252
+        if eval_ctx.volatile or eval_ctx.environment.sandboxed:
889252
             raise Impossible()
889252
         obj = self.node.as_const(eval_ctx)
889252
 
889252
diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py
889252
index a1cbb29..5f70ef8 100644
889252
--- a/jinja2/sandbox.py
889252
+++ b/jinja2/sandbox.py
889252
@@ -12,12 +12,19 @@
889252
     :copyright: (c) 2010 by the Jinja Team.
889252
     :license: BSD.
889252
 """
889252
+import types
889252
 import operator
889252
+from collections import Mapping
889252
 from jinja2.environment import Environment
889252
 from jinja2.exceptions import SecurityError
889252
+from jinja2._compat import string_types, text_type, PY2
889252
 from jinja2.utils import FunctionType, MethodType, TracebackType, CodeType, \
889252
-     FrameType, GeneratorType
889252
+     FrameType, GeneratorType, Markup
889252
 
889252
+has_format = False
889252
+if hasattr(text_type, 'format'):
889252
+    from string import Formatter
889252
+    has_format = True
889252
 
889252
 #: maximum number of items a range may produce
889252
 MAX_RANGE = 100000
889252
@@ -29,6 +36,11 @@ UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict',
889252
 #: unsafe method attributes.  function attributes are unsafe for methods too
889252
 UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self'])
889252
 
889252
+#: unsafe attributes on coroutines
889252
+UNSAFE_COROUTINE_ATTRIBUTES = set(['cr_frame', 'cr_code'])
889252
+
889252
+#: unsafe attributes on async generators
889252
+UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = set(['ag_code', 'ag_frame'])
889252
 
889252
 import warnings
889252
 
889252
@@ -85,6 +97,79 @@ _mutable_spec = (
889252
     ]))
889252
 )
889252
 
889252
+# Bundled EscapeFormatter class from markupsafe >= 0.21 which is used by
889252
+# jinja2 for fixing CVE-2016-10745
889252
+# Copyright 2010 Pallets
889252
+# BSD 3-Clause License
889252
+# https://github.com/pallets/markupsafe/blob/79ee6ce0ed93c6da73512f069d7db866d955df04/LICENSE.rst
889252
+if hasattr(text_type, "format"):
889252
+
889252
+    class EscapeFormatter(Formatter):
889252
+        def __init__(self, escape):
889252
+            self.escape = escape
889252
+
889252
+        def format_field(self, value, format_spec):
889252
+            if hasattr(value, "__html_format__"):
889252
+                rv = value.__html_format__(format_spec)
889252
+            elif hasattr(value, "__html__"):
889252
+                if format_spec:
889252
+                    raise ValueError(
889252
+                        "Format specifier {0} given, but {1} does not"
889252
+                        " define __html_format__. A class that defines"
889252
+                        " __html__ must define __html_format__ to work"
889252
+                        " with format specifiers.".format(format_spec, type(value))
889252
+                    )
889252
+                rv = value.__html__()
889252
+            else:
889252
+                # We need to make sure the format spec is unicode here as
889252
+                # otherwise the wrong callback methods are invoked.  For
889252
+                # instance a byte string there would invoke __str__ and
889252
+                # not __unicode__.
889252
+                rv = Formatter.format_field(self, value, text_type(format_spec))
889252
+            return text_type(self.escape(rv))
889252
+
889252
+class _MagicFormatMapping(Mapping):
889252
+    """This class implements a dummy wrapper to fix a bug in the Python
889252
+    standard library for string formatting.
889252
+
889252
+    See http://bugs.python.org/issue13598 for information about why
889252
+    this is necessary.
889252
+    """
889252
+
889252
+    def __init__(self, args, kwargs):
889252
+        self._args = args
889252
+        self._kwargs = kwargs
889252
+        self._last_index = 0
889252
+
889252
+    def __getitem__(self, key):
889252
+        if key == '':
889252
+            idx = self._last_index
889252
+            self._last_index += 1
889252
+            try:
889252
+                return self._args[idx]
889252
+            except LookupError:
889252
+                pass
889252
+            key = str(idx)
889252
+        return self._kwargs[key]
889252
+
889252
+    def __iter__(self):
889252
+        return iter(self._kwargs)
889252
+
889252
+    def __len__(self):
889252
+        return len(self._kwargs)
889252
+
889252
+
889252
+def inspect_format_method(callable):
889252
+    if not has_format:
889252
+        return None
889252
+    if not isinstance(callable, (types.MethodType,
889252
+                                 types.BuiltinMethodType)) or \
889252
+       callable.__name__ != 'format':
889252
+        return None
889252
+    obj = callable.__self__
889252
+    if isinstance(obj, string_types):
889252
+        return obj
889252
+
889252
 
889252
 def safe_range(*args):
889252
     """A range that can't generate ranges with a length of more than
889252
@@ -139,6 +224,12 @@ def is_internal_attribute(obj, attr):
889252
     elif isinstance(obj, GeneratorType):
889252
         if attr == 'gi_frame':
889252
             return True
889252
+    elif hasattr(types, 'CoroutineType') and isinstance(obj, types.CoroutineType):
889252
+        if attr in UNSAFE_COROUTINE_ATTRIBUTES:
889252
+            return True
889252
+    elif hasattr(types, 'AsyncGeneratorType') and isinstance(obj, types.AsyncGeneratorType):
889252
+        if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES:
889252
+            return True
889252
     return attr.startswith('__')
889252
 
889252
 
889252
@@ -177,8 +268,8 @@ class SandboxedEnvironment(Environment):
889252
     attributes or functions are safe to access.
889252
 
889252
     If the template tries to access insecure code a :exc:`SecurityError` is
889252
-    raised.  However also other exceptions may occour during the rendering so
889252
-    the caller has to ensure that all exceptions are catched.
889252
+    raised.  However also other exceptions may occur during the rendering so
889252
+    the caller has to ensure that all exceptions are caught.
889252
     """
889252
     sandboxed = True
889252
 
889252
@@ -340,8 +431,24 @@ class SandboxedEnvironment(Environment):
889252
             obj.__class__.__name__
889252
         ), name=attribute, obj=obj, exc=SecurityError)
889252
 
889252
+    def format_string(self, s, args, kwargs):
889252
+        """If a format call is detected, then this is routed through this
889252
+        method so that our safety sandbox can be used for it.
889252
+        """
889252
+        if isinstance(s, Markup):
889252
+            formatter = SandboxedEscapeFormatter(self, s.escape)
889252
+        else:
889252
+            formatter = SandboxedFormatter(self)
889252
+        kwargs = _MagicFormatMapping(args, kwargs)
889252
+        rv = formatter.vformat(s, args, kwargs)
889252
+        return type(s)(rv)
889252
+
889252
     def call(__self, __context, __obj, *args, **kwargs):
889252
         """Call an object from sandboxed code."""
889252
+        fmt = inspect_format_method(__obj)
889252
+        if fmt is not None:
889252
+            return __self.format_string(fmt, args, kwargs)
889252
+
889252
         # the double prefixes are to avoid double keyword argument
889252
         # errors when proxying the call.
889252
         if not __self.is_safe_callable(__obj):
889252
@@ -359,3 +466,37 @@ class ImmutableSandboxedEnvironment(SandboxedEnvironment):
889252
         if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value):
889252
             return False
889252
         return not modifies_known_mutable(obj, attr)
889252
+
889252
+
889252
+if has_format:
889252
+    # This really is not a public API apparenlty.
889252
+    try:
889252
+        from _string import formatter_field_name_split
889252
+    except ImportError:
889252
+        def formatter_field_name_split(field_name):
889252
+            return field_name._formatter_field_name_split()
889252
+
889252
+    class SandboxedFormatterMixin(object):
889252
+
889252
+        def __init__(self, env):
889252
+            self._env = env
889252
+
889252
+        def get_field(self, field_name, args, kwargs):
889252
+            first, rest = formatter_field_name_split(field_name)
889252
+            obj = self.get_value(first, args, kwargs)
889252
+            for is_attr, i in rest:
889252
+                if is_attr:
889252
+                    obj = self._env.getattr(obj, i)
889252
+                else:
889252
+                    obj = self._env.getitem(obj, i)
889252
+            return obj, first
889252
+
889252
+    class SandboxedFormatter(SandboxedFormatterMixin, Formatter):
889252
+        def __init__(self, env):
889252
+            SandboxedFormatterMixin.__init__(self, env)
889252
+            Formatter.__init__(self)
889252
+
889252
+    class SandboxedEscapeFormatter(SandboxedFormatterMixin, EscapeFormatter):
889252
+        def __init__(self, env, escape):
889252
+            SandboxedFormatterMixin.__init__(self, env)
889252
+            EscapeFormatter.__init__(self, escape)