Commit Graph

519 Commits

Author SHA1 Message Date
Alexandre Daubois 324fa60545 Fix sandbox bypass: PHP code injection via _self / import macro reference 2026-05-19 23:41:59 +02:00
Fabien Potencier aeb37f4801 Fix deprecations in tests 2026-05-19 23:05:30 +02:00
Alexandre Daubois 819c6a89fe Fix sandbox bypass in the {% sandbox %} tag when including a preloaded template 2026-05-19 23:00:58 +02:00
Fabien Potencier 679447fa29 Encode single quotes as \x27 in Compiler::string()
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).
2026-05-19 22:58:27 +02:00
Alexandre Daubois e9ff55f691 Fix sandbox bypass: PHP code injection via {% use %} template name 2026-05-19 22:50:45 +02:00
Fabien Potencier e494400ce4 Fix deprecation 2026-05-19 22:05:26 +02:00
Fabien Potencier a8eb5a8a74 security #cve-2026-46639 Fix sandbox bypass in object destructuring assignment (alexandre-daubois)
This PR was merged into the twig-3.x branch.
2026-05-19 21:37:04 +02:00
Fabien Potencier c8dfd62064 security #cve-2026-24425 Fix sandbox bypass: propagate Source to checkArrow for source-policy sandboxing (fabpot)
This PR was merged into the twig-3.x branch.
2026-05-19 21:30:51 +02:00
Fabien Potencier fd0760d972 feature #4795 Lazy load EscaperRuntime in EscaperExtension (GromNaN)
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
2026-05-17 07:52:38 +01:00
Fabien Potencier d0579ededc Fix sandbox bypass: propagate sandbox state to checkArrow for source-policy sandboxing 2026-05-16 18:56:08 +01:00
Fabien Potencier 5462817da0 Add a needs_is_sandboxed option for filters, functions, and tests 2026-05-16 18:52:17 +01:00
itsalmostchristmas 2b4aa4458a Make embeds deterministic 2026-05-16 09:48:23 +01:00
Jérôme Tamarelle b73ab8cfd9 Lazy load EscaperRuntime in EscaperExtension
Previously, setEnvironment() called getRuntime(EscaperRuntime::class) eagerly,
which prevented overriding EscaperRuntime via a custom runtime loader since
Environment::__construct() calls setEnvironment() before any loader can be injected.
2026-04-30 13:43:11 +02:00
Alexandre Daubois 3fe13f98f8 Fix sandbox bypass in object destructuring assignment 2026-04-28 12:59:06 +02:00
Fabien Potencier e56cdfdded bug #4778 Fix null coalescing operator with imported macros (fabpot)
This PR was merged into the 3.x branch.

Discussion
----------

Fix null coalescing operator with imported macros

Closes #4776

When using the null coalescing operator with a macro imported via the `from` tag, the `TemplateVariable` node inside `MacroReferenceExpression` was deep-cloned, causing the clone to generate a different `$macros` key than the one assigned by `AssignTemplateVariable`. This resulted in a `Call to a member function hasMacro() on null` error.

Commits
-------

efa004caab Fix null coalescing operator with imported macros
2026-02-25 10:38:36 +01:00
Fabien Potencier efa004caab Fix null coalescing operator with imported macros 2026-02-25 08:38:54 +01:00
Fabien Potencier e5eb95d0d7 Add getOperatorTokens() to ExpressionParserInterface to separate operator token registration from parser identity 2026-02-24 21:13:06 +01:00
brandonkelly a9ac993938 Ensure filters/attributes aren't mistaken for operators 2026-02-24 21:10:18 +01:00
Fabien Potencier 54d5c004b4 Deprecate passing a non-AbstractExpression node to Parser::setParent() 2026-02-22 15:28:59 +01:00
Fabien Potencier 751a187f07 feature #4748 Support short-circuiting in null-safe operator chains (HypeMC)
This PR was merged into the 3.x branch.

Discussion
----------

Support short-circuiting in null-safe operator chains

This PR adds short-circuiting for null-safe operator chains, using the same rules as PHP, `PropertyAccess`, and the `ExpressionLanguage`.

Previously, only the immediate null-safe access was guarded. With this change, as soon as a `null` is encountered at a null-safe access, the rest of the chain is skipped.

