Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python-jinja2: address CVE-2024-56201, CVE-2024-56326 #11752

Merged
merged 6 commits into from
Jan 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions SPECS/python-jinja2/CVE-2024-56201.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
From 739028358bdb8ecbff4dd7c13c316d934ec5cbbd Mon Sep 17 00:00:00 2001
From: Kanishk-Bansal <[email protected]>
Date: Thu, 2 Jan 2025 10:07:33 +0000
Subject: [PATCH] Fix CVE-2024-56201

---
src/jinja2/compiler.py | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/jinja2/compiler.py b/src/jinja2/compiler.py
index 52fd5b8..0314f67 100644
--- a/src/jinja2/compiler.py
+++ b/src/jinja2/compiler.py
@@ -1122,9 +1122,14 @@ class CodeGenerator(NodeVisitor):
)
self.writeline(f"if {frame.symbols.ref(alias)} is missing:")
self.indent()
+ # The position will contain the template name, and will be formatted
+ # into a string that will be compiled into an f-string. Curly braces
+ # in the name must be replaced with escapes so that they will not be
+ # executed as part of the f-string.
+ position = self.position(node).replace("{", "{{").replace("}", "}}")
message = (
"the template {included_template.__name__!r}"
- f" (imported on {self.position(node)})"
+ f" (imported on {position})"
f" does not export the requested name {name!r}"
)
self.writeline(
--
2.45.2

142 changes: 142 additions & 0 deletions SPECS/python-jinja2/CVE-2024-56326.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
From c81e4a5da52f6782157d608356c9a82eaf908a89 Mon Sep 17 00:00:00 2001
From: Kanishk-Bansal <[email protected]>
Date: Thu, 2 Jan 2025 11:09:30 +0000
Subject: [PATCH] Fix CVE-2024-56326

---
src/jinja2/sandbox.py | 77 ++++++++++++++++++++++---------------------
1 file changed, 40 insertions(+), 37 deletions(-)

diff --git a/src/jinja2/sandbox.py b/src/jinja2/sandbox.py
index 4294884..96519a2 100644
--- a/src/jinja2/sandbox.py
+++ b/src/jinja2/sandbox.py
@@ -7,6 +7,7 @@ import typing as t
from _string import formatter_field_name_split # type: ignore
from collections import abc
from collections import deque
+from functools import update_wrapper
from string import Formatter

from markupsafe import EscapeFormatter
@@ -80,19 +81,6 @@ _mutable_spec: t.Tuple[t.Tuple[t.Type, t.FrozenSet[str]], ...] = (
)


-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
@@ -313,6 +301,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)
@@ -330,6 +321,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)
@@ -345,34 +339,46 @@ class SandboxedEnvironment(Environment):
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.
+ def wrap_str_format(self, value: t.Any) -> t.Optional[t.Callable[..., str]]:
+ """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 not isinstance(
+ value, (types.MethodType, types.BuiltinMethodType)
+ ) or value.__name__ not in ("format", "format_map"):
+ return None
+ f_self: t.Any = value.__self__
+ if not isinstance(f_self, str):
+ return None
+ str_type: t.Type[str] = type(f_self)
+ is_format_map = value.__name__ == "format_map"
formatter: SandboxedFormatter
- if isinstance(s, Markup):
- formatter = SandboxedEscapeFormatter(self, escape=s.escape)
+
+ if isinstance(f_self, Markup):
+ formatter = SandboxedEscapeFormatter(self, escape=f_self.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"
- )
+ vformat = formatter.vformat
+
+ def wrapper(*args: t.Any, **kwargs: t.Any) -> str:
+ 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 = ()

- kwargs = args[0]
- args = ()
+ return str_type(vformat(f_self, args, kwargs))

- rv = formatter.vformat(s, args, kwargs)
- return type(s)(rv)
+ return update_wrapper(wrapper, value)

def call(
__self, # noqa: B902
@@ -382,9 +388,6 @@ class SandboxedEnvironment(Environment):
**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.
--
2.45.2

7 changes: 6 additions & 1 deletion SPECS/python-jinja2/python-jinja2.spec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Summary: A fast and easy to use template engine written in pure Python
Name: python-jinja2
Version: 3.0.3
Release: 4%{?dist}
Release: 5%{?dist}
License: BSD
Vendor: Microsoft Corporation
Distribution: Mariner
Expand All @@ -10,6 +10,8 @@ URL: https://jinja.pocoo.org/
Source0: https://files.pythonhosted.org/packages/91/a5/429efc6246119e1e3fbf562c00187d04e83e54619249eb732bb423efa6c6/Jinja2-%{version}.tar.gz
Patch0: CVE-2024-22195.patch
Patch1: CVE-2024-34064.patch
Patch2: CVE-2024-56201.patch
Patch3: CVE-2024-56326.patch
BuildArch: noarch

%description
Expand Down Expand Up @@ -55,6 +57,9 @@ tox -e py%{python3_version_nodots}
%{python3_sitelib}/Jinja2-%{version}-py%{python3_version}.egg-info

%changelog
* Thu Jan 2 2025 Kanishk Bansal <[email protected]> - 3.0.3-5
- Address CVE-2024-56201, CVE-2024-56326.patch with an upstream patch.

* Wed May 22 2024 Sudipta Pandit <[email protected]> - 3.0.3-4
- Backport CVE-2024-34064 from upstream (based on previous backport of CVE-2024-22195)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ python3-Cython-0.29.33-2.cm2.aarch64.rpm
python3-debuginfo-3.9.19-8.cm2.aarch64.rpm
python3-devel-3.9.19-8.cm2.aarch64.rpm
python3-gpg-1.16.0-2.cm2.aarch64.rpm
python3-jinja2-3.0.3-4.cm2.noarch.rpm
python3-jinja2-3.0.3-5.cm2.noarch.rpm
python3-libcap-ng-0.8.2-2.cm2.aarch64.rpm
python3-libs-3.9.19-8.cm2.aarch64.rpm
python3-libxml2-2.10.4-4.cm2.aarch64.rpm
Expand Down
2 changes: 1 addition & 1 deletion toolkit/resources/manifests/package/toolchain_x86_64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ python3-Cython-0.29.33-2.cm2.x86_64.rpm
python3-debuginfo-3.9.19-8.cm2.x86_64.rpm
python3-devel-3.9.19-8.cm2.x86_64.rpm
python3-gpg-1.16.0-2.cm2.x86_64.rpm
python3-jinja2-3.0.3-4.cm2.noarch.rpm
python3-jinja2-3.0.3-5.cm2.noarch.rpm
python3-libcap-ng-0.8.2-2.cm2.x86_64.rpm
python3-libs-3.9.19-8.cm2.x86_64.rpm
python3-libxml2-2.10.4-4.cm2.x86_64.rpm
Expand Down
Loading