Fix merge conflict resolution

This commit is contained in:
Fabien Potencier
2026-06-02 14:10:55 +02:00
parent 19099de78e
commit a8e9d3b14a
3 changed files with 10 additions and 163 deletions
+2 -58
View File
@@ -12,7 +12,6 @@
namespace Twig\Tests\Extension;
use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Twig\Environment;
use Twig\Extension\SandboxExtension;
use Twig\Loader\ArrayLoader;
@@ -21,8 +20,6 @@ use Twig\Sandbox\SecurityNotAllowedFunctionError;
use Twig\Sandbox\SecurityNotAllowedTagError;
use Twig\Sandbox\SecurityPolicy;
use Twig\Sandbox\SecurityPolicyInterface;
use Twig\Sandbox\SourcePolicyInterface;
use Twig\Source;
/**
* Regression tests for the sandbox filter/tag/function allow-list bypass that
@@ -35,8 +32,6 @@ use Twig\Source;
*/
class SandboxStateChangeTest extends TestCase
{
use ExpectDeprecationTrait;
public function testEnableSandboxAfterFirstRender()
{
[$twig, $sandbox] = $this->build(['t' => '{{ "foo"|upper }}'], new SecurityPolicy(allowedFilters: []), false);
@@ -99,32 +94,6 @@ class SandboxStateChangeTest extends TestCase
$this->assertSame('FOO', $twig->render('t'));
}
/**
* @group legacy
*/
public function testSourcePolicyDecisionFlip()
{
$this->expectDeprecation('Since twig/twig 3.27.0: The "Twig\Sandbox\SourcePolicyInterface" interface is deprecated with no replacement, do not pass an instance to "Twig\Extension\SandboxExtension".');
$sourcePolicy = new class implements SourcePolicyInterface {
public array $sandboxFor = [];
public function enableSandbox(Source $source): bool
{
return \in_array($source->getName(), $this->sandboxFor, true);
}
};
[$twig] = $this->build(['t' => '{{ "foo"|upper }}'], new SecurityPolicy(allowedFilters: []), false, $sourcePolicy);
$this->assertSame('FOO', $twig->render('t'));
$sourcePolicy->sandboxFor = ['t'];
$this->expectException(SecurityNotAllowedFilterError::class);
$twig->render('t');
}
public function testPreWarmedParentTemplateThroughExtends()
{
$templates = [
@@ -194,31 +163,6 @@ class SandboxStateChangeTest extends TestCase
$twig->render('caller.twig');
}
/**
* @group legacy
*/
public function testSandboxTagAroundIncludeOfPreWarmedTemplate()
{
$this->expectDeprecation('Since twig/twig 3.15: The "sandbox" tag is deprecated in "wrapper" at line 1.');
$templates = [
'wrapper' => '{% sandbox %}{% include "user" %}{% endsandbox %}',
'user' => '{% extends "shared" %}{% block c %}user{% endblock %}',
'shared' => '{% block c %}{% endblock %}{{ "ok"|upper }}',
];
$policy = new SecurityPolicy(
allowedTags: ['extends', 'block', 'include'],
allowedFilters: ['escape'],
);
[$twig] = $this->build($templates, $policy, false);
$this->assertSame('OK', $twig->render('shared'));
$this->expectException(SecurityNotAllowedFilterError::class);
$this->expectExceptionMessage('Filter "upper" is not allowed');
$twig->render('wrapper');
}
public function testTagBypassThroughPreWarmedParent()
{
$templates = [
@@ -392,10 +336,10 @@ class SandboxStateChangeTest extends TestCase
/**
* @return array{0: Environment, 1: SandboxExtension}
*/
private function build(array $templates, SecurityPolicyInterface $policy, bool $sandboxed, ?SourcePolicyInterface $sourcePolicy = null): array
private function build(array $templates, SecurityPolicyInterface $policy, bool $sandboxed): array
{
$twig = new Environment(new ArrayLoader($templates), ['cache' => false, 'autoescape' => false]);
$sandbox = new SandboxExtension($policy, $sandboxed, $sourcePolicy);
$sandbox = new SandboxExtension($policy, $sandboxed);
$twig->addExtension($sandbox);
return [$twig, $sandbox];
+6 -102
View File
@@ -23,6 +23,7 @@ namespace Twig\Tests\Extension;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Twig\Environment;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\Extension\SandboxExtension;
use Twig\Extension\StringLoaderExtension;
@@ -980,9 +981,7 @@ EOF
}
}
/**
* @dataProvider getStringableTraversableBypassTemplates
*/
#[DataProvider('getStringableTraversableBypassTemplates')]
public function testSandboxBlocksToStringInStringableTraversable(string $template)
{
$twig = $this->getEnvironment(
@@ -1009,43 +1008,6 @@ EOF
yield 'replace' => ['{{ "__toString"|replace(stringable_iterator_map) }}'];
}
/**
* @group legacy
*
* @dataProvider getStringableTraversableBypassTemplates
*/
public function testSourcePolicySandboxBlocksToStringInStringableTraversable(string $template)
{
$this->expectDeprecation('Since twig/twig 3.27.0: The "Twig\Sandbox\SourcePolicyInterface" interface is deprecated with no replacement, do not pass an instance to "Twig\Extension\SandboxExtension".');
$sourcePolicy = new class implements SourcePolicyInterface {
public function enableSandbox(Source $source): bool
{
return true;
}
};
$twig = $this->getEnvironment(
false,
[],
['index' => $template],
[],
['join', 'replace'],
['Twig\Tests\Extension\StringableTraversableObject' => ['__tostring']],
[],
[],
$sourcePolicy,
);
try {
$twig->load('index')->render(self::$params);
$this->fail('Sandbox should block __toString on objects yielded by a Stringable+Traversable container under a SourcePolicyInterface-only sandbox.');
} catch (SecurityNotAllowedMethodError $e) {
$this->assertSame('Twig\Tests\Extension\FooObject', $e->getClassName());
$this->assertSame('__tostring', $e->getMethodName());
}
}
public function testSandboxAllowsPrintingStringableTraversableWhenToStringAllowed()
{
// Printing the container itself yields its `__toString()` value. The
@@ -1066,9 +1028,7 @@ EOF
$this->assertSame('stringable-traversable', $twig->load('index')->render($params));
}
/**
* @dataProvider getCyclicTraversableTemplates
*/
#[DataProvider('getCyclicTraversableTemplates')]
public function testSandboxHandlesCyclicTraversableWithoutStackOverflow(string $template)
{
// A self-referencing IteratorAggregate must not cause the sandbox policy
@@ -1095,56 +1055,6 @@ EOF
yield 'spread' => ['{{ ["a", ...obj]|join(",") }}'];
}
/**
* @group legacy
*/
public function testSourcePolicySandboxBlocksToStringInTraversableJoin()
{
$this->expectDeprecation('Since twig/twig 3.27.0: The "Twig\Sandbox\SourcePolicyInterface" interface is deprecated with no replacement, do not pass an instance to "Twig\Extension\SandboxExtension".');
$sourcePolicy = new class implements SourcePolicyInterface {
public function enableSandbox(Source $source): bool
{
return true;
}
};
$twig = $this->getEnvironment(false, [], ['index' => '{{ iterator|join(", ") }}'], [], ['join'], [], [], [], $sourcePolicy);
try {
$twig->load('index')->render(self::$params);
$this->fail('Sandbox should block __toString on objects contained in a Traversable passed to the "join" filter (SourcePolicyInterface).');
} catch (SecurityNotAllowedMethodError $e) {
$this->assertSame('Twig\Tests\Extension\FooObject', $e->getClassName());
$this->assertSame('__tostring', $e->getMethodName());
}
}
/**
* @group legacy
*/
public function testSourcePolicySandboxBlocksToStringInTraversableReplace()
{
$this->expectDeprecation('Since twig/twig 3.27.0: The "Twig\Sandbox\SourcePolicyInterface" interface is deprecated with no replacement, do not pass an instance to "Twig\Extension\SandboxExtension".');
$sourcePolicy = new class implements SourcePolicyInterface {
public function enableSandbox(Source $source): bool
{
return true;
}
};
$twig = $this->getEnvironment(false, [], ['index' => '{{ "__toString"|replace(iterator_map) }}'], [], ['replace'], [], [], [], $sourcePolicy);
try {
$twig->load('index')->render(self::$params);
$this->fail('Sandbox should block __toString on objects contained in a Traversable passed to the "replace" filter (SourcePolicyInterface).');
} catch (SecurityNotAllowedMethodError $e) {
$this->assertSame('Twig\Tests\Extension\FooObject', $e->getClassName());
$this->assertSame('__tostring', $e->getMethodName());
}
}
public function testSandboxPreservesTraversableArgumentIdentity()
{
// Regression for https://github.com/twigphp/Twig/issues/4820:
@@ -1199,9 +1109,7 @@ EOF
}
}
/**
* @dataProvider getSafePhpTypesSkipToStringWrap
*/
#[DataProvider('getSafePhpTypesSkipToStringWrap')]
public function testSafePhpParamTypesSkipToStringWrap(string $template, callable $func, array $params): void
{
// The sandbox visitor must not wrap arguments whose target PHP
@@ -1252,9 +1160,7 @@ EOF
];
}
/**
* @dataProvider getUnsafePhpTypesStillWrap
*/
#[DataProvider('getUnsafePhpTypesStillWrap')]
public function testUnsafePhpParamTypesStillWrap(string $template, callable $func, array $params): void
{
// Conversely, an unsafe parameter type (`mixed`, untyped, `string`,
@@ -1284,9 +1190,7 @@ EOF
yield 'Stringable param' => ['{{ unsafe_fn(obj) }}', static fn (\Stringable $x) => (string) $x, $params];
}
/**
* @dataProvider getOpenPhpTypesStillWrap
*/
#[DataProvider('getOpenPhpTypesStillWrap')]
public function testOpenPhpParamTypesStillWrap(callable $func, object $obj, string $class): void
{
// Interfaces and non-final classes are "open": a Stringable subtype
+2 -3
View File
@@ -11,14 +11,13 @@
namespace Twig\Tests\Util;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Twig\Util\CallableParameters;
class CallableParametersTest extends TestCase
{
/**
* @dataProvider provideTypes
*/
#[DataProvider('provideTypes')]
public function testIsStringCoercionSafe(?\ReflectionType $type, bool $expected, ?\ReflectionClass $scope = null): void
{
$this->assertSame($expected, CallableParameters::isStringCoercionSafe($type, $scope));