Skip to content

Commit d4a3026

Browse files
committed
Fix sandbox handling for __toString()
1 parent 98e7f71 commit d4a3026

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed

src/Extension/SandboxExtension.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,14 @@ public function checkPropertyAllowed($obj, $property, int $lineno = -1, ?Source
119119

120120
public function ensureToStringAllowed($obj, int $lineno = -1, ?Source $source = null)
121121
{
122+
if (\is_array($obj)) {
123+
foreach ($obj as $v) {
124+
$this->ensureToStringAllowed($v, $lineno, $source);
125+
}
126+
127+
return $obj;
128+
}
129+
122130
if ($this->isSandboxed($source) && $obj instanceof \Stringable) {
123131
try {
124132
$this->policy->checkMethodAllowed($obj, '__toString');

src/NodeVisitor/SandboxNodeVisitor.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@
1515
use Twig\Node\CheckSecurityCallNode;
1616
use Twig\Node\CheckSecurityNode;
1717
use Twig\Node\CheckToStringNode;
18+
use Twig\Node\Expression\ArrayExpression;
1819
use Twig\Node\Expression\Binary\ConcatBinary;
1920
use Twig\Node\Expression\Binary\RangeBinary;
2021
use Twig\Node\Expression\FilterExpression;
2122
use Twig\Node\Expression\FunctionExpression;
2223
use Twig\Node\Expression\GetAttrExpression;
2324
use Twig\Node\Expression\NameExpression;
25+
use Twig\Node\Expression\Unary\SpreadUnary;
2426
use Twig\Node\ModuleNode;
2527
use Twig\Node\Node;
2628
use Twig\Node\Nodes;
@@ -121,7 +123,18 @@ private function wrapNode(Node $node, string $name): void
121123
{
122124
$expr = $node->getNode($name);
123125
if (($expr instanceof NameExpression || $expr instanceof GetAttrExpression) && !$expr->isGenerator()) {
124-
$node->setNode($name, new CheckToStringNode($expr));
126+
// Simplify in 4.0 as the spread attribute has been removed there
127+
$new = new CheckToStringNode($expr);
128+
if ($expr->hasAttribute('spread')) {
129+
$new->setAttribute('spread', $expr->getAttribute('spread'));
130+
}
131+
$node->setNode($name, $new);
132+
} elseif ($expr instanceof SpreadUnary) {
133+
$this->wrapNode($expr, 'node');
134+
} elseif ($expr instanceof ArrayExpression) {
135+
foreach ($expr as $name => $_) {
136+
$this->wrapNode($expr, $name);
137+
}
125138
}
126139
}
127140

tests/Extension/SandboxTest.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ protected function setUp(): void
4242
'obj' => new FooObject(),
4343
'arr' => ['obj' => new FooObject()],
4444
'child_obj' => new ChildClass(),
45+
'some_array' => [5, 6, 7, new FooObject()],
4546
];
4647

4748
self::$templates = [
@@ -261,10 +262,10 @@ public function testSandboxUnallowedProperty()
261262
*/
262263
public function testSandboxUnallowedToString($template)
263264
{
264-
$twig = $this->getEnvironment(true, [], ['index' => $template], [], ['upper'], ['Twig\Tests\Extension\FooObject' => 'getAnotherFooObject'], [], ['random']);
265+
$twig = $this->getEnvironment(true, [], ['index' => $template], [], ['upper', 'join', 'replace'], ['Twig\Tests\Extension\FooObject' => 'getAnotherFooObject'], [], ['random']);
265266
try {
266267
$twig->load('index')->render(self::$params);
267-
$this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
268+
$this->fail('Sandbox throws a SecurityError exception if an unallowed method "__toString()" method is called in the template');
268269
} catch (SecurityNotAllowedMethodError $e) {
269270
$this->assertEquals('Twig\Tests\Extension\FooObject', $e->getClassName(), 'Exception should be raised on the "Twig\Tests\Extension\FooObject" class');
270271
$this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method');
@@ -287,6 +288,16 @@ public static function getSandboxUnallowedToStringTests()
287288
'object_chain_and_function' => ['{{ random(obj.anotherFooObject) }}'],
288289
'concat' => ['{{ obj ~ "" }}'],
289290
'concat_again' => ['{{ "" ~ obj }}'],
291+
'object_in_arguments' => ['{{ "__toString"|replace({"__toString": obj}) }}'],
292+
'object_in_array' => ['{{ [12, "foo", obj]|join(", ") }}'],
293+
'object_in_array_var' => ['{{ some_array|join(", ") }}'],
294+
'object_in_array_nested' => ['{{ [12, "foo", [12, "foo", obj]]|join(", ") }}'],
295+
'object_in_array_var_nested' => ['{{ [12, "foo", some_array]|join(", ") }}'],
296+
'object_in_array_dynamic_key' => ['{{ {(obj): "foo"}|join(", ") }}'],
297+
'object_in_array_dynamic_key_nested' => ['{{ {"foo": { (obj): "foo" }}|join(", ") }}'],
298+
'context' => ['{{ _context|join(", ") }}'],
299+
'spread_array_operator' => ['{{ [1, 2, ...[5, 6, 7, obj]]|join(",") }}'],
300+
'spread_array_operator_var' => ['{{ [1, 2, ...some_array]|join(",") }}'],
290301
];
291302
}
292303

0 commit comments

Comments
 (0)