security #cve-2026-46628 Pre-escape HTML input on the spaceless filter (fabpot)

This PR was merged into the twig-3.x branch.
This commit is contained in:
Fabien Potencier
2026-05-20 08:53:45 +02:00
4 changed files with 34 additions and 1 deletions
+1
View File
@@ -1,5 +1,6 @@
# 3.26.0 (2026-XX-XX)
* Pre-escape HTML input on the `spaceless` filter
* Encode single quotes as `\x27` in `Compiler::string()` as a defense-in-depth measure
* Fix sandbox `__toString` bypasses
* Add `Twig\Node\CoercesChildrenToStringInterface` to let nodes declare which of their child nodes will be string-coerced at runtime so the sandbox wraps them with a `__toString` check
+10
View File
@@ -6,6 +6,16 @@
The ``spaceless`` filter is deprecated as of Twig 3.12. While not a full
replacement, you can check the :ref:`whitespace control features <templates-whitespace-control>`.
.. caution::
The ``spaceless`` filter is declared safe for the ``html`` context, so its
output is not auto-escaped. Its input is therefore pre-escaped by the
auto-escaper when needed. If you re-implement this filter in your own
code, declare it with both ``'pre_escape' => 'html'`` and
``'is_safe' => ['html']``: declaring ``is_safe`` without ``pre_escape``
is equivalent to piping user input through ``|raw`` and opens an XSS
vector.
Use the ``spaceless`` filter to remove whitespace *between HTML tags*, not
whitespace within HTML tags or whitespace in plain text:
+1 -1
View File
@@ -258,7 +258,7 @@ final class CoreExtension extends AbstractExtension
new TwigFilter('striptags', [self::class, 'striptags']),
new TwigFilter('trim', [self::class, 'trim']),
new TwigFilter('nl2br', [self::class, 'nl2br'], ['pre_escape' => 'html', 'is_safe' => ['html']]),
new TwigFilter('spaceless', [self::class, 'spaceless'], ['is_safe' => ['html'], 'deprecation_info' => new DeprecatedCallableInfo('twig/twig', '3.12')]),
new TwigFilter('spaceless', [self::class, 'spaceless'], ['pre_escape' => 'html', 'is_safe' => ['html'], 'deprecation_info' => new DeprecatedCallableInfo('twig/twig', '3.12')]),
// array helpers
new TwigFilter('join', [self::class, 'join']),
@@ -0,0 +1,22 @@
--TEST--
"spaceless" filter pre-escapes its input under autoescape
--DEPRECATION--
Since twig/twig 3.12: Twig Filter "spaceless" is deprecated in index.twig at line 3.
Since twig/twig 3.12: Twig Filter "spaceless" is deprecated in index.twig at line 5.
Since twig/twig 3.12: Twig Filter "spaceless" is deprecated in index.twig at line 7.
--TEMPLATE--
{% autoescape 'html' %}
1. {{ payload|spaceless }}
2. {{ payload|escape|spaceless }}
3. {{ payload|raw|spaceless }}
{% endautoescape %}
--DATA--
return ['payload' => '<script> alert(1) </script>']
--EXPECT--
1. &lt;script&gt; alert(1) &lt;/script&gt;
2. &lt;script&gt; alert(1) &lt;/script&gt;
3. <script> alert(1) </script>