From b63205a6c45966eb8dbd8e9d6036f078207660a4 Mon Sep 17 00:00:00 2001 From: Fabien Potencier Date: Tue, 25 May 2010 18:01:48 +0200 Subject: [PATCH] fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }}) --- CHANGELOG | 1 + lib/Twig/Node/SandboxPrint.php | 44 +++++++++++++++++++++++++++++ lib/Twig/NodeVisitor/Sandbox.php | 5 +++ test/Twig/Tests/Extension/SandboxTest.php | 16 ++++++++++ 4 files changed, 66 insertions(+), 0 deletions(-) create mode 100644 lib/Twig/Node/SandboxPrint.php diff --git a/CHANGELOG b/CHANGELOG index fc1b1fc..aad80f2 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ Backward incompatibilities: * The short notation of the `block` tag changed. + * fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }}) * added a 'as' string to the block tag short notation ({% block title "Title" %} must now be {% block title as "Title" %}) * added an exception when a child template has a non-empty body (as it is always ignored when rendering) diff --git a/lib/Twig/Node/SandboxPrint.php b/lib/Twig/Node/SandboxPrint.php new file mode 100644 index 0000000..a2f98be --- /dev/null +++ b/lib/Twig/Node/SandboxPrint.php @@ -0,0 +1,44 @@ + + * @version SVN: $Id$ + */ +class Twig_Node_SandboxPrint extends Twig_Node_Print +{ + public function compile($compiler) + { + $compiler + ->addDebugInfo($this) + ->write('if ($this->env->hasExtension(\'sandbox\') && is_object(') + ->subcompile($this->expr) + ->raw('))'."\n") + ->write('{'."\n") + ->indent() + ->write('$this->env->getExtension(\'sandbox\')->checkMethodAllowed(') + ->subcompile($this->expr) + ->raw(', \'__toString\');'."\n") + ->outdent() + ->write('}'."\n") + ; + + parent::compile($compiler); + } +} diff --git a/lib/Twig/NodeVisitor/Sandbox.php b/lib/Twig/NodeVisitor/Sandbox.php index 197a4e7..55f751d 100644 --- a/lib/Twig/NodeVisitor/Sandbox.php +++ b/lib/Twig/NodeVisitor/Sandbox.php @@ -34,6 +34,11 @@ class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface $this->filters[$filter[0]] = true; } } + + // look for simple print statement ({{ article }}) + if ($node instanceof Twig_Node_Print && $node->getExpression() instanceof Twig_Node_Expression_Name) { + return new Twig_Node_SandboxPrint($node->getExpression(), $node->getLine(), $node->getTag()); + } } return $node; diff --git a/test/Twig/Tests/Extension/SandboxTest.php b/test/Twig/Tests/Extension/SandboxTest.php index 28e5591..25f4d01 100644 --- a/test/Twig/Tests/Extension/SandboxTest.php +++ b/test/Twig/Tests/Extension/SandboxTest.php @@ -27,6 +27,7 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase '1_basic2' => '{{ name|upper }}', '1_basic3' => '{% if name %}foo{% endif %}', '1_basic4' => '{{ obj.bar }}', + '1_basic5' => '{{ obj }}', '1_basic' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}', ); } @@ -64,9 +65,19 @@ class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase } catch (Twig_Sandbox_SecurityError $e) { } + $twig = $this->getEnvironment(true, self::$templates); + try { + $twig->loadTemplate('1_basic5')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template'); + } catch (Twig_Sandbox_SecurityError $e) { + } + $twig = $this->getEnvironment(true, self::$templates, array(), array(), array('Object' => 'foo')); $this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods'); + $twig = $this->getEnvironment(true, self::$templates, array(), array(), array('Object' => '__toString')); + $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods'); + $twig = $this->getEnvironment(true, self::$templates, array(), array('upper')); $this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters'); @@ -115,6 +126,11 @@ class Object { public $bar = 'bar'; + public function __toString() + { + return 'foo'; + } + public function foo() { return 'foo'; -- 1.7.2.5