Skip to content

Commit 2bb8c24

Browse files
fabpotnicolas-grekas
authored andcommittedNov 5, 2024
Fix sandbox handling for __toString()
1 parent 126b2c9 commit 2bb8c24

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed
 

‎src/Extension/SandboxExtension.php

+8
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

+14-1
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\PrintNode;
@@ -120,7 +122,18 @@ private function wrapNode(Node $node, string $name): void
120122
{
121123
$expr = $node->getNode($name);
122124
if (($expr instanceof NameExpression || $expr instanceof GetAttrExpression) && !$expr->isGenerator()) {
123-
$node->setNode($name, new CheckToStringNode($expr));
125+
// Simplify in 4.0 as the spread attribute has been removed there
126+
$new = new CheckToStringNode($expr);
127+
if ($expr->hasAttribute('spread')) {
128+
$new->setAttribute('spread', $expr->getAttribute('spread'));
129+
}
130+
$node->setNode($name, $new);
131+
} elseif ($expr instanceof SpreadUnary) {
132+
$this->wrapNode($expr, 'node');
133+
} elseif ($expr instanceof ArrayExpression) {
134+
foreach ($expr as $name => $_) {
135+
$this->wrapNode($expr, $name);
136+
}
124137
}
125138
}
126139

‎tests/Extension/SandboxTest.php

+13-2
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 = [
@@ -246,10 +247,10 @@ public function testSandboxUnallowedProperty()
246247
*/
247248
public function testSandboxUnallowedToString($template)
248249
{
249-
$twig = $this->getEnvironment(true, [], ['index' => $template], [], ['upper'], ['Twig\Tests\Extension\FooObject' => 'getAnotherFooObject'], [], ['random']);
250+
$twig = $this->getEnvironment(true, [], ['index' => $template], [], ['upper', 'join', 'replace'], ['Twig\Tests\Extension\FooObject' => 'getAnotherFooObject'], [], ['random']);
250251
try {
251252
$twig->load('index')->render(self::$params);
252-
$this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
253+
$this->fail('Sandbox throws a SecurityError exception if an unallowed method "__toString()" method is called in the template');
253254
} catch (SecurityNotAllowedMethodError $e) {
254255
$this->assertEquals('Twig\Tests\Extension\FooObject', $e->getClassName(), 'Exception should be raised on the "Twig\Tests\Extension\FooObject" class');
255256
$this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method');
@@ -272,6 +273,16 @@ public static function getSandboxUnallowedToStringTests()
272273
'object_chain_and_function' => ['{{ random(obj.anotherFooObject) }}'],
273274
'concat' => ['{{ obj ~ "" }}'],
274275
'concat_again' => ['{{ "" ~ obj }}'],
276+
'object_in_arguments' => ['{{ "__toString"|replace({"__toString": obj}) }}'],
277+
'object_in_array' => ['{{ [12, "foo", obj]|join(", ") }}'],
278+
'object_in_array_var' => ['{{ some_array|join(", ") }}'],
279+
'object_in_array_nested' => ['{{ [12, "foo", [12, "foo", obj]]|join(", ") }}'],
280+
'object_in_array_var_nested' => ['{{ [12, "foo", some_array]|join(", ") }}'],
281+
'object_in_array_dynamic_key' => ['{{ {(obj): "foo"}|join(", ") }}'],
282+
'object_in_array_dynamic_key_nested' => ['{{ {"foo": { (obj): "foo" }}|join(", ") }}'],
283+
'context' => ['{{ _context|join(", ") }}'],
284+
'spread_array_operator' => ['{{ [1, 2, ...[5, 6, 7, obj]]|join(",") }}'],
285+
'spread_array_operator_var' => ['{{ [1, 2, ...some_array]|join(",") }}'],
275286
];
276287
}
277288

0 commit comments

Comments
 (0)
Please sign in to comment.