Fix sandbox support when using include(template_from_string())

This commit is contained in:
Fabien Potencier
2020-08-23 17:22:51 +02:00
parent 04431228af
commit c7be1bb3c4
7 changed files with 50 additions and 7 deletions
+1
View File
@@ -1,5 +1,6 @@
# 1.44.0 (2020-XX-XX) # 1.44.0 (2020-XX-XX)
* Fix sandbox support when using "include(template_from_string())"
* Make round brackets optional for one argument tests like "same as" or "divisible by" * Make round brackets optional for one argument tests like "same as" or "divisible by"
* Add support for ES2015 style object initialisation shortcut { a } is the same as { 'a': a } * Add support for ES2015 style object initialisation shortcut { a } is the same as { 'a': a }
+7
View File
@@ -313,6 +313,8 @@ use Twig\Loader\SourceContextLoaderInterface;
use Twig\Markup; use Twig\Markup;
use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Node; use Twig\Node\Node;
use Twig\Template;
use Twig\TemplateWrapper;
/** /**
* Cycles over a value. * Cycles over a value.
@@ -1562,6 +1564,11 @@ function twig_include(Environment $env, $context, $template, $variables = [], $w
} }
} }
// if a Template instance is passed, it might have been instantiated outside of a sandbox, check security
if ($template instanceof TemplateWrapper || $template instanceof Template) {
$template->unwrap()->checkSecurity();
}
$loaded = null; $loaded = null;
try { try {
$loaded = $env->resolveTemplate($template); $loaded = $env->resolveTemplate($template);
+28
View File
@@ -0,0 +1,28 @@
<?php
/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Twig\Node;
use Twig\Compiler;
/**
* @author Fabien Potencier <fabien@symfony.com>
*/
class CheckSecurityCallNode extends Node
{
public function compile(Compiler $compiler)
{
$compiler
->write("\$this->sandbox = \$this->env->getExtension('\Twig\Extension\SandboxExtension');\n")
->write("\$this->checkSecurity();\n")
;
}
}
+9 -4
View File
@@ -45,10 +45,13 @@ class CheckSecurityNode extends Node
} }
$compiler $compiler
->write("\$this->sandbox = \$this->env->getExtension('\Twig\Extension\SandboxExtension');\n") ->write("\n")
->write('$tags = ')->repr(array_filter($tags))->raw(";\n") ->write("public function checkSecurity()\n")
->write('$filters = ')->repr(array_filter($filters))->raw(";\n") ->write("{\n")
->write('$functions = ')->repr(array_filter($functions))->raw(";\n\n") ->indent()
->write('static $tags = ')->repr(array_filter($tags))->raw(";\n")
->write('static $filters = ')->repr(array_filter($filters))->raw(";\n")
->write('static $functions = ')->repr(array_filter($functions))->raw(";\n\n")
->write("try {\n") ->write("try {\n")
->indent() ->indent()
->write("\$this->sandbox->checkSecurity(\n") ->write("\$this->sandbox->checkSecurity(\n")
@@ -78,6 +81,8 @@ class CheckSecurityNode extends Node
->write("throw \$e;\n") ->write("throw \$e;\n")
->outdent() ->outdent()
->write("}\n\n") ->write("}\n\n")
->outdent()
->write("}\n")
; ;
} }
} }
+3 -1
View File
@@ -12,6 +12,7 @@
namespace Twig\NodeVisitor; namespace Twig\NodeVisitor;
use Twig\Environment; use Twig\Environment;
use Twig\Node\CheckSecurityCallNode;
use Twig\Node\CheckSecurityNode; use Twig\Node\CheckSecurityNode;
use Twig\Node\CheckToStringNode; use Twig\Node\CheckToStringNode;
use Twig\Node\Expression\Binary\ConcatBinary; use Twig\Node\Expression\Binary\ConcatBinary;
@@ -102,7 +103,8 @@ class SandboxNodeVisitor extends AbstractNodeVisitor
if ($node instanceof ModuleNode) { if ($node instanceof ModuleNode) {
$this->inAModule = false; $this->inAModule = false;
$node->getNode('constructor_end')->setNode('_security_check', new Node([new CheckSecurityNode($this->filters, $this->tags, $this->functions), $node->getNode('display_start')])); $node->setNode('constructor_end', new Node([new CheckSecurityCallNode(), $node->getNode('constructor_end')]));
$node->setNode('class_end', new Node([new CheckSecurityNode($this->filters, $this->tags, $this->functions), $node->getNode('class_end')]));
} elseif ($this->inAModule) { } elseif ($this->inAModule) {
if ($node instanceof PrintNode || $node instanceof SetNode) { if ($node instanceof PrintNode || $node instanceof SetNode) {
$this->needsToStringWrap = false; $this->needsToStringWrap = false;
+1 -1
View File
@@ -397,7 +397,7 @@ abstract class Template implements \Twig_TemplateInterface
* *
* @return Template * @return Template
*/ */
protected function unwrap() public function unwrap()
{ {
return $this; return $this;
} }
+1 -1
View File
@@ -100,7 +100,7 @@ class SandboxTest extends \PHPUnit\Framework\TestCase
$twig->load('1_basic2_include_template_from_string')->render(self::$params); $twig->load('1_basic2_include_template_from_string')->render(self::$params);
$this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called'); $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
} catch (SecurityError $e) { } catch (SecurityError $e) {
$this->assertInstanceOf(SecurityNotAllowedFilterError::class, $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFilterError'); $this->assertInstanceOf('\Twig\Sandbox\SecurityNotAllowedFilterError', $e, 'Exception should be an instance of Twig_Sandbox_SecurityNotAllowedFilterError');
$this->assertEquals('upper', $e->getFilterName(), 'Exception should be raised on the "upper" filter'); $this->assertEquals('upper', $e->getFilterName(), 'Exception should be raised on the "upper" filter');
} }
} }