From c630659347e6824a303e63a7706c23d248bf17cc Mon Sep 17 00:00:00 2001 From: Lumir Balhar Date: Wed, 26 Mar 2025 21:51:37 +0100 Subject: [PATCH] CVE-2025-27516 --- src/jinja2/filters.py | 34 ++++++++++++++++------------------ tests/test_security.py | 10 ++++++++++ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/jinja2/filters.py b/src/jinja2/filters.py index 6d4f348..bc6cab3 100644 --- a/src/jinja2/filters.py +++ b/src/jinja2/filters.py @@ -5,6 +5,7 @@ import random import re import warnings from collections import namedtuple +from inspect import getattr_static from itertools import chain from itertools import groupby @@ -1067,28 +1068,25 @@ def do_reverse(value): @environmentfilter def do_attr(environment, obj, name): - """Get an attribute of an object. ``foo|attr("bar")`` works like - ``foo.bar`` just that always an attribute is returned and items are not - looked up. + """Get an attribute of an object. ``foo|attr("bar")`` works like + ``foo.bar``, but returns undefined instead of falling back to ``foo["bar"]`` + if the attribute doesn't exist. See :ref:`Notes on subscriptions ` for more details. """ + # Environment.getattr will fall back to obj[name] if obj.name doesn't exist. + # But we want to call env.getattr to get behavior such as sandboxing. + # Determine if the attr exists first, so we know the fallback won't trigger. try: - name = str(name) - except UnicodeError: - pass - else: - try: - value = getattr(obj, name) - except AttributeError: - pass - else: - if environment.sandboxed and not environment.is_safe_attribute( - obj, name, value - ): - return environment.unsafe_undefined(obj, name) - return value - return environment.undefined(obj=obj, name=name) + # This avoids executing properties/descriptors, but misses __getattr__ + # and __getattribute__ dynamic attrs. + getattr_static(obj, name) + except AttributeError: + # This finds dynamic attrs, and we know it's not a descriptor at this point. + if not hasattr(obj, name): + return environment.undefined(obj=obj, name=name) + + return environment.getattr(obj, name) @contextfilter diff --git a/tests/test_security.py b/tests/test_security.py index 2e2af69..c1c71df 100644 --- a/tests/test_security.py +++ b/tests/test_security.py @@ -225,3 +225,13 @@ class TestStringFormatMap(object): with pytest.raises(SecurityError): t.render() + + def test_attr_filter(self) -> None: + env = SandboxedEnvironment() + t = env.from_string( + """{{ "{0.__call__.__builtins__[__import__]}" + | attr("format")(not_here) }}""" + ) + + with pytest.raises(SecurityError): + t.render() -- 2.49.0