mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-12 10:26:32 +00:00
Merge branch '3.x' into 4.x
* 3.x: Document sandbox handling of magic __call() methods Add sandbox tests for methods routed through __call() # Conflicts: # doc/sandbox.rst # tests/Extension/SandboxTest.php
This commit is contained in:
@@ -56,6 +56,22 @@ Everything else won't be allowed and will generate a
|
||||
Note that native array-like classes (like ``ArrayObject``) are always
|
||||
allowed, you don't need to configure them.
|
||||
|
||||
.. note::
|
||||
|
||||
When an attribute resolves through a PHP magic ``__call()`` method (the
|
||||
class has no real method or property with that name), the sandbox checks
|
||||
the **virtual method name written in the template**, not ``__call``. For
|
||||
example, ``{{ article.slug }}`` on an object that handles ``slug`` via
|
||||
``__call()`` requires ``slug`` in the method allow-list::
|
||||
|
||||
$methods = [
|
||||
'Article' => ['slug'],
|
||||
];
|
||||
|
||||
Allow-listing ``__call`` itself has no effect: it would only match a
|
||||
template that literally writes ``{{ article.__call }}``. Allow each virtual
|
||||
method by its own name so the policy stays granular.
|
||||
|
||||
Marking Filters, Functions, Tests, and Tags as Always Allowed
|
||||
-------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -588,6 +588,54 @@ class SandboxTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function testSandboxAllowedMagicCallMethod(): void
|
||||
{
|
||||
$twig = $this->getEnvironment(true, [], ['index' => '{{ o.hello }}'], [], [], [MagicCallObject::class => 'hello']);
|
||||
$this->assertSame('call:hello', $twig->load('index')->render(['o' => new MagicCallObject()]), 'Sandbox allows a virtual method routed through __call() when its name is allowed');
|
||||
}
|
||||
|
||||
public function testSandboxUnallowedMagicCallMethod(): void
|
||||
{
|
||||
$twig = $this->getEnvironment(true, [], ['index' => '{{ o.hello }}']);
|
||||
try {
|
||||
$twig->load('index')->render(['o' => new MagicCallObject()]);
|
||||
$this->fail('Sandbox throws a SecurityError exception if a virtual method routed through __call() is not allowed');
|
||||
} catch (SecurityNotAllowedPropertyError $e) {
|
||||
$this->assertEquals(MagicCallObject::class, $e->getClassName());
|
||||
$this->assertEquals('hello', $e->getPropertyName());
|
||||
}
|
||||
}
|
||||
|
||||
public function testSandboxUnallowedMagicCallMethodWithMethodSyntax(): void
|
||||
{
|
||||
$twig = $this->getEnvironment(true, [], ['index' => '{{ o.hello() }}']);
|
||||
try {
|
||||
$twig->load('index')->render(['o' => new MagicCallObject()]);
|
||||
$this->fail('Sandbox throws a SecurityError exception if a virtual method routed through __call() is not allowed');
|
||||
} catch (SecurityNotAllowedMethodError $e) {
|
||||
$this->assertEquals(MagicCallObject::class, $e->getClassName());
|
||||
$this->assertEquals('hello', $e->getMethodName());
|
||||
}
|
||||
}
|
||||
|
||||
public function testSandboxAllowingCallLiteralDoesNotAllowMagicCallMethod(): void
|
||||
{
|
||||
$twig = $this->getEnvironment(true, [], ['index' => '{{ o.hello }}'], [], [], [MagicCallObject::class => '__call']);
|
||||
try {
|
||||
$twig->load('index')->render(['o' => new MagicCallObject()]);
|
||||
$this->fail('Sandbox does not allow every virtual method just because "__call" is allowed');
|
||||
} catch (SecurityNotAllowedPropertyError $e) {
|
||||
$this->assertEquals(MagicCallObject::class, $e->getClassName());
|
||||
$this->assertEquals('hello', $e->getPropertyName());
|
||||
}
|
||||
}
|
||||
|
||||
public function testSandboxFallsBackToMagicCallMethodForUnallowedProperty(): void
|
||||
{
|
||||
$twig = $this->getEnvironment(true, [], ['index' => '{{ o.secret }}'], [], [], [MagicCallObject::class => 'secret']);
|
||||
$this->assertSame('call:secret', $twig->load('index')->render(['o' => new MagicCallObject()]), 'Sandbox falls back to __call() when a real property is not allowed but the method is');
|
||||
}
|
||||
|
||||
#[DataProvider('getSandboxUnallowedToStringTests')]
|
||||
public function testSandboxUnallowedToString($template): void
|
||||
{
|
||||
@@ -1894,6 +1942,16 @@ class MagicObject
|
||||
}
|
||||
}
|
||||
|
||||
class MagicCallObject
|
||||
{
|
||||
public $secret = 'secret';
|
||||
|
||||
public function __call($name, $arguments)
|
||||
{
|
||||
return 'call:'.$name;
|
||||
}
|
||||
}
|
||||
|
||||
// Plain object without __toString: column tests exercise property access, not
|
||||
// string coercion, so the array elements must not be Stringable to avoid
|
||||
// triggering the generic filter-input __toString sandbox check.
|
||||
|
||||
Reference in New Issue
Block a user