From 889252b3745ef5399413f76bf041446ab9309bbd Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: May 22 2019 11:44:18 +0000 Subject: import python27-python-jinja2-2.6-15.el7 --- diff --git a/SOURCES/python-jinja2-fix-CVE-2016-10745.patch b/SOURCES/python-jinja2-fix-CVE-2016-10745.patch new file mode 100644 index 0000000..a8a26b8 --- /dev/null +++ b/SOURCES/python-jinja2-fix-CVE-2016-10745.patch @@ -0,0 +1,328 @@ +diff --git a/jinja2/_compat.py b/jinja2/_compat.py +new file mode 100644 +index 0000000..4dbf6ea +--- /dev/null ++++ b/jinja2/_compat.py +@@ -0,0 +1,105 @@ ++# -*- coding: utf-8 -*- ++""" ++ jinja2._compat ++ ~~~~~~~~~~~~~~ ++ ++ Some py2/py3 compatibility support based on a stripped down ++ version of six so we don't have to depend on a specific version ++ of it. ++ ++ :copyright: Copyright 2013 by the Jinja team, see AUTHORS. ++ :license: BSD, see LICENSE for details. ++""" ++import sys ++ ++PY2 = sys.version_info[0] == 2 ++PYPY = hasattr(sys, 'pypy_translation_info') ++_identity = lambda x: x ++ ++ ++if not PY2: ++ unichr = chr ++ range_type = range ++ text_type = str ++ string_types = (str,) ++ integer_types = (int,) ++ ++ iterkeys = lambda d: iter(d.keys()) ++ itervalues = lambda d: iter(d.values()) ++ iteritems = lambda d: iter(d.items()) ++ ++ import pickle ++ from io import BytesIO, StringIO ++ NativeStringIO = StringIO ++ ++ def reraise(tp, value, tb=None): ++ if value.__traceback__ is not tb: ++ raise value.with_traceback(tb) ++ raise value ++ ++ ifilter = filter ++ imap = map ++ izip = zip ++ intern = sys.intern ++ ++ implements_iterator = _identity ++ implements_to_string = _identity ++ encode_filename = _identity ++ ++else: ++ unichr = unichr ++ text_type = unicode ++ range_type = xrange ++ string_types = (str, unicode) ++ integer_types = (int, long) ++ ++ iterkeys = lambda d: d.iterkeys() ++ itervalues = lambda d: d.itervalues() ++ iteritems = lambda d: d.iteritems() ++ ++ import cPickle as pickle ++ from cStringIO import StringIO as BytesIO, StringIO ++ NativeStringIO = BytesIO ++ ++ exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') ++ ++ from itertools import imap, izip, ifilter ++ intern = intern ++ ++ def implements_iterator(cls): ++ cls.next = cls.__next__ ++ del cls.__next__ ++ return cls ++ ++ def implements_to_string(cls): ++ cls.__unicode__ = cls.__str__ ++ cls.__str__ = lambda x: x.__unicode__().encode('utf-8') ++ return cls ++ ++ def encode_filename(filename): ++ if isinstance(filename, unicode): ++ return filename.encode('utf-8') ++ return filename ++ ++ ++def with_metaclass(meta, *bases): ++ """Create a base class with a metaclass.""" ++ # This requires a bit of explanation: the basic idea is to make a ++ # dummy metaclass for one level of class instantiation that replaces ++ # itself with the actual metaclass. ++ class metaclass(type): ++ def __new__(cls, name, this_bases, d): ++ return meta(name, bases, d) ++ return type.__new__(metaclass, 'temporary_class', (), {}) ++ ++ ++try: ++ from urllib.parse import quote_from_bytes as url_quote ++except ImportError: ++ from urllib import quote as url_quote ++ ++ ++try: ++ from collections import abc ++except ImportError: ++ import collections as abc +diff --git a/jinja2/nodes.py b/jinja2/nodes.py +index f9da1da..addee6a 100644 +--- a/jinja2/nodes.py ++++ b/jinja2/nodes.py +@@ -595,7 +595,7 @@ class Call(Expr): + + def as_const(self, eval_ctx=None): + eval_ctx = get_eval_context(self, eval_ctx) +- if eval_ctx.volatile: ++ if eval_ctx.volatile or eval_ctx.environment.sandboxed: + raise Impossible() + obj = self.node.as_const(eval_ctx) + +diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py +index a1cbb29..5f70ef8 100644 +--- a/jinja2/sandbox.py ++++ b/jinja2/sandbox.py +@@ -12,12 +12,19 @@ + :copyright: (c) 2010 by the Jinja Team. + :license: BSD. + """ ++import types + import operator ++from collections import Mapping + from jinja2.environment import Environment + from jinja2.exceptions import SecurityError ++from jinja2._compat import string_types, text_type, PY2 + from jinja2.utils import FunctionType, MethodType, TracebackType, CodeType, \ +- FrameType, GeneratorType ++ FrameType, GeneratorType, Markup + ++has_format = False ++if hasattr(text_type, 'format'): ++ from string import Formatter ++ has_format = True + + #: maximum number of items a range may produce + MAX_RANGE = 100000 +@@ -29,6 +36,11 @@ UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', + #: unsafe method attributes. function attributes are unsafe for methods too + UNSAFE_METHOD_ATTRIBUTES = set(['im_class', 'im_func', 'im_self']) + ++#: unsafe attributes on coroutines ++UNSAFE_COROUTINE_ATTRIBUTES = set(['cr_frame', 'cr_code']) ++ ++#: unsafe attributes on async generators ++UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = set(['ag_code', 'ag_frame']) + + import warnings + +@@ -85,6 +97,79 @@ _mutable_spec = ( + ])) + ) + ++# Bundled EscapeFormatter class from markupsafe >= 0.21 which is used by ++# jinja2 for fixing CVE-2016-10745 ++# Copyright 2010 Pallets ++# BSD 3-Clause License ++# https://github.com/pallets/markupsafe/blob/79ee6ce0ed93c6da73512f069d7db866d955df04/LICENSE.rst ++if hasattr(text_type, "format"): ++ ++ class EscapeFormatter(Formatter): ++ def __init__(self, escape): ++ self.escape = escape ++ ++ def format_field(self, value, format_spec): ++ if hasattr(value, "__html_format__"): ++ rv = value.__html_format__(format_spec) ++ elif hasattr(value, "__html__"): ++ if format_spec: ++ raise ValueError( ++ "Format specifier {0} given, but {1} does not" ++ " define __html_format__. A class that defines" ++ " __html__ must define __html_format__ to work" ++ " with format specifiers.".format(format_spec, type(value)) ++ ) ++ rv = value.__html__() ++ else: ++ # We need to make sure the format spec is unicode here as ++ # otherwise the wrong callback methods are invoked. For ++ # instance a byte string there would invoke __str__ and ++ # not __unicode__. ++ rv = Formatter.format_field(self, value, text_type(format_spec)) ++ return text_type(self.escape(rv)) ++ ++class _MagicFormatMapping(Mapping): ++ """This class implements a dummy wrapper to fix a bug in the Python ++ standard library for string formatting. ++ ++ See http://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 has_format: ++ return None ++ if not isinstance(callable, (types.MethodType, ++ types.BuiltinMethodType)) or \ ++ callable.__name__ != 'format': ++ 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 +@@ -139,6 +224,12 @@ def is_internal_attribute(obj, attr): + elif isinstance(obj, GeneratorType): + if attr == 'gi_frame': + 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('__') + + +@@ -177,8 +268,8 @@ class SandboxedEnvironment(Environment): + 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 occour during the rendering so +- the caller has to ensure that all exceptions are catched. ++ raised. However also other exceptions may occur during the rendering so ++ the caller has to ensure that all exceptions are caught. + """ + sandboxed = True + +@@ -340,8 +431,24 @@ class SandboxedEnvironment(Environment): + obj.__class__.__name__ + ), name=attribute, obj=obj, exc=SecurityError) + ++ def format_string(self, s, args, kwargs): ++ """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) ++ kwargs = _MagicFormatMapping(args, kwargs) ++ rv = formatter.vformat(s, args, kwargs) ++ return type(s)(rv) ++ + def call(__self, __context, __obj, *args, **kwargs): + """Call an object from sandboxed code.""" ++ fmt = inspect_format_method(__obj) ++ if fmt is not None: ++ return __self.format_string(fmt, args, kwargs) ++ + # the double prefixes are to avoid double keyword argument + # errors when proxying the call. + if not __self.is_safe_callable(__obj): +@@ -359,3 +466,37 @@ class ImmutableSandboxedEnvironment(SandboxedEnvironment): + if not SandboxedEnvironment.is_safe_attribute(self, obj, attr, value): + return False + return not modifies_known_mutable(obj, attr) ++ ++ ++if has_format: ++ # This really is not a public API apparenlty. ++ 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) diff --git a/SPECS/python-jinja2.spec b/SPECS/python-jinja2.spec index 2840d4f..05db935 100644 --- a/SPECS/python-jinja2.spec +++ b/SPECS/python-jinja2.spec @@ -7,7 +7,7 @@ Name: %{?scl_prefix}python-jinja2 Version: 2.6 -Release: 14%{?dist} +Release: 15%{?dist} Summary: General purpose template engine Group: Development/Languages License: BSD @@ -18,11 +18,20 @@ Source0: http://pypi.python.org/packages/source/J/Jinja2/Jinja2-%{version}.tar.g # https://github.com/mitsuhiko/jinja2/commit/acb672b6a179567632e032f547582f30fa2f4aa7 # https://github.com/mitsuhiko/jinja2/pull/296/files Patch0: %{pkg_name}-fix-CVE-2014-1402.patch + +# Fix CVE-2016-10745 +# Also bundling the EscapeFormatter class from markupsafe >= 0.21, as we don't ship +# that version in RHEL7 and it's required for the CVE fix +# https://github.com/pallets/jinja/commit/9b53045c34e61013dc8f09b7e52a555fa16bed16 +# https://bugzilla.redhat.com/show_bug.cgi?id=1701310 +Patch1: python-jinja2-fix-CVE-2016-10745.patch + BuildRoot: %{_tmppath}/%{pkg_name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildArch: noarch BuildRequires: %{?scl_prefix}python-devel BuildRequires: %{?scl_prefix}python-setuptools BuildRequires: %{?scl_prefix}python-markupsafe + %if 0%{?with_docs} BuildRequires: %{?scl_prefix}python-sphinx %endif # with_docs @@ -47,6 +56,7 @@ environments. find . -name '*.pyo' -o -name '*.pyc' -delete %patch0 -p0 +%patch1 -p1 # fix EOL sed -i 's|\r$||g' LICENSE @@ -99,6 +109,10 @@ make test %exclude %{python_sitelib}/jinja2/_debugsupport.c %changelog +* Thu May 02 2019 Charalampos Stratakis - 2.6-15 +- Fix for CVE-2016-10745 +Resolves: rhbz#1701310 + * Fri May 25 2018 Charalampos Stratakis - 2.6-14 - Rebuild for multi-arch bootstrap - Enable docs