unsafe attributes and methods is prohibited. The sandbox security is managed
by a policy instance. By default, Twig comes with one policy class:
`Twig_Sandbox_SecurityPolicy`. This class allows you to white-list some tags,
-filters, and methods:
+filters, properties, and methods:
[php]
$tags = array('if');
$methods = array(
'Article' => array('getTitle', 'getBody'),
);
- $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods);
+ $properties = array(
+ 'Article' => array('title', 'body),
+ );
+ $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties);
With the previous configuration, the security policy will only allow usage of
the `if` tag, and the `upper` filter. Moreover, the templates will only be
-able to call the `getTitle()` and `getBody()` methods on `Article` objects.
-Everything else won't be allowed and will generate a
-`Twig_Sandbox_SecurityError` exception.
+able to call the `getTitle()` and `getBody()` methods on `Article` objects,
+and the `title` and `body` public properties. Everything else won't be allowed
+and will generate a `Twig_Sandbox_SecurityError` exception.
The policy object is the first argument of the sandbox constructor:
}
}
+ public function checkPropertyAllowed($obj, $method)
+ {
+ if ($this->isSandboxed())
+ {
+ $this->policy->checkPropertyAllowed($obj, $method);
+ }
+ }
+
/**
* Returns the name of the extension.
*
return null;
}
+ if (is_object($object) && isset($object->$item))
+ {
+ if ($this->env->hasExtension('sandbox'))
+ {
+ $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
+ }
+
+ return $object->$item;
+ }
+
if (
!is_object($object) ||
(
protected $allowedTags;
protected $allowedFilters;
protected $allowedMethods;
+ protected $allowedProperties;
- public function __construct(array $allowedTags = array(), array $allowedFilters = array(), array $allowedMethods = array())
+ public function __construct(array $allowedTags = array(), array $allowedFilters = array(), array $allowedMethods = array(), array $allowedProperties = array())
{
$this->allowedTags = $allowedTags;
$this->allowedFilters = $allowedFilters;
$this->allowedMethods = $allowedMethods;
+ $this->allowedProperties = $allowedProperties;
}
public function setAllowedTags(array $tags)
$this->allowedMethods = $methods;
}
+ public function setAllowedProperties(array $properties)
+ {
+ $this->allowedProperties = $properties;
+ }
+
public function checkSecurity($tags, $filters)
{
foreach ($tags as $tag)
throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, get_class($obj)));
}
}
+
+ public function checkPropertyAllowed($obj, $property)
+ {
+ $allowed = false;
+ foreach ($this->allowedProperties as $class => $properties)
+ {
+ if ($obj instanceof $class)
+ {
+ $allowed = in_array($property, is_array($properties) ? $properties : array($properties));
+
+ break;
+ }
+ }
+
+ if (!$allowed)
+ {
+ throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, get_class($obj)));
+ }
+ }
}
public function checkSecurity($tags, $filters);
public function checkMethodAllowed($obj, $method);
+
+ public function checkPropertyAllowed($obj, $method);
}
class Object
{
+ public $bar = 'bar';
+
public function foo()
{
return 'foo';
'1_basic1' => '{{ obj.foo }}',
'1_basic2' => '{{ name|upper }}',
'1_basic3' => '{% if name %}foo{% endif %}',
+ '1_basic4' => '{{ obj.bar }}',
'1_basic' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
);
-$t = new LimeTest(9);
+$t = new LimeTest(11);
$t->diag('Sandbox globally set');
$twig = get_environment(false, $templates);
$t->pass('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
}
+$twig = get_environment(true, $templates);
+try
+{
+ $twig->loadTemplate('1_basic4')->render($params);
+ $t->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
+}
+catch (Twig_Sandbox_SecurityError $e)
+{
+ $t->pass('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
+}
+
$twig = get_environment(true, $templates, array(), array(), array('Object' => 'foo'));
$t->is($twig->loadTemplate('1_basic1')->render($params), 'foo', 'Sandbox allow some methods');
$twig = get_environment(true, $templates, array('if'));
$t->is($twig->loadTemplate('1_basic3')->render($params), 'foo', 'Sandbox allow some tags');
+$twig = get_environment(true, $templates, array(), array(), array(), array('Object' => 'bar'));
+$t->is($twig->loadTemplate('1_basic4')->render($params), 'bar', 'Sandbox allow some properties');
+
$t->diag('Sandbox locally set for an include');
$templates = array(
}
-function get_environment($sandboxed, $templates, $tags = array(), $filters = array(), $methods = array())
+function get_environment($sandboxed, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array())
{
static $prefix = 0;
$loader = new Twig_Loader_Var($templates, $prefix);
$twig = new Twig_Environment($loader, array('trim_blocks' => true, 'debug' => true));
- $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods);
+ $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties);
$twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
return $twig;