You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
8.8 KiB
209 lines
8.8 KiB
<?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.
|
|
*/
|
|
|
|
class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase
|
|
{
|
|
static protected $params, $templates;
|
|
|
|
public function setUp()
|
|
{
|
|
self::$params = array(
|
|
'name' => 'Fabien',
|
|
'obj' => new FooObject(),
|
|
'arr' => array('obj' => new FooObject()),
|
|
);
|
|
|
|
self::$templates = array(
|
|
'1_basic1' => '{{ obj.foo }}',
|
|
'1_basic2' => '{{ name|upper }}',
|
|
'1_basic3' => '{% if name %}foo{% endif %}',
|
|
'1_basic4' => '{{ obj.bar }}',
|
|
'1_basic5' => '{{ obj }}',
|
|
'1_basic6' => '{{ arr.obj }}',
|
|
'1_basic7' => '{{ cycle(["foo","bar"], 1) }}',
|
|
'1_basic8' => '{{ obj.getfoobar }}{{ obj.getFooBar }}',
|
|
'1_basic' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
|
|
'1_layout' => '{% block content %}{% endblock %}',
|
|
'1_child' => '{% extends "1_layout" %}{% block content %}{{ "a"|json_encode }}{% endblock %}',
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @expectedException Twig_Sandbox_SecurityError
|
|
* @expectedExceptionMessage Filter "json_encode" is not allowed in "1_child".
|
|
*/
|
|
public function testSandboxWithInheritance()
|
|
{
|
|
$twig = $this->getEnvironment(true, array(), self::$templates, array('block'));
|
|
$twig->loadTemplate('1_child')->render(array());
|
|
}
|
|
|
|
public function testSandboxGloballySet()
|
|
{
|
|
$twig = $this->getEnvironment(false, array(), self::$templates);
|
|
$this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally');
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates);
|
|
try {
|
|
$twig->loadTemplate('1_basic1')->render(self::$params);
|
|
$this->fail('Sandbox throws a SecurityError exception if an unallowed method is called');
|
|
} catch (Twig_Sandbox_SecurityError $e) {
|
|
}
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates);
|
|
try {
|
|
$twig->loadTemplate('1_basic2')->render(self::$params);
|
|
$this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
|
|
} catch (Twig_Sandbox_SecurityError $e) {
|
|
}
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates);
|
|
try {
|
|
$twig->loadTemplate('1_basic3')->render(self::$params);
|
|
$this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
|
|
} catch (Twig_Sandbox_SecurityError $e) {
|
|
}
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates);
|
|
try {
|
|
$twig->loadTemplate('1_basic4')->render(self::$params);
|
|
$this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
|
|
} catch (Twig_Sandbox_SecurityError $e) {
|
|
}
|
|
|
|
$twig = $this->getEnvironment(true, array(), 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, array(), self::$templates);
|
|
try {
|
|
$twig->loadTemplate('1_basic6')->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, array(), self::$templates);
|
|
try {
|
|
$twig->loadTemplate('1_basic7')->render(self::$params);
|
|
$this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template');
|
|
} catch (Twig_Sandbox_SecurityError $e) {
|
|
}
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
|
|
FooObject::reset();
|
|
$this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
|
|
$this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once');
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString'));
|
|
FooObject::reset();
|
|
$this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods');
|
|
$this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper'));
|
|
$this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters');
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates, array('if'));
|
|
$this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags');
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('FooObject' => 'bar'));
|
|
$this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties');
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('cycle'));
|
|
$this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions');
|
|
|
|
foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
|
|
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name));
|
|
FooObject::reset();
|
|
$this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic8')->render(self::$params), 'Sandbox allow methods in a case-insensitive way');
|
|
$this->assertEquals(2, FooObject::$called['getFooBar'], 'Sandbox only calls method once');
|
|
}
|
|
}
|
|
|
|
public function testSandboxLocallySetForAnInclude()
|
|
{
|
|
self::$templates = array(
|
|
'2_basic' => '{{ obj.foo }}{% include "2_included" %}{{ obj.foo }}',
|
|
'2_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
|
|
);
|
|
|
|
$twig = $this->getEnvironment(false, array(), self::$templates);
|
|
$this->assertEquals('fooFOOfoo', $twig->loadTemplate('2_basic')->render(self::$params), 'Sandbox does nothing if disabled globally and sandboxed not used for the include');
|
|
|
|
self::$templates = array(
|
|
'3_basic' => '{{ obj.foo }}{% sandbox %}{% include "3_included" %}{% endsandbox %}{{ obj.foo }}',
|
|
'3_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
|
|
);
|
|
|
|
$twig = $this->getEnvironment(true, array(), self::$templates);
|
|
try {
|
|
$twig->loadTemplate('3_basic')->render(self::$params);
|
|
$this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed');
|
|
} catch (Twig_Sandbox_SecurityError $e) {
|
|
}
|
|
}
|
|
|
|
public function testMacrosInASandbox()
|
|
{
|
|
$twig = $this->getEnvironment(true, array('autoescape' => true), array('index' => <<<EOF
|
|
{% macro test(text) %}<p>{{ text }}</p>{% endmacro %}
|
|
{{ _self.test('username') }}
|
|
EOF
|
|
), array('macro'), array('escape'));
|
|
|
|
$this->assertEquals('<p>username</p>', $twig->loadTemplate('index')->render(array()));
|
|
}
|
|
|
|
protected function getEnvironment($sandboxed, $options, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array(), $functions = array())
|
|
{
|
|
$loader = new Twig_Loader_Array($templates);
|
|
$twig = new Twig_Environment($loader, array_merge(array('debug' => true, 'cache' => false, 'autoescape' => false), $options));
|
|
$policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
|
|
$twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
|
|
|
|
return $twig;
|
|
}
|
|
}
|
|
|
|
class FooObject
|
|
{
|
|
static public $called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
|
|
|
|
public $bar = 'bar';
|
|
|
|
static public function reset()
|
|
{
|
|
self::$called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
|
|
}
|
|
|
|
public function __toString()
|
|
{
|
|
++self::$called['__toString'];
|
|
|
|
return 'foo';
|
|
}
|
|
|
|
public function foo()
|
|
{
|
|
++self::$called['foo'];
|
|
|
|
return 'foo';
|
|
}
|
|
|
|
public function getFooBar()
|
|
{
|
|
++self::$called['getFooBar'];
|
|
|
|
return 'foobar';
|
|
}
|
|
}
|
|
|