Skip to content

Commit cafc608

Browse files
committedNov 6, 2024
Fix sandbox handling for __toString()
1 parent ff063af commit cafc608

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) && \is_object($obj) && method_exists($obj, '__toString')) {
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
@@ -38,6 +38,7 @@ protected function setUp(): void
3838
'obj' => new FooObject(),
3939
'arr' => ['obj' => new FooObject()],
4040
'child_obj' => new ChildClass(),
41+
'some_array' => [5, 6, 7, new FooObject()],
4142
];
4243

4344
self::$templates = [
@@ -184,10 +185,10 @@ public function testSandboxUnallowedProperty()
184185
*/
185186
public function testSandboxUnallowedToString($template)
186187
{
187-
$twig = $this->getEnvironment(true, [], ['index' => $template], [], ['upper'], ['Twig\Tests\Extension\FooObject' => 'getAnotherFooObject'], [], ['random']);
188+
$twig = $this->getEnvironment(true, [], ['index' => $template], [], ['upper', 'join', 'replace'], ['Twig\Tests\Extension\FooObject' => 'getAnotherFooObject'], [], ['random']);
188189
try {
189190
$twig->load('index')->render(self::$params);
190-
$this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
191+
$this->fail('Sandbox throws a SecurityError exception if an unallowed method "__toString()" method is called in the template');
191192
} catch (SecurityNotAllowedMethodError $e) {
192193
$this->assertEquals('Twig\Tests\Extension\FooObject', $e->getClassName(), 'Exception should be raised on the "Twig\Tests\Extension\FooObject" class');
193194
$this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method');
@@ -210,6 +211,16 @@ public function getSandboxUnallowedToStringTests()
210211
'object_chain_and_function' => ['{{ random(obj.anotherFooObject) }}'],
211212
'concat' => ['{{ obj ~ "" }}'],
212213
'concat_again' => ['{{ "" ~ obj }}'],
214+
'object_in_arguments' => ['{{ "__toString"|replace({"__toString": obj}) }}'],
215+
'object_in_array' => ['{{ [12, "foo", obj]|join(", ") }}'],
216+
'object_in_array_var' => ['{{ some_array|join(", ") }}'],
217+
'object_in_array_nested' => ['{{ [12, "foo", [12, "foo", obj]]|join(", ") }}'],
218+
'object_in_array_var_nested' => ['{{ [12, "foo", some_array]|join(", ") }}'],
219+
'object_in_array_dynamic_key' => ['{{ {(obj): "foo"}|join(", ") }}'],
220+
'object_in_array_dynamic_key_nested' => ['{{ {"foo": { (obj): "foo" }}|join(", ") }}'],
221+
'context' => ['{{ _context|join(", ") }}'],
222+
'spread_array_operator' => ['{{ [1, 2, ...[5, 6, 7, obj]]|join(",") }}'],
223+
'spread_array_operator_var' => ['{{ [1, 2, ...some_array]|join(",") }}'],
213224
];
214225
}
215226

0 commit comments

Comments
 (0)