diff --git a/src/Node/IncludeNode.php b/src/Node/IncludeNode.php index 6e17300f0..f34a5da06 100644 --- a/src/Node/IncludeNode.php +++ b/src/Node/IncludeNode.php @@ -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); diff --git a/src/TokenParser/SandboxTokenParser.php b/src/TokenParser/SandboxTokenParser.php index 536c14f30..e6f4a63f3 100644 --- a/src/TokenParser/SandboxTokenParser.php +++ b/src/TokenParser/SandboxTokenParser.php @@ -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); } } diff --git a/tests/Extension/SandboxTest.php b/tests/Extension/SandboxTest.php index c3c0aaded..71cb47fef 100644 --- a/tests/Extension/SandboxTest.php +++ b/tests/Extension/SandboxTest.php @@ -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' => <<