return $this->policy;
}
- public function checkSecurity($tags, $filters)
+ public function checkSecurity($tags, $filters, $functions)
{
if ($this->isSandboxed()) {
- $this->policy->checkSecurity($tags, $filters);
+ $this->policy->checkSecurity($tags, $filters, $functions);
}
}
{
protected $usedFilters;
protected $usedTags;
+ protected $usedFunctions;
- public function __construct(Twig_Node_Module $node, array $usedFilters, array $usedTags)
+ public function __construct(Twig_Node_Module $node, array $usedFilters, array $usedTags, array $usedFunctions)
{
parent::__construct($node->getNode('body'), $node->getNode('parent'), $node->getNode('blocks'), $node->getNode('macros'), $node->getAttribute('filename'), $node->getLine(), $node->getNodeTag());
$this->usedFilters = $usedFilters;
$this->usedTags = $usedTags;
+ $this->usedFunctions = $usedFunctions;
}
protected function compileDisplayBody($compiler)
->write("\$this->env->getExtension('sandbox')->checkSecurity(\n")
->indent()
->write(!$this->usedTags ? "array(),\n" : "array('".implode('\', \'', $this->usedTags)."'),\n")
- ->write(!$this->usedFilters ? "array()\n" : "array('".implode('\', \'', $this->usedFilters)."')\n")
+ ->write(!$this->usedFilters ? "array(),\n" : "array('".implode('\', \'', $this->usedFilters)."'),\n")
+ ->write(!$this->usedFunctions ? "array()\n" : "array('".implode('\', \'', $this->usedFunctions)."')\n")
->outdent()
->write(");\n")
;
protected $inAModule = false;
protected $tags;
protected $filters;
+ protected $functions;
/**
* Called before child nodes are visited.
$this->inAModule = true;
$this->tags = array();
$this->filters = array();
+ $this->functions = array();
return $node;
} elseif ($this->inAModule) {
$this->filters[] = $node->getNode('filter')->getAttribute('value');
}
+ // look for functions
+ if ($node instanceof Twig_Node_Expression_Function) {
+ $this->functions[] = $node->getNode('name')->getAttribute('name');
+ }
+
// wrap print to check __toString() calls
if ($node instanceof Twig_Node_Print) {
return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getLine(), $node->getNodeTag());
if ($node instanceof Twig_Node_Module) {
$this->inAModule = false;
- return new Twig_Node_SandboxedModule($node, array_unique($this->filters), array_unique($this->tags));
+ return new Twig_Node_SandboxedModule($node, array_unique($this->filters), array_unique($this->tags), array_unique($this->functions));
}
return $node;
protected $allowedFilters;
protected $allowedMethods;
protected $allowedProperties;
+ protected $allowedFunctions;
- public function __construct(array $allowedTags = array(), array $allowedFilters = array(), array $allowedMethods = array(), array $allowedProperties = array())
+ public function __construct(array $allowedTags = array(), array $allowedFilters = array(), array $allowedMethods = array(), array $allowedProperties = array(), array $allowedFunctions = array())
{
$this->allowedTags = $allowedTags;
$this->allowedFilters = $allowedFilters;
$this->allowedMethods = $allowedMethods;
$this->allowedProperties = $allowedProperties;
+ $this->allowedFunctions = $allowedFunctions;
}
public function setAllowedTags(array $tags)
$this->allowedProperties = $properties;
}
- public function checkSecurity($tags, $filters)
+ public function setAllowedFunctions(array $functions)
+ {
+ $this->allowedFunctions = $functions;
+ }
+
+ public function checkSecurity($tags, $filters, $functions)
{
foreach ($tags as $tag) {
if (!in_array($tag, $this->allowedTags)) {
throw new Twig_Sandbox_SecurityError(sprintf('Filter "%s" is not allowed.', $filter));
}
}
+
+ foreach ($functions as $function) {
+ if (!in_array($function, $this->allowedFunctions)) {
+ throw new Twig_Sandbox_SecurityError(sprintf('Function "%s" is not allowed.', $function));
+ }
+ }
}
public function checkMethodAllowed($obj, $method)
*/
interface Twig_Sandbox_SecurityPolicyInterface
{
- public function checkSecurity($tags, $filters);
+ public function checkSecurity($tags, $filters, $functions);
public function checkMethodAllowed($obj, $method);
'1_basic4' => '{{ obj.bar }}',
'1_basic5' => '{{ obj }}',
'1_basic6' => '{{ arr.obj }}',
+ '1_basic7' => '{{ cycle(["foo","bar"], 1) }}',
'1_basic' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
);
}
} 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('Object' => 'foo'));
$this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('Object' => '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');
+
}
public function testSandboxLocallySetForAnInclude()
$this->assertEquals('<p>username</p>', $twig->loadTemplate('index')->render(array()));
}
- protected function getEnvironment($sandboxed, $options, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = 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);
+ $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
$twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
return $twig;
$macros = new Twig_Node();
$filename = 'foo.twig';
$node = new Twig_Node_Module($body, $parent, $blocks, $macros, $filename);
- $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'));
+ $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
$this->assertEquals($body, $node->getNode('body'));
$this->assertEquals($blocks, $node->getNode('blocks'));
$filename = 'foo.twig';
$node = new Twig_Node_Module($body, $extends, $blocks, $macros, $filename);
- $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'));
+ $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
$tests[] = array($node, <<<EOF
<?php
protected function checkSecurity() {
\$this->env->getExtension('sandbox')->checkSecurity(
array('upper'),
- array('for')
+ array('for'),
+ array('cycle')
);
}
$filename = 'foo.twig';
$node = new Twig_Node_Module($body, $extends, $blocks, $macros, $filename);
- $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'));
+ $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
$tests[] = array($node, <<<EOF
<?php
protected function checkSecurity() {
\$this->env->getExtension('sandbox')->checkSecurity(
array('upper'),
- array('for')
+ array('for'),
+ array('cycle')
);
\$this->parent->checkSecurity();