diff --git a/dev/Backtrace.php b/dev/Backtrace.php index 9aa762773c6..ffb0c5f5517 100644 --- a/dev/Backtrace.php +++ b/dev/Backtrace.php @@ -5,6 +5,36 @@ */ class SS_Backtrace { + /** + * @var array Replaces all arguments with a '' string, + * mostly for security reasons. Use string values for global functions, + * and array notation for class methods. + * PHP's debug_backtrace() doesn't allow to inspect the argument names, + * so all arguments of the provided functions will be filtered out. + */ + static $ignore_function_args = array( + 'mysql_connect', + 'mssql_connect', + 'pg_connect', + array('DB', 'connect'), + array('Security', 'check_default_admin'), + array('Security', 'encrypt_password'), + array('Security', 'setDefaultAdmin'), + array('DB', 'createDatabase'), + array('Member', 'checkPassword'), + array('Member', 'changePassword'), + array('MemberPassword', 'checkPassword'), + array('PasswordValidator', 'validate'), + array('PasswordEncryptor_PHPHash', 'encrypt'), + array('PasswordEncryptor_PHPHash', 'salt'), + array('PasswordEncryptor_LegacyPHPHash', 'encrypt'), + array('PasswordEncryptor_LegacyPHPHash', 'salt'), + array('PasswordEncryptor_MySQLPassword', 'encrypt'), + array('PasswordEncryptor_MySQLPassword', 'salt'), + array('PasswordEncryptor_MySQLOldPassword', 'encrypt'), + array('PasswordEncryptor_MySQLOldPassword', 'salt'), + ); + /** * Return debug_backtrace() results with functions filtered * specific to the debugging system, and not the trace. @@ -53,6 +83,21 @@ static function filter_backtrace($bt, $ignoredFunctions = null) { array_shift($bt); } + // Filter out arguments + foreach($bt as $i => $frame) { + $match = false; + if(@$bt[$i]['class']) { + foreach(self::$ignore_function_args as $fnSpec) { + if(is_array($fnSpec) && $bt[$i]['class'] == $fnSpec[0] && $bt[$i]['function'] == $fnSpec[1]) $match = true; + } + } else { + if(in_array($bt[$i]['function'], self::$ignore_function_args)) $match = true; + } + if($match) { + foreach($bt[$i]['args'] as $j => $arg) $bt[$i]['args'][$j] = ''; + } + } + return $bt; } diff --git a/tests/dev/BacktraceTest.php b/tests/dev/BacktraceTest.php index a7a01ff4b68..2cb4289693a 100644 --- a/tests/dev/BacktraceTest.php +++ b/tests/dev/BacktraceTest.php @@ -23,4 +23,44 @@ function testFullFuncNameWithArgsAndCustomCharLimit() { ); } + function testIgnoredFunctionArgs() { + $orig = SS_Backtrace::$ignore_function_args; + + $bt = array( + array( + 'type' => '->', + 'file' => 'MyFile.php', + 'line' => 99, + 'function' => 'myIgnoredGlobalFunction', + 'args' => array('password' => 'secred',) + ), + array( + 'class' => 'MyClass', + 'type' => '->', + 'file' => 'MyFile.php', + 'line' => 99, + 'function' => 'myIgnoredClassFunction', + 'args' => array('password' => 'secred',) + ), + array( + 'class' => 'MyClass', + 'type' => '->', + 'file' => 'MyFile.php', + 'line' => 99, + 'function' => 'myFunction', + 'args' => array('myarg' => 'myval') + ) + ); + SS_Backtrace::$ignore_function_args[] = array('MyClass', 'myIgnoredClassFunction'); + SS_Backtrace::$ignore_function_args[] = 'myIgnoredGlobalFunction'; + + $filtered = SS_Backtrace::filter_backtrace($bt); + + $this->assertEquals('', $filtered[0]['args']['password'], 'Filters global functions'); + $this->assertEquals('', $filtered[1]['args']['password'], 'Filters class functions'); + $this->assertEquals('myval', $filtered[2]['args']['myarg'], 'Doesnt filter other functions'); + + SS_Backtrace::$ignore_function_args = $orig; + } + } \ No newline at end of file