My approach was to move the null check outside of the `getAttribute()` calls so the expression can immediately return `null`, eg:

```twig
foo?.bar.baz
```

Before:

```php
yield $this->env
    ->getRuntime('Twig\Runtime\EscaperRuntime')
    ->escape(
        CoreExtension::getAttribute(
            $this->env,
            $this->source,
            (
                null === (
                    $_v0 = (
                        isset($context['foo']) || array_key_exists('foo', $context)
                            ? $context['foo']
                            : throw new RuntimeError('Variable "foo" does not exist.', 3, $this->source)
                    )
                )
                    ? null
                    : CoreExtension::getAttribute(
                        $this->env,
                        $this->source,
                        $_v0,
                        'bar',
                        [],
                        'any',
                        false,
                        false,
                        false,
                        3
                    )
            ),
            'baz',
            [],
            'any',
            false,
            false,
            false,
            3
        ),
        'html',
        null,
        true
    );
```

Now:

```php
yield $this->env
    ->getRuntime('Twig\Runtime\EscaperRuntime')
    ->escape(
        (
            null === (
                $_v0 = (
                    isset($context['foo']) || array_key_exists('foo', $context)
                        ? $context['foo']
                        : throw new RuntimeError('Variable "foo" does not exist.', 3, $this->source)
                )
            )
                ? null
                : CoreExtension::getAttribute(
                    $this->env,
                    $this->source,
                    CoreExtension::getAttribute(
                        $this->env,
                        $this->source,
                        $_v0,
                        'bar',
                        [],
                        'any',
                        false,
                        false,
                        false,
                        3
                    ),
                    'baz',
                    [],
                    'any',
                    false,
                    false,
                    false,
                    3
                )
        ),
        'html',
        null,
        true
    );
```

Commits
-------

d56e8e2dba Support short-circuiting in null-safe operator chains
2026-02-08 19:02:03 +01:00
Matthias Pigulla 04aa3df49f Add html_attr_relaxed escaping strategy 2026-02-08 18:59:07 +01:00
Fabien Potencier df893829f2 feature #4759 Add support for renaming variables in object destructuring (fabpot)
This PR was merged into the 3.x branch.

Discussion
----------

Add support for renaming variables in object destructuring

Closes #4747

Commits
-------

3cc1b5233c Add support for renaming variables in object destructuring
2026-02-07 14:22:40 +01:00
Fabien Potencier 861215c507 Fix CS 2026-02-07 09:07:38 +01:00
HypeMC d56e8e2dba Support short-circuiting in null-safe operator chains 2026-02-06 22:36:57 +01:00
Fabien Potencier 3cc1b5233c Add support for renaming variables in object destructuring 2026-02-06 22:12:59 +01:00
HypeMC a16ac6bd35 Fix null-safe operator test 2026-01-25 04:57:19 +01:00
Fabien Potencier 8a0f8acbdf Add support for object and mapping destructuring 2026-01-21 13:21:14 +01:00
Fabien Potencier bb99af3b39 Assignment operator array destructuring 2026-01-21 08:46:28 +01:00
Fabien Potencier bfbbef05f2 Add the = assignment operator 2026-01-19 16:39:57 +01:00
Felds Liscia 263c04fd1f Add null-safe operator 2026-01-17 14:57:47 +01:00
Fabien Potencier 94c8bdd6a6 Add === and !== operators 2026-01-14 09:58:48 +01:00
Fabien Potencier 2d741f9a16 bug #4731 Fix spread operator behavior (fabpot)
This PR was merged into the 3.x branch.

Discussion
----------

Fix spread operator behavior

Closes #4637

Commits
-------

