Commit 06407917 by seldaek

Added support for instance and static method calls white-listing in…

Added support for instance and static method calls white-listing in Dwoo_Security_Policy (see allowMethod()), this is hardly efficient though for instance calls since it has to do runtime checks so use it with caution git-svn-id: http://svn.dwoo.org/trunk@345 0598d79b-80c4-4d41-97ba-ac86fbbd088b
parent ad60ed1a
......@@ -10,6 +10,10 @@
+ Improved parsing of array() to support real php array syntax as well
as variables as array keys, thanks to acecream for the help
+ Improved parsing of named parameters that can now be quoted
+ Added support for instance and static method calls white-listing in
Dwoo_Security_Policy (see allowMethod()), this is hardly efficient
though for instance calls since it has to do runtime checks so use
it with caution
* Added $this->viewParam support to ZendFramework adapter through a
Dwoo_Adapters_ZendFramework_Dwoo class that extends Dwoo, you should use
this if you called setEngine() on the ZF view
......
......@@ -1706,7 +1706,11 @@ class Dwoo_Compiler implements Dwoo_ICompiler
}
if ($curBlock === 'method' || $func === 'do' || strstr($func, '::') !== false) {
$pluginType = Dwoo_Core::NATIVE_PLUGIN;
// handle static method calls with security policy
if (strstr($func, '::') !== false && $this->securityPolicy !== null && $this->securityPolicy->isMethodAllowed(explode('::', strtolower($func))) !== true) {
throw new Dwoo_Security_Exception('Call to a disallowed php function : '.$func);
}
$pluginType = Dwoo::NATIVE_PLUGIN;
} else {
$pluginType = $this->getPluginType($func);
}
......@@ -2291,15 +2295,26 @@ class Dwoo_Compiler implements Dwoo_ICompiler
} else {
// method
if (substr($methMatch[2], 0, 2) === '()') {
$parsedCall = '->'.$methMatch[1].'()';
$parsedCall = $methMatch[1].'()';
$ptr += strlen($methMatch[1]) + 2;
} else {
$parsedCall = '->'.$this->parseFunction($methodCall, $ptr, strlen($methodCall), false, 'method', $ptr);
$parsedCall = $this->parseFunction($methodCall, $ptr, strlen($methodCall), false, 'method', $ptr);
}
if ($curBlock === 'root') {
$output .= $parsedCall;
if ($this->securityPolicy !== null) {
$argPos = strpos($parsedCall, '(');
$method = strtolower(substr($parsedCall, 0, $argPos));
$args = substr($parsedCall, $argPos);
if ($curBlock === 'root') {
$output = '$this->getSecurityPolicy()->callMethod($this, '.$output.', '.var_export($method, true).', array'.$args.')';
} else {
$output = '(($tmp = '.$output.') ? $this->getSecurityPolicy()->callMethod($this, $tmp, '.var_export($method, true).', array'.$args.') : null)';
}
} else {
$output = '(($tmp = '.$output.') ? $tmp'.$parsedCall.' : null)';
if ($curBlock === 'root') {
$output .= '->'.$parsedCall;
} else {
$output = '(($tmp = '.$output.') ? $tmp->'.$parsedCall.' : null)';
}
}
}
}
......@@ -2921,8 +2936,10 @@ class Dwoo_Compiler implements Dwoo_ICompiler
$pluginType = -1;
if (($this->securityPolicy === null && (function_exists($name) || strtolower($name) === 'isset' || strtolower($name) === 'empty')) ||
($this->securityPolicy !== null && in_array(strtolower($name), $this->securityPolicy->getAllowedPhpFunctions()) !== false)) {
($this->securityPolicy !== null && array_key_exists(strtolower($name), $this->securityPolicy->getAllowedPhpFunctions()) !== false)) {
$phpFunc = true;
} elseif ($this->securityPolicy !== null && function_exists($name) && array_key_exists(strtolower($name), $this->securityPolicy->getAllowedPhpFunctions()) === false) {
throw new Dwoo_Security_Exception('Call to a disallowed php function : '.$name);
}
while ($pluginType <= 0) {
......
......@@ -47,11 +47,28 @@ class Dwoo_Security_Policy
*/
protected $allowedPhpFunctions = array
(
'str_repeat', 'number_format', 'htmlentities', 'htmlspecialchars',
'long2ip', 'strlen', 'list', 'empty', 'count', 'sizeof', 'in_array', 'is_array',
'str_repeat' => true,
'number_format' => true,
'htmlentities' => true,
'htmlspecialchars' => true,
'long2ip' => true,
'strlen' => true,
'list' => true,
'empty' => true,
'count' => true,
'sizeof' => true,
'in_array' => true,
'is_array' => true,
);
/**
* methods that are allowed to be used within the template
*
* @var array
*/
protected $allowedMethods = array();
/**
* paths that are safe to use with include or other file-access plugins
*
* @var array
......@@ -116,6 +133,49 @@ class Dwoo_Security_Policy
}
/**
* adds a class method to the allowed list, this must be used for
* both static and non static method by providing the class name
* and method name to use
*
* @param mixed $class class name or array of array('class', 'method') couples
* @param string $method method name
*/
public function allowMethod($class, $method = null)
{
if (is_array($class))
foreach ($class as $elem)
$this->allowedMethods[strtolower($elem[0])][strtolower($elem[1])] = true;
else
$this->allowedMethods[strtolower($class)][strtolower($method)] = true;
}
/**
* removes a class method from the allowed list
*
* @param mixed $class class name or array of array('class', 'method') couples
* @param string $method method name
*/
public function disallowMethod($class, $method = null)
{
if (is_array($class))
foreach ($class as $elem)
unset($this->allowedMethods[strtolower($elem[0])][strtolower($elem[1])]);
else
unset($this->allowedMethods[strtolower($class)][strtolower($method)]);
}
/**
* returns the list of class methods allowed to run, note that the class names
* and method names are stored in the array keys and not values
*
* @return array
*/
public function getAllowedMethods()
{
return $this->allowedMethods;
}
/**
* adds a directory to the safelist for includes and other file-access plugins
*
* note that all the includePath directories you provide to the Dwoo_Template_File class
......@@ -196,4 +256,49 @@ class Dwoo_Security_Policy
{
return $this->constHandling;
}
/**
* this is used at run time to check whether method calls are allowed or not
*
* @param Dwoo_Core $dwoo dwoo instance that calls this
* @param object $obj any object on which the method must be called
* @param string $method lowercased method name
* @param array $args arguments array
* @return mixed result of method call or unll + E_USER_NOTICE if not allowed
*/
public function callMethod(Dwoo_Core $dwoo, $obj, $method, $args)
{
foreach ($this->allowedMethods as $class => $methods) {
if (!isset($methods[$method])) {
continue;
}
if ($obj instanceof $class) {
return call_user_func_array(array($obj, $method), $args);
}
}
$dwoo->triggerError('The current security policy prevents you from calling '.get_class($obj).'::'.$method.'()');
return null;
}
/**
* this is used at compile time to check whether static method calls are allowed or not
*
* @param mixed $class lowercased class name or array('class', 'method') couple
* @param string $method lowercased method name
* @return bool
*/
public function isMethodAllowed($class, $method = null) {
if (is_array($class)) {
list($class, $method) = $class;
}
foreach ($this->allowedMethods as $allowedClass => $methods) {
if (!isset($methods[$method])) {
continue;
}
if ($class === $allowedClass || is_subclass_of($class, $allowedClass)) {
return true;
}
}
return false;
}
}
......@@ -2,8 +2,6 @@
require_once DWOO_DIRECTORY . 'Dwoo/Compiler.php';
function testphpfunc($input) { return $input.'OK'; }
class SecurityTests extends PHPUnit_Framework_TestCase
{
protected $compiler;
......@@ -67,19 +65,78 @@ class SecurityTests extends PHPUnit_Framework_TestCase
$tpl->forceCompilation();
$this->assertEquals("fooOK", $this->dwoo->get($tpl, array(), $this->compiler));
$this->policy->disallowPhpFunction('testphpfunc');
}
/**
* @expectedException Dwoo_Exception
* @expectedException Dwoo_Security_Exception
*/
public function testNotAllowedPhpFunction()
{
$tpl = new Dwoo_Template_String('{strtotime("2000-01-01")}');
$tpl = new Dwoo_Template_String('{testphpfunc("foo")}');
$tpl->forceCompilation();
$this->dwoo->get($tpl, array(), $this->compiler);
}
public function testAllowMethod()
{
$this->policy->allowMethod('testSecurityClass','testOK');
$tpl = new Dwoo_Template_String('{$obj->testOK("foo")}');
$tpl->forceCompilation();
$this->assertEquals("fooOK", $this->dwoo->get($tpl, array('obj' => new testSecurityClass), $this->compiler));
$this->policy->disallowMethod('testSecurityClass','test');
}
/**
* @expectedException PHPUnit_Framework_Error
*/
public function testNotAllowedMethod()
{
$tpl = new Dwoo_Template_String('{$obj->testOK("foo")}');
$tpl->forceCompilation();
$this->dwoo->get($tpl, array('obj' => new testSecurityClass), $this->compiler);
}
public function testAllowStaticMethod()
{
$this->policy->allowMethod('testSecurityClass','testStatic');
$tpl = new Dwoo_Template_String('{testSecurityClass::testStatic("foo")}');
$tpl->forceCompilation();
$this->assertEquals("fooOK", $this->dwoo->get($tpl, array(), $this->compiler));
$this->policy->disallowMethod('testSecurityClass','testStatic');
}
/**
* @expectedException Dwoo_Security_Exception
*/
public function testNotAllowedStaticMethod()
{
$tpl = new Dwoo_Template_String('{testSecurityClass::testStatic("foo")}');
$tpl->forceCompilation();
$this->dwoo->get($tpl, array(), $this->compiler);
}
/**
* @expectedException Dwoo_Security_Exception
*/
public function testNotAllowedSubExecution()
{
$tpl = new Dwoo_Template_String('{$obj->test(preg_replace_callback("{.}", "mail", "f"))}');
$tpl->forceCompilation();
$this->dwoo->get($tpl, array('obj' => new testSecurityClass), $this->compiler);
}
public function testAllowDirectoryGetSet()
{
$old = $this->policy->getAllowedDirectories();
......@@ -104,3 +161,19 @@ class SecurityTests extends PHPUnit_Framework_TestCase
$this->assertEquals($old, $this->policy->getAllowedPhpFunctions());
}
}
function testphpfunc($input) { return $input.'OK'; }
class testSecurityClass {
public static function testStatic($input) {
return $input.'OK';
}
public function testOK($input) {
return $input.'OK';
}
public function test($input) {
throw new Exception('can not call');
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment