Merge pull request #10364 from creative-commoners/pulls/4/update-backtrace-filter-methods

ENH Update list of filtered backtrace methods
This commit is contained in:
Sabina Talipova 2022-06-27 14:18:23 +12:00 committed by GitHub
commit f63655bd40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 119 additions and 37 deletions

43
_config/backtrace.yml Normal file
View File

@ -0,0 +1,43 @@
---
Name: framework-backtrace
---
SilverStripe\Dev\Backtrace:
ignore_function_args:
- 'mssql_connect'
- 'mysql_connect'
- 'pg_connect'
- ['mysqli', 'mysqli']
- ['mysqli', 'real_connect']
- ['mysqli', 'select_db']
- ['PDO', '__construct']
- ['SilverStripe\Control\Middleware\ConfirmationMiddleware\GetParameter', buildConfirmationItem]
- ['SilverStripe\Control\Middleware\ConfirmationMiddleware\Url', buildConfirmationItem]
- ['SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith', buildConfirmationItem]
- ['SilverStripe\Core\Startup\AbstractConfirmationToken', 'checkToken']
- ['SilverStripe\Core\Startup\AbstractConfirmationToken', 'pathForToken']
- ['SilverStripe\Core\Startup\AbstractConfirmationToken', 'prepare_tokens']
- ['SilverStripe\ORM\DB', 'connect']
- ['SilverStripe\ORM\DB', 'createDatabase']
- ['SilverStripe\Security\Confirmation\Item', '__construct']
- ['SilverStripe\Security\DefaultAdminService', 'isDefaultAdminCredentials']
- ['SilverStripe\Security\DefaultAdminService', 'setDefaultAdmin']
- ['SilverStripe\Security\Member', 'changePassword']
- ['SilverStripe\Security\MemberAuthenticator\ChangePasswordHandler', 'setSessionToken']
- ['SilverStripe\Security\MemberAuthenticator\CookieAuthenticationHandler', 'setTokenCookieName']
- ['SilverStripe\Security\MemberAuthenticator\CookieAuthenticationHandler', 'setTokenCookieSecure']
- ['SilverStripe\Security\MemberAuthenticator\LostPasswordHandler', 'sendEmail']
- ['SilverStripe\Security\PasswordEncryptor', 'check']
- ['SilverStripe\Security\PasswordEncryptor', 'encrypt']
- ['SilverStripe\Security\PasswordEncryptor', 'salt']
- ['SilverStripe\Security\PasswordEncryptor_Blowfish', 'encryptA']
- ['SilverStripe\Security\PasswordEncryptor_Blowfish', 'encryptX']
- ['SilverStripe\Security\PasswordEncryptor_Blowfish', 'encryptY']
- ['SilverStripe\Security\PasswordValidator', 'validate']
- ['SilverStripe\Security\RememberLoginHash', 'setToken']
- ['SilverStripe\Security\Security', 'check_default_admin']
- ['SilverStripe\Security\Security', 'encrypt_password']
- ['SilverStripe\Security\Security', 'setDefaultAdmin']
- ['*', 'checkPassword']
- ['*', 'onAfterChangePassword']
- ['*', 'onBeforeChangePassword']
- ['*', 'updateValidatePassword']

View File

@ -13,42 +13,14 @@ class Backtrace
use Configurable;
/**
* @var array Replaces all arguments with a '<filtered>' string,
* Replaces all arguments with a '<filtered>' 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.
* @var array
*/
private static $ignore_function_args = [
'mysql_connect',
'mssql_connect',
'pg_connect',
['PDO', '__construct'],
['mysqli', 'mysqli'],
['mysqli', 'select_db'],
['mysqli', 'real_connect'],
['SilverStripe\\ORM\\DB', 'connect'],
['SilverStripe\\Security\\Security', 'check_default_admin'],
['SilverStripe\\Security\\Security', 'encrypt_password'],
['SilverStripe\\Security\\Security', 'setDefaultAdmin'],
['SilverStripe\\ORM\\DB', 'createDatabase'],
['SilverStripe\\Security\\Member', 'checkPassword'],
['SilverStripe\\Security\\Member', 'changePassword'],
['SilverStripe\\Security\\MemberAuthenticator\\MemberAuthenticator', 'checkPassword'],
['SilverStripe\\Security\\MemberPassword', 'checkPassword'],
['SilverStripe\\Security\\PasswordValidator', 'validate'],
['SilverStripe\\Security\\PasswordEncryptor_PHPHash', 'encrypt'],
['SilverStripe\\Security\\PasswordEncryptor_PHPHash', 'salt'],
['SilverStripe\\Security\\PasswordEncryptor_LegacyPHPHash', 'encrypt'],
['SilverStripe\\Security\\PasswordEncryptor_LegacyPHPHash', 'salt'],
['SilverStripe\\Security\\PasswordEncryptor_MySQLPassword', 'encrypt'],
['SilverStripe\\Security\\PasswordEncryptor_MySQLPassword', 'salt'],
['SilverStripe\\Security\\PasswordEncryptor_MySQLOldPassword', 'encrypt'],
['SilverStripe\\Security\\PasswordEncryptor_MySQLOldPassword', 'salt'],
['SilverStripe\\Security\\PasswordEncryptor_Blowfish', 'encrypt'],
['SilverStripe\\Security\\PasswordEncryptor_Blowfish', 'salt'],
['*', 'updateValidatePassword'],
];
private static $ignore_function_args = [];
/**
* Return debug_backtrace() results with functions filtered
@ -107,22 +79,23 @@ class Backtrace
// Filter out arguments
foreach ($bt as $i => $frame) {
$match = false;
if (!empty($bt[$i]['class'])) {
if (!empty($frame['class'])) {
foreach ($ignoredArgs as $fnSpec) {
if (is_array($fnSpec) &&
('*' == $fnSpec[0] || $bt[$i]['class'] == $fnSpec[0]) &&
$bt[$i]['function'] == $fnSpec[1]
if (is_array($fnSpec)
&& self::matchesFilterableClass($frame['class'], $fnSpec[0])
&& $frame['function'] == $fnSpec[1]
) {
$match = true;
break;
}
}
} else {
if (in_array($bt[$i]['function'], $ignoredArgs ?? [])) {
if (in_array($frame['function'], $ignoredArgs ?? [])) {
$match = true;
}
}
if ($match) {
foreach ($bt[$i]['args'] as $j => $arg) {
foreach ($frame['args'] as $j => $arg) {
$bt[$i]['args'][$j] = '<filtered>';
}
}
@ -229,4 +202,13 @@ class Backtrace
}
return $result;
}
/**
* Checks if the filterable class is wildcard, of if the class name is the filterable class, or a subclass of it,
* or implements it.
*/
private static function matchesFilterableClass(string $className, string $filterableClass): bool
{
return $filterableClass === '*' || $className === $filterableClass || is_subclass_of($className, $filterableClass);
}
}

View File

@ -2,8 +2,13 @@
namespace SilverStripe\Dev\Tests;
use ReflectionMethod;
use SilverStripe\Core\BaseKernel;
use SilverStripe\Core\CoreKernel;
use SilverStripe\Core\Kernel;
use SilverStripe\Dev\Backtrace;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\DataObject;
class BacktraceTest extends SapphireTest
{
@ -109,4 +114,56 @@ class BacktraceTest extends SapphireTest
$this->assertEquals('<filtered>', $filtered[1]['args']['password']);
$this->assertEquals('myval', $filtered[2]['args']['myarg']);
}
public function matchesFilterableClassProvider(): array
{
return [
[
'anything',
'*',
true,
'Wildcard counts as a match',
],
[
DataObject::class,
BaseKernel::class,
false,
'No match',
],
[
DataObject::class,
DataObject::class,
true,
'Exact match',
],
[
CoreKernel::class,
BaseKernel::class,
true,
'Subclass counts as a match',
],
[
BaseKernel::class,
CoreKernel::class,
false,
'Superclass does not count as a match',
],
[
CoreKernel::class,
Kernel::class,
true,
'Implements interface counts as a match',
],
];
}
/**
* @dataProvider matchesFilterableClassProvider
*/
public function testMatchesFilterableClass(string $className, string $filterableClass, bool $expected, string $message): void
{
$reflectionMethod = new ReflectionMethod(Backtrace::class . '::matchesFilterableClass');
$reflectionMethod->setAccessible(true);
$this->assertSame($expected, $reflectionMethod->invoke(null, $className, $filterableClass), $message);
}
}