On 27/02/2025 23:06, Lee Garrett wrote:
Package: release.debian.org Severity: normal Tags: bookworm X-Debbugs-Cc: jin...@packages.debian.org, deb...@rocketjump.eu Control: affects -1 + src:jinja2 User: release.debian....@packages.debian.org Usertags: pu[ Reason ] Fix CVE-2024-56201 Fix CVE-2024-56326 [ Impact ] Two security vulnerabilities will stay unfixed. [ Tests ] The patches are taken from upstream, and include test coverage. Both patches applied with minimal changes. [ Risks ] Low, are nearly direct patches from upstream. [ Checklist ] [x] *all* changes are documented in the d/changelog [x] I reviewed all changes and I approve them [x] attach debdiff against the package in (old)stable [x] the issue is verified as fixed in unstable [ Changes ] Fix CVE-2024-56201 Fix CVE-2024-56326 [ Other info ] %
Forgot the debdiff. Is attached now.
diff -Nru jinja2-3.1.2/debian/changelog jinja2-3.1.2/debian/changelog --- jinja2-3.1.2/debian/changelog 2024-12-07 18:15:36.000000000 +0100 +++ jinja2-3.1.2/debian/changelog 2025-02-27 22:30:54.000000000 +0100 @@ -1,3 +1,31 @@ +jinja2 (3.1.2-1+deb12u2) bookworm; urgency=medium + + * Non-maintainer upload by the LTS security team. + * Fix CVE-2024-56201: + In versions on the 3.x branch prior to 3.1.5, a bug in the Jinja compiler + allows an attacker that controls both the content and filename of a template + to execute arbitrary Python code, regardless of if Jinja's sandbox is used. + To exploit the vulnerability, an attacker needs to control both the filename + and the contents of a template. Whether that is the case depends on the type + of application using Jinja. This vulnerability impacts users of applications + which execute untrusted templates where the template author can also choose + the template filename. + * Fix CVE-2024-56326: + Prior to 3.1.5, An oversight in how the Jinja sandboxed environment detects + calls to str.format allows an attacker that controls the content of a + template to execute arbitrary Python code. To exploit the vulnerability, an + attacker needs to control the content of a template. Whether that is the + case depends on the type of application using Jinja. This vulnerability + impacts users of applications which execute untrusted templates. Jinja's + sandbox does catch calls to str.format and ensures they don't escape the + sandbox. However, it's possible to store a reference to a malicious string's + format method, then pass that to a filter that calls it. No such filters are + built-in to Jinja, but could be present through custom filters in an + application. After the fix, such indirect calls are also handled by the + sandbox. + + -- Lee Garrett <deb...@rocketjump.eu> Thu, 27 Feb 2025 22:30:54 +0100 + jinja2 (3.1.2-1+deb12u1) bookworm; urgency=medium * Non-maintainer upload. diff -Nru jinja2-3.1.2/debian/gbp.conf jinja2-3.1.2/debian/gbp.conf --- jinja2-3.1.2/debian/gbp.conf 1970-01-01 01:00:00.000000000 +0100 +++ jinja2-3.1.2/debian/gbp.conf 2025-02-27 21:54:52.000000000 +0100 @@ -0,0 +1,10 @@ +# Configuration for git-buildpackage and affiliated tools + +[DEFAULT] +debian-branch = debian/bookworm +pristine-tar = True +sign-tags = True +upstream-branch = upstream/bookworm + +[import-orig] +merge-mode = replace diff -Nru jinja2-3.1.2/debian/patches/0006-Fix-CVE-2024-56201.patch jinja2-3.1.2/debian/patches/0006-Fix-CVE-2024-56201.patch --- jinja2-3.1.2/debian/patches/0006-Fix-CVE-2024-56201.patch 1970-01-01 01:00:00.000000000 +0100 +++ jinja2-3.1.2/debian/patches/0006-Fix-CVE-2024-56201.patch 2025-02-27 22:29:56.000000000 +0100 @@ -0,0 +1,61 @@ +Description: Fix CVE-2024-56201 +Origin: backport, https://github.com/pallets/jinja/commit/767b23617628419ae3709ccfb02f9602ae9fe51f +Reviewed-by: Lee Garrett <deb...@rocketjump.eu> +Last-Update: 2025-02-27 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ + +diff --git a/src/jinja2/compiler.py b/src/jinja2/compiler.py +index 3458095..27d86c3 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( +diff --git a/tests/test_compile.py b/tests/test_compile.py +index 42a773f..aecf149 100644 +--- a/tests/test_compile.py ++++ b/tests/test_compile.py +@@ -1,6 +1,9 @@ + import os + import re + ++import pytest ++ ++from jinja2 import UndefinedError + from jinja2.environment import Environment + from jinja2.loaders import DictLoader + +@@ -26,3 +29,19 @@ def test_import_as_with_context_deterministic(tmp_path): + expect = [f"'bar{i}': " for i in range(10)] + found = re.findall(r"'bar\d': ", content)[:10] + assert found == expect ++ ++ ++def test_undefined_import_curly_name(): ++ env = Environment( ++ loader=DictLoader( ++ { ++ "{bad}": "{% from 'macro' import m %}{{ m() }}", ++ "macro": "", ++ } ++ ) ++ ) ++ ++ # Must not raise `NameError: 'bad' is not defined`, as that would indicate ++ # that `{bad}` is being interpreted as an f-string. It must be escaped. ++ with pytest.raises(UndefinedError): ++ env.get_template("{bad}").render() diff -Nru jinja2-3.1.2/debian/patches/0007-Fix-CVE-2024-56326.patch jinja2-3.1.2/debian/patches/0007-Fix-CVE-2024-56326.patch --- jinja2-3.1.2/debian/patches/0007-Fix-CVE-2024-56326.patch 1970-01-01 01:00:00.000000000 +0100 +++ jinja2-3.1.2/debian/patches/0007-Fix-CVE-2024-56326.patch 2025-02-27 22:30:08.000000000 +0100 @@ -0,0 +1,165 @@ +Description: Fix CVE-2024-56326 +Origin: backport, https://github.com/pallets/jinja/commit/48b0687e05a5466a91cd5812d604fa37ad0943b4 +Reviewed-by: Lee Garrett <deb...@rocketjump.eu> +Last-Update: 2025-02-27 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +diff --git a/src/jinja2/sandbox.py b/src/jinja2/sandbox.py +index f443c18..26d9d7e 100644 +--- a/src/jinja2/sandbox.py ++++ b/src/jinja2/sandbox.py +@@ -10,6 +10,7 @@ try: + from collections.abc import deque + except ImportError: + from collections import deque ++from functools import update_wrapper + from string import Formatter + + from markupsafe import EscapeFormatter +@@ -83,20 +84,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 + MAX_RANGE items. +@@ -316,6 +303,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) +@@ -333,6 +323,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) +@@ -348,34 +341,49 @@ 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 +@@ -385,9 +393,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. +diff --git a/tests/test_security.py b/tests/test_security.py +index 0e8dc5c..81a32ae 100644 +--- a/tests/test_security.py ++++ b/tests/test_security.py +@@ -171,3 +171,20 @@ class TestStringFormatMap: + '{{ ("a{x.foo}b{y}"|safe).format_map({"x":{"foo": 42}, "y":"<foo>"}) }}' + ) + assert t.render() == "a42b<foo>" ++ ++ def test_indirect_call(self): ++ def run(value, arg): ++ return value.run(arg) ++ ++ env = SandboxedEnvironment() ++ env.filters["run"] = run ++ t = env.from_string( ++ """{% set ++ ns = namespace(run="{0.__call__.__builtins__[__import__]}".format) ++ %} ++ {{ ns | run(not_here) }} ++ """ ++ ) ++ ++ with pytest.raises(SecurityError): ++ t.render() diff -Nru jinja2-3.1.2/debian/patches/series jinja2-3.1.2/debian/patches/series --- jinja2-3.1.2/debian/patches/series 2024-12-07 18:15:36.000000000 +0100 +++ jinja2-3.1.2/debian/patches/series 2025-02-27 22:21:40.000000000 +0100 @@ -3,3 +3,5 @@ 0003-fix-nose-leftovers.patch 0001-xmlattr-filter-disallows-keys-with-spaces.patch 0002-disallow-invalid-characters-in-keys-to-xmlattr-filte.patch +0006-Fix-CVE-2024-56201.patch +0007-Fix-CVE-2024-56326.patch