]> git.ipfire.org Git - thirdparty/jinja.git/commitdiff
disallow invalid characters in keys to xmlattr filter
authorDavid Lord <davidism@gmail.com>
Thu, 2 May 2024 16:14:00 +0000 (09:14 -0700)
committerDavid Lord <davidism@gmail.com>
Thu, 2 May 2024 16:14:00 +0000 (09:14 -0700)
CHANGES.rst
src/jinja2/filters.py
tests/test_filters.py

index bf670420c0f27a878697dfc21fd17f52521c6ffc..ba3b2244e98c1adb945a818696b88f699e9bea2f 100644 (file)
@@ -5,6 +5,12 @@ Version 3.1.4
 
 Unreleased
 
+-   The ``xmlattr`` filter does not allow keys with ``/`` solidus, ``>``
+    greater-than sign, or ``=`` equals sign, in addition to disallowing spaces.
+    Regardless of any validation done by Jinja, user input should never be used
+    as keys to this filter, or must be separately validated first.
+    GHSA-h75v-3vvj-5mfj
+
 
 Version 3.1.3
 -------------
index 4cf3c11fbaf2150dc23b4b013bd30d8853f8b2cf..acd11976e4fc74486025a4e480991cb82e64091a 100644 (file)
@@ -250,7 +250,9 @@ def do_items(value: t.Union[t.Mapping[K, V], Undefined]) -> t.Iterator[t.Tuple[K
     yield from value.items()
 
 
-_space_re = re.compile(r"\s", flags=re.ASCII)
+# Check for characters that would move the parser state from key to value.
+# https://html.spec.whatwg.org/#attribute-name-state
+_attr_key_re = re.compile(r"[\s/>=]", flags=re.ASCII)
 
 
 @pass_eval_context
@@ -259,8 +261,14 @@ def do_xmlattr(
 ) -> str:
     """Create an SGML/XML attribute string based on the items in a dict.
 
-    If any key contains a space, this fails with a ``ValueError``. Values that
-    are neither ``none`` nor ``undefined`` are automatically escaped.
+    **Values** that are neither ``none`` nor ``undefined`` are automatically
+    escaped, safely allowing untrusted user input.
+
+    User input should not be used as **keys** to this filter. If any key
+    contains a space, ``/`` solidus, ``>`` greater-than sign, or ``=`` equals
+    sign, this fails with a ``ValueError``. Regardless of this, user input
+    should never be used as keys to this filter, or must be separately validated
+    first.
 
     .. sourcecode:: html+jinja
 
@@ -280,6 +288,10 @@ def do_xmlattr(
     As you can see it automatically prepends a space in front of the item
     if the filter returned something unless the second parameter is false.
 
+    .. versionchanged:: 3.1.4
+        Keys with ``/`` solidus, ``>`` greater-than sign, or ``=`` equals sign
+        are not allowed.
+
     .. versionchanged:: 3.1.3
         Keys with spaces are not allowed.
     """
@@ -289,8 +301,8 @@ def do_xmlattr(
         if value is None or isinstance(value, Undefined):
             continue
 
-        if _space_re.search(key) is not None:
-            raise ValueError(f"Spaces are not allowed in attributes: '{key}'")
+        if _attr_key_re.search(key) is not None:
+            raise ValueError(f"Invalid character in attribute name: {key!r}")
 
         items.append(f'{escape(key)}="{escape(value)}"')
 
index f50ed13ab5e16022e95163cd77c500b57addc732..d8e9114d0f61a9158bac32d94c4e2ca31faea065 100644 (file)
@@ -474,11 +474,12 @@ class TestFilter:
         assert 'bar="23"' in out
         assert 'blub:blub="&lt;?&gt;"' in out
 
-    def test_xmlattr_key_with_spaces(self, env):
-        with pytest.raises(ValueError, match="Spaces are not allowed"):
-            env.from_string(
-                "{{ {'src=1 onerror=alert(1)': 'my_class'}|xmlattr }}"
-            ).render()
+    @pytest.mark.parametrize("sep", ("\t", "\n", "\f", " ", "/", ">", "="))
+    def test_xmlattr_key_invalid(self, env: Environment, sep: str) -> None:
+        with pytest.raises(ValueError, match="Invalid character"):
+            env.from_string("{{ {key: 'my_class'}|xmlattr }}").render(
+                key=f"class{sep}onclick=alert(1)"
+            )
 
     def test_sort1(self, env):
         tmpl = env.from_string("{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}")