Version in base suite: 3.5.1-1+deb12u1 Base version: php-twig_3.5.1-1+deb12u1 Target version: php-twig_3.5.1-1+deb12u2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/p/php-twig/php-twig_3.5.1-1+deb12u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/p/php-twig/php-twig_3.5.1-1+deb12u2.dsc changelog | 18 patches/0003-Fix-sandbox-handling-for-__toString.patch | 114 +++++ patches/0004-security-cve-2026-46628-Pre-escape-HTML-input-on-the.patch | 53 ++ patches/0005-security-cve-2026-46629-Fix-unbounded-memoisation-of.patch | 98 ++++ patches/0006-security-cve-2026-46633-Fix-sandbox-bypass-PHP-code-.patch | 132 ++++++ patches/0007-security-cve-2026-46637-Fix-XSS-and-pre-escape-input.patch | 202 ++++++++++ patches/0008-security-cve-2026-47730-Profiler-Escape-template-and.patch | 79 +++ patches/0009-Update-expected-output-with-php-symfony-intl-latest-.patch | 82 ++++ patches/series | 7 9 files changed, 785 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpre03bzrk/php-twig_3.5.1-1+deb12u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpre03bzrk/php-twig_3.5.1-1+deb12u2.dsc: no acceptable signature found diff -Nru php-twig-3.5.1/debian/changelog php-twig-3.5.1/debian/changelog --- php-twig-3.5.1/debian/changelog 2024-09-14 15:27:44.000000000 +0000 +++ php-twig-3.5.1/debian/changelog 2026-06-02 03:22:19.000000000 +0000 @@ -1,3 +1,21 @@ +php-twig (3.5.1-1+deb12u2) bookworm-security; urgency=medium + + * Backport security fixes from upstream + - Fix sandbox handling for __toString() [CVE-2024-51754] + (Closes: #1086884) + - Pre-escape HTML input on the `spaceless` [CVE-2026-46628] + - Fix unbounded memoisation of `IntlDateFormatter` / `NumberFormatter` + [CVE-2026-46629] + - Fix sandbox bypass: PHP code injection via {% use %} template name + [CVE-2026-46633] + - Fix XSS and pre-escape input on HTML-emitting filters in the extras + [CVE-2026-46637] + - [Profiler] Escape template and profile names in `HtmlDumper` + [CVE-2026-47730] + * Update expected output with php-symfony-intl latest update + + -- David Prévot Tue, 02 Jun 2026 05:22:19 +0200 + php-twig (3.5.1-1+deb12u1) bookworm-security; urgency=medium * Fix a security issue when an included sandboxed template has been loaded diff -Nru php-twig-3.5.1/debian/patches/0003-Fix-sandbox-handling-for-__toString.patch php-twig-3.5.1/debian/patches/0003-Fix-sandbox-handling-for-__toString.patch --- php-twig-3.5.1/debian/patches/0003-Fix-sandbox-handling-for-__toString.patch 1970-01-01 00:00:00.000000000 +0000 +++ php-twig-3.5.1/debian/patches/0003-Fix-sandbox-handling-for-__toString.patch 2026-06-02 03:22:19.000000000 +0000 @@ -0,0 +1,114 @@ +From: Fabien Potencier +Date: Fri, 25 Oct 2024 11:04:18 +0200 +Subject: Fix sandbox handling for __toString() + +Origin: backport, https://github.com/twigphp/Twig/commit/cafc608ece310e62a35a76f17e25c04ab9ed05cc +Bug: https://github.com/advisories/GHSA-6377-hfv9-hqf6 +Bug-Debian: https://bugs.debian.org/1086884 +Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2024-51754 +--- + src/Extension/SandboxExtension.php | 8 ++++++++ + src/NodeVisitor/SandboxNodeVisitor.php | 15 ++++++++++++++- + tests/Extension/SandboxTest.php | 15 +++++++++++++-- + 3 files changed, 35 insertions(+), 3 deletions(-) + +diff --git a/src/Extension/SandboxExtension.php b/src/Extension/SandboxExtension.php +index c861159..cda4e86 100644 +--- a/src/Extension/SandboxExtension.php ++++ b/src/Extension/SandboxExtension.php +@@ -107,6 +107,14 @@ final class SandboxExtension extends AbstractExtension + + public function ensureToStringAllowed($obj, int $lineno = -1, Source $source = null) + { ++ if (\is_array($obj)) { ++ foreach ($obj as $v) { ++ $this->ensureToStringAllowed($v, $lineno, $source); ++ } ++ ++ return $obj; ++ } ++ + if ($this->isSandboxed() && \is_object($obj) && method_exists($obj, '__toString')) { + try { + $this->policy->checkMethodAllowed($obj, '__toString'); +diff --git a/src/NodeVisitor/SandboxNodeVisitor.php b/src/NodeVisitor/SandboxNodeVisitor.php +index 1446cee..efbe301 100644 +--- a/src/NodeVisitor/SandboxNodeVisitor.php ++++ b/src/NodeVisitor/SandboxNodeVisitor.php +@@ -15,12 +15,14 @@ use Twig\Environment; + use Twig\Node\CheckSecurityCallNode; + use Twig\Node\CheckSecurityNode; + use Twig\Node\CheckToStringNode; ++use Twig\Node\Expression\ArrayExpression; + use Twig\Node\Expression\Binary\ConcatBinary; + use Twig\Node\Expression\Binary\RangeBinary; + use Twig\Node\Expression\FilterExpression; + use Twig\Node\Expression\FunctionExpression; + use Twig\Node\Expression\GetAttrExpression; + use Twig\Node\Expression\NameExpression; ++use Twig\Node\Expression\Unary\SpreadUnary; + use Twig\Node\ModuleNode; + use Twig\Node\Node; + use Twig\Node\PrintNode; +@@ -117,7 +119,18 @@ final class SandboxNodeVisitor implements NodeVisitorInterface + { + $expr = $node->getNode($name); + if ($expr instanceof NameExpression || $expr instanceof GetAttrExpression) { +- $node->setNode($name, new CheckToStringNode($expr)); ++ // Simplify in 4.0 as the spread attribute has been removed there ++ $new = new CheckToStringNode($expr); ++ if ($expr->hasAttribute('spread')) { ++ $new->setAttribute('spread', $expr->getAttribute('spread')); ++ } ++ $node->setNode($name, $new); ++ } elseif ($expr instanceof SpreadUnary) { ++ $this->wrapNode($expr, 'node'); ++ } elseif ($expr instanceof ArrayExpression) { ++ foreach ($expr as $name => $_) { ++ $this->wrapNode($expr, $name); ++ } + } + } + +diff --git a/tests/Extension/SandboxTest.php b/tests/Extension/SandboxTest.php +index e365da6..86e68a5 100644 +--- a/tests/Extension/SandboxTest.php ++++ b/tests/Extension/SandboxTest.php +@@ -36,6 +36,7 @@ class SandboxTest extends TestCase + 'name' => 'Fabien', + 'obj' => new FooObject(), + 'arr' => ['obj' => new FooObject()], ++ 'some_array' => [5, 6, 7, new FooObject()], + ]; + + self::$templates = [ +@@ -187,10 +188,10 @@ class SandboxTest extends TestCase + */ + public function testSandboxUnallowedToString($template) + { +- $twig = $this->getEnvironment(true, [], ['index' => $template], [], ['upper'], ['Twig\Tests\Extension\FooObject' => 'getAnotherFooObject'], [], ['random']); ++ $twig = $this->getEnvironment(true, [], ['index' => $template], [], ['upper', 'join', 'replace'], ['Twig\Tests\Extension\FooObject' => 'getAnotherFooObject'], [], ['random']); + try { + $twig->load('index')->render(self::$params); +- $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template'); ++ $this->fail('Sandbox throws a SecurityError exception if an unallowed method "__toString()" method is called in the template'); + } catch (SecurityError $e) { + $this->assertInstanceOf(SecurityNotAllowedMethodError::class, $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedMethodError'); + $this->assertEquals('Twig\Tests\Extension\FooObject', $e->getClassName(), 'Exception should be raised on the "Twig\Tests\Extension\FooObject" class'); +@@ -214,6 +215,16 @@ class SandboxTest extends TestCase + 'object_chain_and_function' => ['{{ random(obj.anotherFooObject) }}'], + 'concat' => ['{{ obj ~ "" }}'], + 'concat_again' => ['{{ "" ~ obj }}'], ++ 'object_in_arguments' => ['{{ "__toString"|replace({"__toString": obj}) }}'], ++ 'object_in_array' => ['{{ [12, "foo", obj]|join(", ") }}'], ++ 'object_in_array_var' => ['{{ some_array|join(", ") }}'], ++ 'object_in_array_nested' => ['{{ [12, "foo", [12, "foo", obj]]|join(", ") }}'], ++ 'object_in_array_var_nested' => ['{{ [12, "foo", some_array]|join(", ") }}'], ++ 'object_in_array_dynamic_key' => ['{{ {(obj): "foo"}|join(", ") }}'], ++ 'object_in_array_dynamic_key_nested' => ['{{ {"foo": { (obj): "foo" }}|join(", ") }}'], ++ 'context' => ['{{ _context|join(", ") }}'], ++ # 'spread_array_operator' => ['{{ [1, 2, ...[5, 6, 7, obj]]|join(",") }}'], ++ # 'spread_array_operator_var' => ['{{ [1, 2, ...some_array]|join(",") }}'], + ]; + } + diff -Nru php-twig-3.5.1/debian/patches/0004-security-cve-2026-46628-Pre-escape-HTML-input-on-the.patch php-twig-3.5.1/debian/patches/0004-security-cve-2026-46628-Pre-escape-HTML-input-on-the.patch --- php-twig-3.5.1/debian/patches/0004-security-cve-2026-46628-Pre-escape-HTML-input-on-the.patch 1970-01-01 00:00:00.000000000 +0000 +++ php-twig-3.5.1/debian/patches/0004-security-cve-2026-46628-Pre-escape-HTML-input-on-the.patch 2026-06-02 03:22:19.000000000 +0000 @@ -0,0 +1,53 @@ +From: Fabien Potencier +Date: Wed, 20 May 2026 08:53:45 +0200 +Subject: security #cve-2026-46628 Pre-escape HTML input on the `spaceless` + filter (fabpot) + +This PR was merged into the twig-3.x branch. + +Origin: backport, https://github.com/twigphp/twig/commit/7923de168b908305489bc07d859f2cb93b0f0ade +Bug: https://github.com/twigphp/Twig/security/advisories/GHSA-4j38-f5cw-54h7 +Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-46628 +--- + src/Extension/CoreExtension.php | 2 +- + .../Fixtures/filters/spaceless_pre_escape.legacy.test | 18 ++++++++++++++++++ + 2 files changed, 19 insertions(+), 1 deletion(-) + create mode 100644 tests/Fixtures/filters/spaceless_pre_escape.legacy.test + +diff --git a/src/Extension/CoreExtension.php b/src/Extension/CoreExtension.php +index 50c4109..5bd952f 100644 +--- a/src/Extension/CoreExtension.php ++++ b/src/Extension/CoreExtension.php +@@ -198,7 +198,7 @@ final class CoreExtension extends AbstractExtension + new TwigFilter('striptags', 'twig_striptags'), + new TwigFilter('trim', 'twig_trim_filter'), + new TwigFilter('nl2br', 'twig_nl2br', ['pre_escape' => 'html', 'is_safe' => ['html']]), +- new TwigFilter('spaceless', 'twig_spaceless', ['is_safe' => ['html']]), ++ new TwigFilter('spaceless', 'twig_spaceless', ['pre_escape' => 'html', 'is_safe' => ['html']]), + + // array helpers + new TwigFilter('join', 'twig_join_filter'), +diff --git a/tests/Fixtures/filters/spaceless_pre_escape.legacy.test b/tests/Fixtures/filters/spaceless_pre_escape.legacy.test +new file mode 100644 +index 0000000..cb1181c +--- /dev/null ++++ b/tests/Fixtures/filters/spaceless_pre_escape.legacy.test +@@ -0,0 +1,18 @@ ++--TEST-- ++"spaceless" filter pre-escapes its input under autoescape ++--TEMPLATE-- ++{% autoescape 'html' %} ++1. {{ payload|spaceless }} ++ ++2. {{ payload|escape|spaceless }} ++ ++3. {{ payload|raw|spaceless }} ++{% endautoescape %} ++--DATA-- ++return ['payload' => ''] ++--EXPECT-- ++1. <script> alert(1) </script> ++ ++2. <script> alert(1) </script> ++ ++3. diff -Nru php-twig-3.5.1/debian/patches/0005-security-cve-2026-46629-Fix-unbounded-memoisation-of.patch php-twig-3.5.1/debian/patches/0005-security-cve-2026-46629-Fix-unbounded-memoisation-of.patch --- php-twig-3.5.1/debian/patches/0005-security-cve-2026-46629-Fix-unbounded-memoisation-of.patch 1970-01-01 00:00:00.000000000 +0000 +++ php-twig-3.5.1/debian/patches/0005-security-cve-2026-46629-Fix-unbounded-memoisation-of.patch 2026-06-02 03:22:19.000000000 +0000 @@ -0,0 +1,98 @@ +From: Fabien Potencier +Date: Tue, 19 May 2026 22:47:15 +0200 +Subject: security #cve-2026-46629 Fix unbounded memoisation of + `IntlDateFormatter` / `NumberFormatter` (alexandre-daubois) + +This PR was merged into the twig-3.x branch. + +Origin: upstream, https://github.com/twigphp/twig/commit/afe54db2e5a6586a132100b58d8fd05b5f484eed +Bug: https://github.com/twigphp/Twig/security/advisories/GHSA-35wc-cvqg-78fp +Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-46629 +--- + extra/intl-extra/IntlExtension.php | 8 +++++++ + extra/intl-extra/Tests/IntlExtensionTest.php | 33 ++++++++++++++++++++++++++++ + 2 files changed, 41 insertions(+) + +diff --git a/extra/intl-extra/IntlExtension.php b/extra/intl-extra/IntlExtension.php +index 76c2b27..f9793b0 100644 +--- a/extra/intl-extra/IntlExtension.php ++++ b/extra/intl-extra/IntlExtension.php +@@ -116,6 +116,8 @@ final class IntlExtension extends AbstractExtension + 'monetary_grouping_separator' => \NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, + ]; + ++ private const MAX_CACHED_FORMATTERS = 100; ++ + private $dateFormatters = []; + private $numberFormatters = []; + private $dateFormatterPrototype; +@@ -398,6 +400,9 @@ final class IntlExtension extends AbstractExtension + $hash = $locale.'|'.$dateFormatValue.'|'.$timeFormatValue.'|'.$timezone->getName().'|'.$calendar.'|'.$pattern; + + if (!isset($this->dateFormatters[$hash])) { ++ if (\count($this->dateFormatters) >= self::MAX_CACHED_FORMATTERS) { ++ array_shift($this->dateFormatters); ++ } + $this->dateFormatters[$hash] = new \IntlDateFormatter($locale, $dateFormatValue, $timeFormatValue, $timezone, $calendar, $pattern); + } + +@@ -444,6 +449,9 @@ final class IntlExtension extends AbstractExtension + $hash = $locale.'|'.$style.'|'.json_encode($attrs).'|'.json_encode($textAttrs).'|'.json_encode($symbols); + + if (!isset($this->numberFormatters[$hash])) { ++ if (\count($this->numberFormatters) >= self::MAX_CACHED_FORMATTERS) { ++ array_shift($this->numberFormatters); ++ } + $this->numberFormatters[$hash] = new \NumberFormatter($locale, self::NUMBER_STYLES[$style]); + } + +diff --git a/extra/intl-extra/Tests/IntlExtensionTest.php b/extra/intl-extra/Tests/IntlExtensionTest.php +index f263762..2e78429 100644 +--- a/extra/intl-extra/Tests/IntlExtensionTest.php ++++ b/extra/intl-extra/Tests/IntlExtensionTest.php +@@ -12,7 +12,9 @@ + namespace Twig\Extra\Intl\Tests; + + use PHPUnit\Framework\TestCase; ++use Twig\Environment; + use Twig\Extra\Intl\IntlExtension; ++use Twig\Loader\ArrayLoader; + + class IntlExtensionTest extends TestCase + { +@@ -25,4 +27,35 @@ class IntlExtensionTest extends TestCase + $ext = new IntlExtension($dateFormatterProto, $numberFormatterProto); + $this->assertSame('++12,3', $ext->formatNumber('12.3456')); + } ++ ++ public function testDateFormatterCacheIsBounded() ++ { ++ $ext = new IntlExtension(); ++ $env = new Environment(new ArrayLoader()); ++ $date = new \DateTime('2020-02-20T13:37:00+00:00'); ++ ++ for ($i = 0; $i < 250; ++$i) { ++ $ext->formatDateTime($env, $date, 'medium', 'medium', 'yyyy-MM-dd-'.$i, 'UTC', 'gregorian', 'en_US'); ++ } ++ ++ $cache = (new \ReflectionProperty(IntlExtension::class, 'dateFormatters'))->getValue($ext); ++ $this->assertLessThanOrEqual(100, \count($cache)); ++ $this->assertSame( ++ '2020-02-20-249', ++ $ext->formatDateTime($env, $date, 'medium', 'medium', 'yyyy-MM-dd-249', 'UTC', 'gregorian', 'en_US') ++ ); ++ } ++ ++ public function testNumberFormatterCacheIsBounded() ++ { ++ $ext = new IntlExtension(); ++ ++ for ($i = 0; $i < 250; ++$i) { ++ $ext->formatNumber(1, ['multiplier' => $i + 1], 'decimal', 'default', 'en_US'); ++ } ++ ++ $cache = (new \ReflectionProperty(IntlExtension::class, 'numberFormatters'))->getValue($ext); ++ $this->assertLessThanOrEqual(100, \count($cache)); ++ $this->assertGreaterThan(1, \count($cache)); ++ } + } diff -Nru php-twig-3.5.1/debian/patches/0006-security-cve-2026-46633-Fix-sandbox-bypass-PHP-code-.patch php-twig-3.5.1/debian/patches/0006-security-cve-2026-46633-Fix-sandbox-bypass-PHP-code-.patch --- php-twig-3.5.1/debian/patches/0006-security-cve-2026-46633-Fix-sandbox-bypass-PHP-code-.patch 1970-01-01 00:00:00.000000000 +0000 +++ php-twig-3.5.1/debian/patches/0006-security-cve-2026-46633-Fix-sandbox-bypass-PHP-code-.patch 2026-06-02 03:22:19.000000000 +0000 @@ -0,0 +1,132 @@ +From: Fabien Potencier +Date: Tue, 19 May 2026 23:00:20 +0200 +Subject: security #cve-2026-46633 Fix sandbox bypass: PHP code injection via + {% use %} template name (alexandre-daubois, fabpot) + +This PR was merged into the twig-3.x branch. + +Origin: backport, https://github.com/twigphp/twig/commit/ea7879a67f5b2d94514220cff4658969e6f69855 +Bug: https://github.com/twigphp/Twig/security/advisories/GHSA-7p85-w9px-jpjp +Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-46633 +--- + src/Compiler.php | 8 +++++++- + src/Node/ModuleNode.php | 6 +++--- + tests/CompilerTest.php | 19 +++++++++++++++++++ + tests/Node/ModuleTest.php | 25 +++++++++++++++++++++++++ + 4 files changed, 54 insertions(+), 4 deletions(-) + +diff --git a/src/Compiler.php b/src/Compiler.php +index eb652c6..72d13ac 100644 +--- a/src/Compiler.php ++++ b/src/Compiler.php +@@ -118,7 +118,13 @@ class Compiler + */ + public function string(string $value) + { +- $this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\")); ++ // Single quotes are encoded as \x27 (not \') as a defense-in-depth measure: ++ // it guarantees that the compiled output never contains a literal "'" derived ++ // from user input, which prevents breaking out of a surrounding single-quoted ++ // PHP context if a caller mistakenly concatenates the result into one. ++ // \' is not a recognized escape sequence in PHP double-quoted strings (the ++ // backslash would be kept literally), so \x27 is used instead. ++ $this->source .= sprintf('"%s"', str_replace("'", '\\x27', addcslashes($value, "\0\t\"\$\\"))); + + return $this; + } +diff --git a/src/Node/ModuleNode.php b/src/Node/ModuleNode.php +index e972b6b..a3da1a2 100644 +--- a/src/Node/ModuleNode.php ++++ b/src/Node/ModuleNode.php +@@ -213,11 +213,11 @@ final class ModuleNode extends Node + ->string($key) + ->raw("])) {\n") + ->indent() +- ->write("throw new RuntimeError('Block ") ++ ->write("throw new RuntimeError(sprintf('Block \"%s\" is not defined in trait \"%s\".', ") + ->string($key) +- ->raw(' is not defined in trait ') ++ ->raw(', ') + ->subcompile($trait->getNode('template')) +- ->raw(".', ") ++ ->raw('), ') + ->repr($node->getTemplateLine()) + ->raw(", \$this->source);\n") + ->outdent() +diff --git a/tests/CompilerTest.php b/tests/CompilerTest.php +index 35ffad9..8f9d4bd 100644 +--- a/tests/CompilerTest.php ++++ b/tests/CompilerTest.php +@@ -14,10 +14,29 @@ namespace Twig\Tests; + use PHPUnit\Framework\TestCase; + use Twig\Compiler; + use Twig\Environment; ++use Twig\Loader\ArrayLoader; + use Twig\Loader\LoaderInterface; + + class CompilerTest extends TestCase + { ++ public function testStringEncodesSingleQuotesAsHexEscape() ++ { ++ $compiler = new Compiler(new Environment(new ArrayLoader())); ++ ++ // Defense in depth: a single quote in the source value must NOT appear as a ++ // literal "'" in the compiled output, so that even if a caller mistakenly ++ // concatenates the result into a single-quoted PHP string, the value cannot ++ // break out of that context. It must still decode back to the original byte. ++ $source = $compiler->string("it's \"a\" test")->getSource(); ++ ++ $this->assertStringNotContainsString("'", $source); ++ $this->assertSame('"it\\x27s \\"a\\" test"', $source); ++ ++ $decoded = null; ++ eval('$decoded = '.$source.';'); ++ $this->assertSame("it's \"a\" test", $decoded); ++ } ++ + public function testReprNumericValueWithLocale() + { + $compiler = new Compiler(new Environment($this->createMock(LoaderInterface::class))); +diff --git a/tests/Node/ModuleTest.php b/tests/Node/ModuleTest.php +index 85adc21..c62b4f2 100644 +--- a/tests/Node/ModuleTest.php ++++ b/tests/Node/ModuleTest.php +@@ -12,6 +12,8 @@ namespace Twig\Tests\Node; + */ + + use Twig\Environment; ++use Twig\Error\RuntimeError; ++use Twig\Loader\ArrayLoader; + use Twig\Loader\LoaderInterface; + use Twig\Node\Expression\AssignNameExpression; + use Twig\Node\Expression\ConditionalExpression; +@@ -43,6 +45,29 @@ class ModuleTest extends NodeTestCase + $this->assertEquals($source->getName(), $node->getTemplateName()); + } + ++ public function testUseTagTemplateNameDoesNotInjectPhpInCompiledOutput() ++ { ++ $evilName = "evil' . print('BAD-EOL') . '.twig"; ++ $loader = new ArrayLoader([ ++ $evilName => '{% block existing %}ok{% endblock %}', ++ 'main.twig' => "{% use \"$evilName\" with absent_block as alias %}", ++ ]); ++ $twig = new Environment($loader); ++ ++ ob_start(); ++ $message = null; ++ try { ++ $twig->load('main.twig'); ++ } catch (RuntimeError $e) { ++ $message = $e->getMessage(); ++ } ++ $stdout = ob_get_clean(); ++ ++ $this->assertSame('', $stdout, 'No code from the template name must execute when the trait is loaded.'); ++ $this->assertNotNull($message, 'A RuntimeError must be raised for the missing block.'); ++ $this->assertStringContainsString($evilName, $message, 'The error message must contain the literal template name.'); ++ } ++ + public function getTests() + { + $twig = new Environment($this->createMock(LoaderInterface::class)); diff -Nru php-twig-3.5.1/debian/patches/0007-security-cve-2026-46637-Fix-XSS-and-pre-escape-input.patch php-twig-3.5.1/debian/patches/0007-security-cve-2026-46637-Fix-XSS-and-pre-escape-input.patch --- php-twig-3.5.1/debian/patches/0007-security-cve-2026-46637-Fix-XSS-and-pre-escape-input.patch 1970-01-01 00:00:00.000000000 +0000 +++ php-twig-3.5.1/debian/patches/0007-security-cve-2026-46637-Fix-XSS-and-pre-escape-input.patch 2026-06-02 03:22:19.000000000 +0000 @@ -0,0 +1,202 @@ +From: Fabien Potencier +Date: Tue, 19 May 2026 22:37:15 +0200 +Subject: security #cve-2026-46637 Fix XSS and pre-escape input on + HTML-emitting filters in the extras (nicolas-grekas) + +This PR was squashed before being merged into the twig-3.x branch. + +Origin: backport, https://github.com/twigphp/twig/commit/b675555ea2f7a0e2438d1086923d06d98930714b +Bug: https://github.com/twigphp/Twig/security/advisories/GHSA-jv8m-2544-3pg3 +Bug-Debian: https://security-tracker.debian.org/tracker/CVE-2026-46637 +--- + extra/cssinliner-extra/CssInlinerExtension.php | 2 +- + extra/cssinliner-extra/Tests/FunctionalTest.php | 47 ++++++++++++++++++++++ + extra/inky-extra/InkyExtension.php | 2 +- + extra/inky-extra/Tests/FunctionalTest.php | 33 +++++++++++++++ + extra/markdown-extra/MarkdownExtension.php | 4 +- + .../Tests/Fixtures/html_to_markdown_escaping.test | 12 ++++++ + extra/markdown-extra/Tests/FunctionalTest.php | 19 +++++++++ + 7 files changed, 115 insertions(+), 4 deletions(-) + create mode 100644 extra/cssinliner-extra/Tests/FunctionalTest.php + create mode 100644 extra/inky-extra/Tests/FunctionalTest.php + create mode 100644 extra/markdown-extra/Tests/Fixtures/html_to_markdown_escaping.test + +diff --git a/extra/cssinliner-extra/CssInlinerExtension.php b/extra/cssinliner-extra/CssInlinerExtension.php +index 4d8b75a..e11148b 100644 +--- a/extra/cssinliner-extra/CssInlinerExtension.php ++++ b/extra/cssinliner-extra/CssInlinerExtension.php +@@ -20,7 +20,7 @@ class CssInlinerExtension extends AbstractExtension + public function getFilters() + { + return [ +- new TwigFilter('inline_css', 'Twig\\Extra\\CssInliner\\twig_inline_css', ['is_safe' => ['all']]), ++ new TwigFilter('inline_css', 'Twig\\Extra\\CssInliner\\twig_inline_css', ['is_safe' => ['html'], 'pre_escape' => 'html']), + ]; + } + } +diff --git a/extra/cssinliner-extra/Tests/FunctionalTest.php b/extra/cssinliner-extra/Tests/FunctionalTest.php +new file mode 100644 +index 0000000..72c1521 +--- /dev/null ++++ b/extra/cssinliner-extra/Tests/FunctionalTest.php +@@ -0,0 +1,47 @@ ++ "{% autoescape 'js' %}{% apply inline_css %}

x

{% endapply %}{% endautoescape %}", ++ ])); ++ $twig->addExtension(new CssInlinerExtension()); ++ ++ $output = $twig->render('index'); ++ ++ $this->assertStringNotContainsString('

', $output); ++ $this->assertStringNotContainsString('

', $output); ++ $this->assertMatchesRegularExpression('{\\\\u003[Cc]p\\\\u003[Ee]x\\\\u003[Cc]\\\\/p\\\\u003[Ee]}', $output); ++ } ++ ++ public function testInlineCssPreEscapesUnsafeInput() ++ { ++ $twig = new Environment(new ArrayLoader([ ++ 'index' => '{{ payload|inline_css }}', ++ ])); ++ $twig->addExtension(new CssInlinerExtension()); ++ ++ $output = $twig->render('index', ['payload' => '']); ++ ++ $this->assertStringNotContainsString('']); ++ ++ $this->assertStringNotContainsString('