diff --git a/system/Debug/BaseExceptionHandler.php b/system/Debug/BaseExceptionHandler.php index 9afd450e8b48..0330c53cf97c 100644 --- a/system/Debug/BaseExceptionHandler.php +++ b/system/Debug/BaseExceptionHandler.php @@ -70,7 +70,7 @@ protected function collectVars(Throwable $exception, int $statusCode): array $trace = $exception->getTrace(); if ($this->config->sensitiveDataInTrace !== []) { - $this->maskSensitiveData($trace, $this->config->sensitiveDataInTrace); + $trace = $this->maskSensitiveData($trace, $this->config->sensitiveDataInTrace); } return [ @@ -86,33 +86,50 @@ protected function collectVars(Throwable $exception, int $statusCode): array /** * Mask sensitive data in the trace. + */ + protected function maskSensitiveData(array $trace, array $keysToMask, string $path = ''): array + { + foreach ($trace as $i => $line) { + $trace[$i]['args'] = $this->maskData($line['args'], $keysToMask); + } + + return $trace; + } + + /** + * @param array|object $args * - * @param array|object $trace + * @return array|object */ - protected function maskSensitiveData(&$trace, array $keysToMask, string $path = ''): void + private function maskData($args, array $keysToMask, string $path = '') { foreach ($keysToMask as $keyToMask) { $explode = explode('/', $keyToMask); $index = end($explode); if (strpos(strrev($path . '/' . $index), strrev($keyToMask)) === 0) { - if (is_array($trace) && array_key_exists($index, $trace)) { - $trace[$index] = '******************'; - } elseif (is_object($trace) && property_exists($trace, $index) && isset($trace->{$index})) { - $trace->{$index} = '******************'; + if (is_array($args) && array_key_exists($index, $args)) { + $args[$index] = '******************'; + } elseif ( + is_object($args) && property_exists($args, $index) + && isset($args->{$index}) && is_scalar($args->{$index}) + ) { + $args->{$index} = '******************'; } } } - if (is_object($trace)) { - $trace = get_object_vars($trace); - } - - if (is_array($trace)) { - foreach ($trace as $pathKey => $subarray) { - $this->maskSensitiveData($subarray, $keysToMask, $path . '/' . $pathKey); + if (is_array($args)) { + foreach ($args as $pathKey => $subarray) { + $args[$pathKey] = $this->maskData($subarray, $keysToMask, $path . '/' . $pathKey); + } + } elseif (is_object($args)) { + foreach ($args as $pathKey => $subarray) { + $args->{$pathKey} = $this->maskData($subarray, $keysToMask, $path . '/' . $pathKey); } } + + return $args; } /** diff --git a/system/Debug/Exceptions.php b/system/Debug/Exceptions.php index 3a7c0e4a3c39..8f07681f3a31 100644 --- a/system/Debug/Exceptions.php +++ b/system/Debug/Exceptions.php @@ -334,11 +334,11 @@ protected function collectVars(Throwable $exception, int $statusCode): array /** * Mask sensitive data in the trace. * - * @param array|object $trace + * @param array $trace * - * @return array|object + * @return array * - * @deprecated No longer used. Moved to BaseExceptionHandler. + * @deprecated 4.4.0 No longer used. Moved to BaseExceptionHandler. */ protected function maskSensitiveData($trace, array $keysToMask, string $path = '') { @@ -353,6 +353,8 @@ protected function maskSensitiveData($trace, array $keysToMask, string $path = ' * @param array|object $args * * @return array|object + * + * @deprecated 4.4.0 No longer used. Moved to BaseExceptionHandler. */ private function maskData($args, array $keysToMask, string $path = '') { diff --git a/tests/system/Debug/ExceptionHandlerTest.php b/tests/system/Debug/ExceptionHandlerTest.php index f28f44a9b7e4..cd87eff739cf 100644 --- a/tests/system/Debug/ExceptionHandlerTest.php +++ b/tests/system/Debug/ExceptionHandlerTest.php @@ -11,6 +11,7 @@ namespace CodeIgniter\Debug; +use App\Controllers\Home; use CodeIgniter\Exceptions\PageNotFoundException; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\StreamFilterTrait; @@ -140,4 +141,85 @@ public function testHandleCLIPageNotFoundException(): void $this->resetStreamFilterBuffer(); } + + public function testMaskSensitiveData(): void + { + $maskSensitiveData = $this->getPrivateMethodInvoker($this->handler, 'maskSensitiveData'); + + $trace = [ + 0 => [ + 'file' => '/var/www/CodeIgniter4/app/Controllers/Home.php', + 'line' => 15, + 'function' => 'f', + 'class' => Home::class, + 'type' => '->', + 'args' => [ + 0 => (object) [ + 'password' => 'secret1', + ], + 1 => (object) [ + 'default' => [ + 'password' => 'secret2', + ], + ], + 2 => [ + 'password' => 'secret3', + ], + 3 => [ + 'default' => ['password' => 'secret4'], + ], + ], + ], + 1 => [ + 'file' => '/var/www/CodeIgniter4/system/CodeIgniter.php', + 'line' => 932, + 'function' => 'index', + 'class' => Home::class, + 'type' => '->', + 'args' => [ + ], + ], + ]; + $keysToMask = ['password']; + $path = ''; + + $newTrace = $maskSensitiveData($trace, $keysToMask, $path); + + $this->assertSame(['password' => '******************'], (array) $newTrace[0]['args'][0]); + $this->assertSame(['password' => '******************'], $newTrace[0]['args'][1]->default); + $this->assertSame(['password' => '******************'], $newTrace[0]['args'][2]); + $this->assertSame(['password' => '******************'], $newTrace[0]['args'][3]['default']); + } + + public function testMaskSensitiveDataTraceDataKey(): void + { + $maskSensitiveData = $this->getPrivateMethodInvoker($this->handler, 'maskSensitiveData'); + + $trace = [ + 0 => [ + 'file' => '/var/www/CodeIgniter4/app/Controllers/Home.php', + 'line' => 15, + 'function' => 'f', + 'class' => Home::class, + 'type' => '->', + 'args' => [ + ], + ], + 1 => [ + 'file' => '/var/www/CodeIgniter4/system/CodeIgniter.php', + 'line' => 932, + 'function' => 'index', + 'class' => Home::class, + 'type' => '->', + 'args' => [ + ], + ], + ]; + $keysToMask = ['file']; + $path = ''; + + $newTrace = $maskSensitiveData($trace, $keysToMask, $path); + + $this->assertSame('/var/www/CodeIgniter4/app/Controllers/Home.php', $newTrace[0]['file']); + } }