2376bbaf3e Fix spread operator behavior
2026-01-13 10:14:41 +01:00
Fabien Potencier 8391928896 Fix deprecations 2026-01-12 09:52:14 +01:00
Fabien Potencier 2376bbaf3e Fix spread operator behavior 2026-01-12 08:54:38 +01:00
Younes ENNAJI 473653d19b [Core] Fix cycle() with non-countable ArrayAccess+Traversable objects 2025-11-23 13:20:23 +01:00
Thomas Cochard 40248e4ca9 CoreExtension : Make error message more readable 2025-11-15 08:57:35 +01:00
Nicolas Grekas 9ae75d315c Fix accessing arrays with stringable objects as key 2025-10-24 14:19:16 +02:00
Simon André e4d7915702 Compile 'index' with repr (not string) in EmbedNode
Before this fix, the generated Template code had quotes around the
index (integer) parameter value.
2025-09-25 08:28:51 +02:00
Vincent Langlet cc12df995c Introduce registerUndefinedTestCallback 2025-09-16 07:40:27 +02:00
Vincent Langlet 30977bdea9 Support two words test guard 2025-09-15 07:51:41 +02:00
Nicolas Grekas 85a4817128 CS fixes 2025-07-29 10:07:07 +02:00
Fabien Potencier 8a7d912deb feature #3916 Create attributes AsTwigFilter, AsTwigFunction and AsTwigTest to ease extension development (GromNaN)
This PR was squashed before being merged into the 3.x branch.

Discussion
----------

Create attributes `AsTwigFilter`, `AsTwigFunction` and `AsTwigTest` to ease extension development

One drawback to writing extensions at present is that the declaration of functions/filters/tests is not directly adjacent to the methods. It's worse for runtime extensions because they need to be in 2 different classes. See [`SerializerExtension`](https://github.com/symfony/symfony/blob/7.0/src/Symfony/Bridge/Twig/Extension/SerializerExtension.php) and [`SerializerRuntime`](https://github.com/symfony/symfony/blob/7.0/src/Symfony/Bridge/Twig/Extension/SerializerRuntime.php) as an example.

By using attributes for filters, functions and tests definition, we can make writing extensions more expressive, and use reflection to detect particular options (`needs_environment`, `needs_context`, `is_variadic`).

Example if we implemented the `formatDate` filter: https://github.com/twigphp/Twig/blob/aeeec9a5e907a79e50a6bb78979154599401726e/extra/intl-extra/IntlExtension.php#L392-L395

By using the `AsTwigFilter` attribute, it is not necessary to create the `getFilters()` method. The `needs_environment` option is detected from method signature. The name is still required as the method naming convention (camelCase) doesn't match with Twig naming convention (snake_case).

```php
use Twig\Extension\Attribute\AsTwigFilter;

class IntlExtension
{
    #[AsTwigFilter(name: 'format_date')]
    public function formatDate(Environment $env, $date, ?string $dateFormat = 'medium', string $pattern = '', $timezone = null, string $calendar = 'gregorian', string $locale = null): string
    {
        return $this->formatDateTime($env, $date, $dateFormat, 'none', $pattern, $timezone, $calendar, $locale);
    }
}
```

This approach does not totally replace the current definition of extensions, which is still necessary for advanced needs. It does, however, make for more pleasant reading and writing.

This makes writing lazy-loaded runtime extension the easiest way to create Twig extension in Symfony: https://github.com/symfony/symfony/pull/52748

Related to https://github.com/symfony/symfony/issues/50016

Is there any need to cache the parsing of method attributes? They are only read at compile time, but that can have a performance impact during development or when using dynamic templates.

Commits
-------

5886907b28 Create attributes `AsTwigFilter`, `AsTwigFunction` and `AsTwigTest` to ease extension development
2025-03-24 22:34:23 +01:00
Jérôme Tamarelle 5886907b28 Create attributes AsTwigFilter, AsTwigFunction and AsTwigTest to ease extension development 2025-03-24 22:34:21 +01:00
Fabien Potencier 09f9a22310 Fix wrong array index (again) 2025-03-21 09:42:17 +01:00
Fabien Potencier da64c42dc2 Add tests 2025-02-28 08:52:34 +01:00
Fabien Potencier b86fd0a059 Remove obsolete files 2025-02-26 22:13:11 +01:00
Fabien Potencier 56204e951a Move some tests 2025-02-26 22:10:10 +01:00
Fabien Potencier 7b43bd0fd7 Fix Error when the trace has Twig file/line information instead of the original PHP info 2025-02-26 21:02:58 +01:00
Fabien Potencier 4a121d90c6 Sync Error file and line 2025-02-26 13:26:24 +01:00
Fabien Potencier 7cde13f6c9 bug #4595 Improve error reporting (fabpot)
This PR was merged into the 3.x branch.

Discussion
----------

Improve error reporting

Commits
-------

d5dc3eee99 Improve error reporting
2025-02-22 18:27:58 +01:00