mirror of
https://github.com/twigphp/Twig.git
synced 2026-08-30 03:57:21 +00:00
Merge branch '3.x' into 4.x
* 3.x: Clarify the security scope for untrusted templates Normalize destructuring assignment targets Deprecate duplicate macro definitions # Conflicts: # CHANGELOG # doc/deprecated.rst # tests/ExpressionParserTest.php
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
Security Policy
|
||||
===============
|
||||
|
||||
DO NOT PUBLISH SECURITY REPORTS PUBLICLY.
|
||||
|
||||
Reporting a Security Issue
|
||||
--------------------------
|
||||
|
||||
If you find an issue that might have security implications, send a report to
|
||||
security[at]symfony.com.
|
||||
|
||||
The full [security reporting and resolution process][1] is described in the
|
||||
Symfony documentation.
|
||||
|
||||
Security Scope for Untrusted Templates
|
||||
--------------------------------------
|
||||
|
||||
Twig treats template source as trusted code unless the template is rendered in
|
||||
the [Twig sandbox][2]. The regular Twig environment is not a security boundary.
|
||||
|
||||
Applications that render templates supplied by untrusted users must enable and
|
||||
correctly configure the Twig sandbox. Any behavior that is possible because an
|
||||
application renders an untrusted template without the sandbox is not a security
|
||||
issue in Twig and must not be reported as one.
|
||||
|
||||
Reports about untrusted templates are in scope only when they demonstrate a
|
||||
sandbox restriction bypass while the sandbox is enabled and its security policy
|
||||
does not allow the demonstrated operation.
|
||||
|
||||
[1]: https://symfony.com/security
|
||||
[2]: https://twig.symfony.com/doc/3.x/sandbox.html
|
||||
@@ -3,6 +3,14 @@ Twig Sandbox
|
||||
|
||||
The ``sandbox`` extension can be used to evaluate untrusted code.
|
||||
|
||||
.. warning::
|
||||
|
||||
Twig treats template source as trusted code by default. If an application
|
||||
accepts templates from untrusted users, it must enable and correctly
|
||||
configure the sandbox. The regular Twig environment is not a security
|
||||
boundary, and any behavior caused by rendering an untrusted template
|
||||
without the sandbox is not a security issue in Twig.
|
||||
|
||||
Registering the Sandbox
|
||||
-----------------------
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ use Twig\Node\Expression\Binary\AbstractBinary;
|
||||
use Twig\Node\Expression\Binary\ObjectDestructuringSetBinary;
|
||||
use Twig\Node\Expression\Binary\SequenceDestructuringSetBinary;
|
||||
use Twig\Node\Expression\Binary\SetBinary;
|
||||
use Twig\Node\Expression\Variable\AssignContextVariable;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
@@ -49,6 +50,12 @@ class AssignmentExpressionParser extends BinaryOperatorExpressionParser
|
||||
};
|
||||
|
||||
if ($left instanceof ArrayExpression) {
|
||||
foreach ($left->getKeyValuePairs() as $i => $pair) {
|
||||
if ($pair['value'] instanceof ContextVariable && !$pair['value'] instanceof AssignContextVariable) {
|
||||
$left->setNode(2 * $i + 1, new AssignContextVariable($pair['value']->getAttribute('name'), $pair['value']->getTemplateLine()));
|
||||
}
|
||||
}
|
||||
|
||||
if ($left->isSequence()) {
|
||||
return new SequenceDestructuringSetBinary($left, $right, $token->getLine());
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use Twig\Error\SyntaxError;
|
||||
use Twig\Extension\SandboxExtension;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Node\Expression\Variable\AssignContextVariable;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
@@ -37,7 +37,7 @@ class ObjectDestructuringSetBinary extends AbstractBinary
|
||||
throw new \LogicException('Left side must be ArrayExpression for object/mapping destructuring.');
|
||||
}
|
||||
foreach ($left->getKeyValuePairs() as $pair) {
|
||||
if (!$pair['value'] instanceof ContextVariable) {
|
||||
if (!$pair['value'] instanceof AssignContextVariable) {
|
||||
throw new SyntaxError(\sprintf('Cannot assign to "%s", only variables can be assigned in object/mapping destructuring.', $pair['value']::class), $lineno);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ use Twig\Error\SyntaxError;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\EmptyExpression;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Node\Expression\Variable\AssignContextVariable;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
@@ -35,7 +35,7 @@ class SequenceDestructuringSetBinary extends AbstractBinary
|
||||
foreach ($left->getKeyValuePairs() as $pair) {
|
||||
if ($pair['value'] instanceof EmptyExpression) {
|
||||
$this->variables[] = null;
|
||||
} elseif ($pair['value'] instanceof ContextVariable) {
|
||||
} elseif ($pair['value'] instanceof AssignContextVariable) {
|
||||
$this->variables[] = $pair['value']->getAttribute('name');
|
||||
} else {
|
||||
throw new SyntaxError(\sprintf('Cannot assign to "%s", only variables can be assigned in sequence destructuring.', $pair['value']::class), $lineno);
|
||||
|
||||
@@ -284,6 +284,10 @@ class Parser
|
||||
|
||||
public function setMacro(string $name, MacroNode $node): void
|
||||
{
|
||||
if (isset($this->macros[$name])) {
|
||||
trigger_deprecation('twig/twig', '3.29', 'Defining the macro "%s" more than once in "%s" is deprecated and will throw a SyntaxError in Twig 4.0 (first definition at line %d, second at line %d).', $name, $this->stream->getSourceContext()->getName(), $this->macros[$name]->getTemplateLine(), $node->getTemplateLine());
|
||||
}
|
||||
|
||||
$this->macros[$name] = $node;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,9 +34,13 @@ use Twig\Extension\AbstractExtension;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\Binary\ConcatBinary;
|
||||
use Twig\Node\Expression\Binary\ObjectDestructuringSetBinary;
|
||||
use Twig\Node\Expression\Binary\SequenceDestructuringSetBinary;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\EmptyExpression;
|
||||
use Twig\Node\Expression\Unary\AbstractUnary;
|
||||
use Twig\Node\Expression\Unary\SpreadUnary;
|
||||
use Twig\Node\Expression\Variable\AssignContextVariable;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Parser;
|
||||
use Twig\Source;
|
||||
@@ -231,6 +235,35 @@ class ExpressionParserTest extends TestCase
|
||||
$env->compileSource(new Source('{{ [1,,2] }}', 'index'));
|
||||
}
|
||||
|
||||
public function testSequenceDestructuringUsesAssignmentTargets(): void
|
||||
{
|
||||
$env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]);
|
||||
$parser = new Parser($env);
|
||||
$node = $parser->parse($env->tokenize(new Source('{{ ([first, , third] = values) }}', 'index')))->getNode('body')->getNode('0')->getNode('expr');
|
||||
|
||||
$this->assertInstanceOf(SequenceDestructuringSetBinary::class, $node);
|
||||
$pairs = $node->getNode('left')->getKeyValuePairs();
|
||||
$this->assertSame(AssignContextVariable::class, $pairs[0]['value']::class);
|
||||
$this->assertSame('first', $pairs[0]['value']->getAttribute('name'));
|
||||
$this->assertSame(EmptyExpression::class, $pairs[1]['value']::class);
|
||||
$this->assertSame(AssignContextVariable::class, $pairs[2]['value']::class);
|
||||
$this->assertSame('third', $pairs[2]['value']->getAttribute('name'));
|
||||
}
|
||||
|
||||
public function testObjectDestructuringUsesAssignmentTargets(): void
|
||||
{
|
||||
$env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]);
|
||||
$parser = new Parser($env);
|
||||
$node = $parser->parse($env->tokenize(new Source('{{ ({name: user_name} = user) }}', 'index')))->getNode('body')->getNode('0')->getNode('expr');
|
||||
|
||||
$this->assertInstanceOf(ObjectDestructuringSetBinary::class, $node);
|
||||
$pair = $node->getNode('left')->getKeyValuePairs()[0];
|
||||
$this->assertSame(ConstantExpression::class, $pair['key']::class);
|
||||
$this->assertSame('name', $pair['key']->getAttribute('value'));
|
||||
$this->assertSame(AssignContextVariable::class, $pair['value']::class);
|
||||
$this->assertSame('user_name', $pair['value']->getAttribute('name'));
|
||||
}
|
||||
|
||||
#[DataProvider('getTestsForString')]
|
||||
public function testStringExpression($template, $expected): void
|
||||
{
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
--TEST--
|
||||
Defining a macro more than once is deprecated
|
||||
--DEPRECATION--
|
||||
Since twig/twig 3.29: Defining the macro "greet" more than once in "index.twig" is deprecated and will throw a SyntaxError in Twig 4.0 (first definition at line 3, second at line 4).
|
||||
--TEMPLATE--
|
||||
{% import _self as macros %}
|
||||
{% macro greet() %}first{% endmacro %}
|
||||
{% macro greet() %}second{% endmacro %}
|
||||
{{ macros.greet() }}
|
||||
--DATA--
|
||||
return []
|
||||
--EXPECT--
|
||||
second
|
||||
Reference in New Issue
Block a user