* 3.x: (23 commits)
Bump version
Prepare the 3.26.0 release
Update CHANGELOG
Document that the sandbox doesn't protect against resource exhaustion
Document template_from_string caveats when used in a sandboxed env
Pre-escape HTML input on the `spaceless` filter
Add docs on Markup about the goal of this class in the context of a sandbox
Fix sandbox bypass in the "column" filter
Fix sandbox `__toString` bypasses
Validate macro name in MacroReferenceExpression constructor
Fix sandbox bypass: PHP code injection via _self / import macro reference
Fix deprecations in tests
Fix sandbox bypass in the `{% sandbox %}` tag when including a preloaded template
Encode single quotes as \x27 in Compiler::string()
Fix sandbox bypass: PHP code injection via {% use %} template name
Fix unbounded memoisation of `IntlDateFormatter` / `NumberFormatter`
Fix deprecation
[Profiler] Escape template and profile names in HtmlDumper
Bump version
Fix sandbox bypass: propagate sandbox state to checkArrow for source-policy sandboxing
...
# Conflicts:
# CHANGELOG
# doc/filters/spaceless.rst
# extra/cssinliner-extra/CssInlinerExtension.php
# extra/inky-extra/InkyExtension.php
# extra/markdown-extra/MarkdownExtension.php
# src/Environment.php
# src/ExpressionParser/Infix/DotExpressionParser.php
# src/Extension/CoreExtension.php
# src/Node/Expression/FilterExpression.php
# src/Node/Expression/FunctionExpression.php
# src/Node/Expression/TestExpression.php
# src/Node/ModuleNode.php
# src/NodeVisitor/SandboxNodeVisitor.php
# src/Resources/core.php
# src/TokenParser/SandboxTokenParser.php
# tests/Extension/SandboxTest.php
The name passed to MacroReferenceExpression is emitted as raw PHP in
compile() via "->{$name}(...)". Callers were expected to validate
the name, but a missing check led to CVE-2026-XXXXX (PHP code injection
via _self / import macro reference): defense-in-depth, validate the
name in the constructor so the class is safe by construction.
This is a defense-in-depth measure: callers must always concatenate the
result into a double-quoted PHP context, but if one ever (mistakenly)
embeds it inside a single-quoted PHP literal, an attacker-controlled
single quote in the source value could break out of that context. The
previous commit fixed exactly such a bug in ModuleNode for the {% use %}
template name.
Encoding ' as the hex escape \x27 guarantees that the emitted PHP source
never contains a literal single quote derived from user input, while the
decoded runtime value is unchanged. \' is not used because it is not a
recognized escape sequence in PHP double-quoted strings (the backslash
would be kept literally).
The HtmlDumper output is intended to be rendered in a browser, and the
template and macro/block names it interpolates are loader-controlled
(e.g. the key for ArrayLoader or a database row id), so they can carry
arbitrary HTML when an application stores templates under user-supplied
identifiers.
* origin/3.x:
Add a `needs_is_sandboxed` option for filters, functions, and tests
Bump version
Make embeds deterministic
[Doc] Document loose comparison in the `in` operator
[Doc] Reword whitespace control note about first-newline removal
Stop publishing extra package minor versions with no changes
Lazy load EscaperRuntime in EscaperExtension
Fix typo
Replace parent-child analogy in `doc/tags/extends.rst`
doc: Add missing toctree entries and fix ordering
Fix CHANGELOG
Bump version
Prepare the 3.24.0 release
This PR was merged into the 3.x branch.
Discussion
----------
Lazy load `EscaperRuntime` in `EscaperExtension`
This allows overriding `EscaperRuntime` via a custom runtime loader.
Previously, `EscaperExtension::setEnvironment()` was calling `$environment->getRuntime(EscaperRuntime::class)` eagerly. Since this method is called from `Environment::__construct()`, the runtime was resolved before any custom runtime loader could be injected, making it impossible to override `EscaperRuntime`.
- Required by https://github.com/symfony/symfony/pull/63929
Commits
-------
b73ab8cfd9 Lazy load EscaperRuntime in EscaperExtension
This PR was merged into the 3.x branch.
Discussion
----------
Add a `needs_is_sandboxed` option for filters, functions, and tests
This PR introduces a new `needs_is_sandboxed` option for Twig callables.
When set to `true`, Twig passes the current sandbox state (enabled/disabled) as a boolean to the callable. This lets filters, functions, and tests adapt their behavior depending on whether the sandbox is enabled or not.
Commits
-------
5462817da0 Add a `needs_is_sandboxed` option for filters, functions, and tests
Both filters consume HTML on the input side. Adding `pre_escape => 'html'`
makes the autoescaper escape attacker-controlled inputs before the filters
process them, so they are no longer reachable via `{{ user_input|inline_css }}`
or `{{ user_input|inky_to_html }}` without an explicit `|raw`.