added public properties support
authorfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Thu, 5 Nov 2009 17:31:02 +0000 (17:31 +0000)
committerfabien <fabien@93ef8e89-cb99-4229-a87c-7fa0fa45744b>
Thu, 5 Nov 2009 17:31:02 +0000 (17:31 +0000)
git-svn-id: http://svn.twig-project.org/trunk@113 93ef8e89-cb99-4229-a87c-7fa0fa45744b

doc/03-Twig-for-Developers.markdown
lib/Twig/Extension/Sandbox.php
lib/Twig/Resource.php
lib/Twig/Sandbox/SecurityPolicy.php
lib/Twig/Sandbox/SecurityPolicyInterface.php
test/unit/Twig/Extension/Sandbox.php

index b03c1fb..4ac9fd5 100644 (file)
@@ -285,7 +285,7 @@ The `sandbox` extension can be used to evaluate untrusted code. Access to
 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');
@@ -293,13 +293,16 @@ filters, and methods:
     $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:
 
index b900803..0baee4b 100644 (file)
@@ -76,6 +76,14 @@ class Twig_Extension_Sandbox extends Twig_Extension
     }
   }
 
+  public function checkPropertyAllowed($obj, $method)
+  {
+    if ($this->isSandboxed())
+    {
+      $this->policy->checkPropertyAllowed($obj, $method);
+    }
+  }
+
   /**
    * Returns the name of the extension.
    *
index 3173593..4fcd620 100644 (file)
@@ -41,6 +41,16 @@ abstract class Twig_Resource
       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) ||
       (
index 92b3756..fe6ac1d 100644 (file)
@@ -21,12 +21,14 @@ class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterfac
   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)
@@ -44,6 +46,11 @@ class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterfac
     $this->allowedMethods = $methods;
   }
 
+  public function setAllowedProperties(array $properties)
+  {
+    $this->allowedProperties = $properties;
+  }
+
   public function checkSecurity($tags, $filters)
   {
     foreach ($tags as $tag)
@@ -81,4 +88,23 @@ class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterfac
       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)));
+    }
+  }
 }
index afb7e61..bcb9f02 100644 (file)
@@ -21,4 +21,6 @@ interface Twig_Sandbox_SecurityPolicyInterface
   public function checkSecurity($tags, $filters);
 
   public function checkMethodAllowed($obj, $method);
+
+  public function checkPropertyAllowed($obj, $method);
 }
index 64775d0..c929edd 100644 (file)
@@ -19,6 +19,8 @@ require_once dirname(__FILE__).'/../../../lib/Twig_Loader_Var.php';
 
 class Object
 {
+  public $bar = 'bar';
+
   public function foo()
   {
     return 'foo';
@@ -33,10 +35,11 @@ $templates = array(
   '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);
@@ -75,6 +78,17 @@ catch (Twig_Sandbox_SecurityError $e)
   $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');
 
@@ -84,6 +98,9 @@ $t->is($twig->loadTemplate('1_basic2')->render($params), 'FABIEN', 'Sandbox allo
 $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(
@@ -112,7 +129,7 @@ catch (Twig_Sandbox_SecurityError $e)
 }
 
 
-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;
 
@@ -120,7 +137,7 @@ function get_environment($sandboxed, $templates, $tags = array(), $filters = arr
 
   $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;