From 6aa4943552fc7a4f7f59da535944f3229e6132ea Mon Sep 17 00:00:00 2001 From: zhuhongbo Date: Fri, 21 Feb 2025 14:16:22 +0800 Subject: [PATCH] fix cve CVE-2024-56326 --- 0001-fix-cve-CVE-2024-56326.patch | 337 ++++++++++++++++++++++++++++++ python-jinja2.spec | 10 +- 2 files changed, 346 insertions(+), 1 deletion(-) create mode 100644 0001-fix-cve-CVE-2024-56326.patch diff --git a/0001-fix-cve-CVE-2024-56326.patch b/0001-fix-cve-CVE-2024-56326.patch new file mode 100644 index 0000000..2a73c15 --- /dev/null +++ b/0001-fix-cve-CVE-2024-56326.patch @@ -0,0 +1,337 @@ +From 9471f7d3bd90f6bdb91ce957a4a1d7e3869e1fb8 Mon Sep 17 00:00:00 2001 +From: zhuhongbo +Date: Fri, 21 Feb 2025 16:36:52 +0800 +Subject: [PATCH] fix cve CVE-2024-56326 + +--- + jinja2/sandbox.py | 207 +++++++++++++++++++++------------------------- + 1 file changed, 95 insertions(+), 112 deletions(-) + +diff --git a/jinja2/sandbox.py b/jinja2/sandbox.py +index 7e31a7a..5516f96 100644 +--- a/jinja2/sandbox.py ++++ b/jinja2/sandbox.py +@@ -9,29 +9,34 @@ + + The behavior can be changed by subclassing the environment. + +- :copyright: (c) 2010 by the Jinja Team. ++ :copyright: (c) 2017 by the Jinja Team. + :license: BSD. + """ + import types + import operator + from collections import Mapping ++from functools import update_wrapper + from jinja2.environment import Environment + from jinja2.exceptions import SecurityError +-from jinja2._compat import string_types, function_type, method_type, \ +- traceback_type, code_type, frame_type, generator_type, text_type, PY2 ++from jinja2._compat import string_types, PY2 + from jinja2.utils import Markup + +-has_format = False +-if hasattr(text_type, 'format'): +- from string import Formatter +- has_format = True ++from markupsafe import EscapeFormatter ++from string import Formatter ++ + + #: maximum number of items a range may produce + MAX_RANGE = 100000 + + #: attributes of function objects that are considered unsafe. +-UNSAFE_FUNCTION_ATTRIBUTES = set(['func_closure', 'func_code', 'func_dict', +- 'func_defaults', 'func_globals']) ++if PY2: ++ UNSAFE_FUNCTION_ATTRIBUTES = set(['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 = set(['im_class', 'im_func', 'im_self']) +@@ -45,11 +50,6 @@ UNSAFE_COROUTINE_ATTRIBUTES = set(['cr_frame', 'cr_code']) + #: unsafe attributes on async generators + UNSAFE_ASYNC_GENERATOR_ATTRIBUTES = set(['ag_code', 'ag_frame']) + +-# On versions > python 2 the special attributes on functions are gone, +-# but they remain on methods and generators for whatever reason. +-if not PY2: +- UNSAFE_FUNCTION_ATTRIBUTES = set() +- + import warnings + + # make sure we don't warn in python 2.6 about stuff we don't care about +@@ -80,13 +80,11 @@ except ImportError: + pass + + #: register Python 2.6 abstract base classes +-try: +- from collections import MutableSet, MutableMapping, MutableSequence +- _mutable_set_types += (MutableSet,) +- _mutable_mapping_types += (MutableMapping,) +- _mutable_sequence_types += (MutableSequence,) +-except ImportError: +- pass ++from collections import MutableSet, MutableMapping, MutableSequence ++_mutable_set_types += (MutableSet,) ++_mutable_mapping_types += (MutableMapping,) ++_mutable_sequence_types += (MutableSequence,) ++ + + _mutable_spec = ( + (_mutable_set_types, frozenset([ +@@ -105,42 +103,12 @@ _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 ++ See https://bugs.python.org/issue13598 for information about why + this is necessary. + """ + +@@ -167,18 +135,6 @@ class _MagicFormatMapping(Mapping): + 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 + MAX_RANGE items. +@@ -210,33 +166,31 @@ def is_internal_attribute(obj, attr): + :meth:`~SandboxedEnvironment.is_safe_attribute` is overridden. + + >>> from jinja2.sandbox import is_internal_attribute +- >>> is_internal_attribute(lambda: None, "func_code") +- True +- >>> is_internal_attribute((lambda x:x).func_code, 'co_code') ++ >>> is_internal_attribute(str, "mro") + True + >>> is_internal_attribute(str, "upper") + False + """ +- if isinstance(obj, function_type): ++ if isinstance(obj, types.FunctionType): + if attr in UNSAFE_FUNCTION_ATTRIBUTES: + return True +- elif isinstance(obj, method_type): ++ 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, (code_type, traceback_type, frame_type)): ++ elif isinstance(obj, (types.CodeType, types.TracebackType, types.FrameType)): + return True +- elif isinstance(obj, generator_type): ++ 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 attri in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES: ++ if attr in UNSAFE_ASYNC_GENERATOR_ATTRIBUTES: + return True + return attr.startswith('__') + +@@ -409,6 +363,9 @@ class SandboxedEnvironment(Environment): + except AttributeError: + pass + else: ++ fmt = self.wrap_str_format(value) ++ if fmt is not None: ++ return fmt + if self.is_safe_attribute(obj, argument, value): + return value + return self.unsafe_undefined(obj, argument) +@@ -426,6 +383,9 @@ class SandboxedEnvironment(Environment): + except (TypeError, LookupError): + pass + else: ++ fmt = self.wrap_str_format(value) ++ if fmt is not None: ++ return fmt + if self.is_safe_attribute(obj, attribute, value): + return value + return self.unsafe_undefined(obj, attribute) +@@ -439,23 +399,44 @@ 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. ++ def wrap_str_format(self, value): ++ """If the given value is a ``str.format`` or ``str.format_map`` method, ++ return a new function than handles sandboxing. This is done at access ++ rather than in :meth:`call`, so that calls made without ``call`` are ++ also sandboxed. + """ +- if isinstance(s, Markup): +- formatter = SandboxedEscapeFormatter(self, s.escape) ++ if not isinstance( ++ value, (types.MethodType, types.BuiltinMethodType) ++ ) or value.__name__ not in ("format", "format_map"): ++ return None ++ f_self = value.__self__ ++ if not isinstance(f_self, str): ++ return None ++ str_type = type(f_self) ++ is_format_map = value.__name__ == "format_map" ++ if isinstance(f_self, Markup): ++ formatter = SandboxedEscapeFormatter(self, escape=f_self.escape) + else: + formatter = SandboxedFormatter(self) +- kwargs = _MagicFormatMapping(args, kwargs) +- rv = formatter.vformat(s, args, kwargs) +- return type(s)(rv) ++ ++ vformat = formatter.vformat ++ def wrapper(*args, **kwargs): ++ if is_format_map: ++ if kwargs: ++ raise TypeError("format_map() takes no keyword arguments") ++ if len(args) != 1: ++ raise TypeError( ++ f"format_map() takes exactly one argument ({len(args)} given)" ++ ) ++ kwargs = args[0] ++ args = () ++ ++ return str_type(vformat(f_self, args, kwargs)) ++ ++ return update_wrapper(wrapper, value) + + 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. +@@ -476,35 +457,37 @@ class ImmutableSandboxedEnvironment(SandboxedEnvironment): + 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() ++# 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 ++class SandboxedFormatterMixin(object): + +- 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) ++ 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) +-- +2.39.3 + diff --git a/python-jinja2.spec b/python-jinja2.spec index 6d12183..2201abf 100644 --- a/python-jinja2.spec +++ b/python-jinja2.spec @@ -10,7 +10,7 @@ Name: python-jinja2 Version: 2.7.2 -Release: 4%{?dist} +Release: 5%{?dist} Summary: General purpose template engine Group: Development/Languages License: BSD @@ -36,6 +36,10 @@ Patch3: python-jinja2-lambda-to-dict.patch # https://bugzilla.redhat.com/show_bug.cgi?id=1701309 Patch4: python-jinja2-fix-CVE-2016-10745.patch +#add by uos +Patch5: 0001-fix-cve-CVE-2024-56326.patch +#end + BuildArch: noarch BuildRequires: python2-devel BuildRequires: python2-setuptools @@ -93,6 +97,7 @@ environments. %patch2 -p1 %patch3 -p1 %patch4 -p1 +#%patch5 -p1 # cleanup find . -name '*.pyo' -o -name '*.pyc' -delete @@ -172,6 +177,9 @@ popd %changelog +* Fri Feb 21 2025 zhuhongbo - 2.7.2-5 +- cve: fix cve CVE-2024-56326 + * Thu May 02 2019 Charalampos Stratakis - 2.7.2-4 - Fix for CVE-2016-10745 Resolves: rhbz#1701309 -- Gitee