mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-16 12:26:30 +00:00
security #cve-2026-46638 Fix sandbox bypass in the {% sandbox %} tag when including a preloaded template (alexandre-daubois)
This PR was merged into the twig-3.x branch.
This commit is contained in:
@@ -38,6 +38,8 @@ class IncludeNode extends Node implements NodeOutputInterface
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
$sandboxed = $this->hasAttribute('sandboxed') && $this->getAttribute('sandboxed');
|
||||
|
||||
if ($this->getAttribute('ignore_missing')) {
|
||||
$template = $compiler->getVarName();
|
||||
|
||||
@@ -60,15 +62,32 @@ class IncludeNode extends Node implements NodeOutputInterface
|
||||
->write("}\n")
|
||||
->write(\sprintf("if ($%s) {\n", $template))
|
||||
->indent()
|
||||
->write(\sprintf('yield from $%s->unwrap()->yield(', $template))
|
||||
;
|
||||
|
||||
if ($sandboxed) {
|
||||
$compiler->write(\sprintf("\$%s->unwrap()->checkSecurity();\n", $template));
|
||||
}
|
||||
|
||||
$compiler->write(\sprintf('yield from $%s->unwrap()->yield(', $template));
|
||||
|
||||
$this->addTemplateArguments($compiler);
|
||||
$compiler
|
||||
->raw(");\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
} elseif ($sandboxed) {
|
||||
$template = $compiler->getVarName();
|
||||
|
||||
$compiler->write(\sprintf('$%s = ', $template));
|
||||
$this->addGetTemplate($compiler);
|
||||
$compiler
|
||||
->raw(";\n")
|
||||
->write(\sprintf("\$%s->unwrap()->checkSecurity();\n", $template))
|
||||
->write(\sprintf('yield from $%s->unwrap()->yield(', $template))
|
||||
;
|
||||
$this->addTemplateArguments($compiler);
|
||||
$compiler->raw(");\n");
|
||||
} else {
|
||||
$compiler->write('yield from ');
|
||||
$this->addGetTemplate($compiler);
|
||||
|
||||
@@ -41,7 +41,9 @@ final class SandboxTokenParser extends AbstractTokenParser
|
||||
$stream->expect(Token::BLOCK_END_TYPE);
|
||||
|
||||
// in a sandbox tag, only include tags are allowed
|
||||
if (!$body instanceof IncludeNode) {
|
||||
if ($body instanceof IncludeNode) {
|
||||
$body->setAttribute('sandboxed', true);
|
||||
} else {
|
||||
foreach ($body as $node) {
|
||||
if ($node instanceof TextNode && ctype_space($node->getAttribute('data'))) {
|
||||
continue;
|
||||
@@ -50,6 +52,8 @@ final class SandboxTokenParser extends AbstractTokenParser
|
||||
if (!$node instanceof IncludeNode) {
|
||||
throw new SyntaxError('Only "include" tags are allowed within a "sandbox" section.', $node->getTemplateLine(), $stream->getSourceContext());
|
||||
}
|
||||
|
||||
$node->setAttribute('sandboxed', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -535,6 +535,32 @@ EOF
|
||||
$this->assertFalse($twig->getExtension(SandboxExtension::class)->isSandboxed(), 'Sandboxed include() function call should not leave Sandbox enabled when an error occurs.');
|
||||
}
|
||||
|
||||
public function testSandboxTagIncludeWithPreloadedTemplate()
|
||||
{
|
||||
$twig = $this->getEnvironment(false, [], [
|
||||
'index' => '{% sandbox %}{% include "included" %}{% endsandbox %}',
|
||||
'included' => '{{ "hello"|upper }}',
|
||||
]);
|
||||
|
||||
$twig->load('included');
|
||||
|
||||
$this->expectException(SecurityNotAllowedFilterError::class);
|
||||
$twig->load('index')->render([]);
|
||||
}
|
||||
|
||||
public function testSandboxTagIncludeIgnoreMissingWithPreloadedTemplate()
|
||||
{
|
||||
$twig = $this->getEnvironment(false, [], [
|
||||
'index' => '{% sandbox %}{% include "included" ignore missing %}{% endsandbox %}',
|
||||
'included' => '{{ "hello"|upper }}',
|
||||
]);
|
||||
|
||||
$twig->load('included');
|
||||
|
||||
$this->expectException(SecurityNotAllowedFilterError::class);
|
||||
$twig->load('index')->render([]);
|
||||
}
|
||||
|
||||
public function testSandboxWithNoClosureFilter()
|
||||
{
|
||||
$twig = $this->getEnvironment(true, ['autoescape' => 'html'], ['index' => <<<EOF
|
||||
|
||||
Reference in New Issue
Block a user