diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 01701274c..ee142bcd7 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,11 +1,5 @@ parameters: ignoreErrors: - - - message: '#^Method DebugKit\\Mailer\\Transport\\DebugKitTransport\:\:send\(\) should return array\{headers\: string, message\: string\} but returns array\{headers\: non\-empty\-array\, message\: array\{text\: string, html\: string\}\}\.$#' - identifier: return.type - count: 1 - path: src/Mailer/Transport/DebugKitTransport.php - - message: '#^Parameter \#1 \$request of method DebugKit\\ToolbarService\:\:saveData\(\) expects Cake\\Http\\ServerRequest, Psr\\Http\\Message\\ServerRequestInterface given\.$#' identifier: argument.type diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 91a8f8e74..a0523438c 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -11,9 +11,6 @@ - - - emailLog]]> diff --git a/src/Controller/ToolbarController.php b/src/Controller/ToolbarController.php index a236b5e8d..abf2b8614 100644 --- a/src/Controller/ToolbarController.php +++ b/src/Controller/ToolbarController.php @@ -46,6 +46,9 @@ public function clearCache(): void if (!$name) { throw new NotFoundException('Invalid cache engine name.'); } + if (!Cache::getConfig($name)) { + throw new NotFoundException(sprintf('Unknown cache engine "%s".', $name)); + } $success = Cache::clear($name); $message = $success ? sprintf('%s cache cleared.', $name) : diff --git a/src/Mailer/Transport/DebugKitTransport.php b/src/Mailer/Transport/DebugKitTransport.php index 0cd510160..f764bb66d 100644 --- a/src/Mailer/Transport/DebugKitTransport.php +++ b/src/Mailer/Transport/DebugKitTransport.php @@ -7,6 +7,7 @@ use Cake\Core\App; use Cake\Mailer\AbstractTransport; use Cake\Mailer\Message; +use InvalidArgumentException; /** * Debug Transport class, useful for emulating the email sending process and inspecting @@ -37,6 +38,11 @@ class DebugKitTransport extends AbstractTransport */ public function __construct(array $config = [], ?AbstractTransport $originalTransport = null) { + if (!isset($config['debugKitLog']) || !$config['debugKitLog'] instanceof ArrayObject) { + throw new InvalidArgumentException( + 'DebugKitTransport requires a `debugKitLog` config entry of type `ArrayObject`.', + ); + } $this->emailLog = $config['debugKitLog']; if ($originalTransport !== null) { @@ -62,7 +68,17 @@ public function __construct(array $config = [], ?AbstractTransport $originalTran } /** - * @inheritDoc + * Capture the message into the in-memory email log and optionally forward + * to a wrapped real transport. + * + * Overrides the parent return shape: DebugKit stores the headers as an + * associative array (so the panel can render rows) and splits the body + * into text/html parts. Callers that consume this transport's return + * value directly must account for this richer shape. + * + * @param \Cake\Mailer\Message $message The message to capture. + * @return array + * @phpstan-return array{headers: array, message: array{text: string, html: string}}|array */ public function send(Message $message): array { diff --git a/src/Panel/RequestPanel.php b/src/Panel/RequestPanel.php index ad8bc3fa4..377dd970b 100644 --- a/src/Panel/RequestPanel.php +++ b/src/Panel/RequestPanel.php @@ -54,7 +54,7 @@ public function shutdown(EventInterface $event): void 'query' => Debugger::exportVarAsNodes($request->getQueryParams(), $maxDepth), 'data' => Debugger::exportVarAsNodes($request->getData(), $maxDepth), 'cookie' => Debugger::exportVarAsNodes($request->getCookieParams(), $maxDepth), - 'get' => Debugger::exportVarAsNodes($_GET, $maxDepth), + 'get' => Debugger::exportVarAsNodes($request->getQueryParams(), $maxDepth), 'session' => Debugger::exportVarAsNodes($request->getSession()->read(), $maxDepth), 'matchedRoute' => $request->getParam('_matchedRoute'), 'headers' => [ diff --git a/src/ToolbarService.php b/src/ToolbarService.php index ad544bcf4..590c2a195 100644 --- a/src/ToolbarService.php +++ b/src/ToolbarService.php @@ -291,20 +291,22 @@ public function saveData(ServerRequest $request, ResponseInterface $response): R foreach ($this->registry->loaded() as $name) { $panel = $this->registry->{$name}; $data = null; + $handlerInstalled = false; try { $data = $panel->data(); - // Set error handler to catch warnings/errors during serialization - set_error_handler(function ($errno, $errstr) use ($name): void { - throw new Exception("Serialization error in panel '{$name}': {$errstr}"); - }); + // Catch only warnings/notices raised during serialization; fatals + // and exceptions in __sleep/__serialize already surface as throws. + set_error_handler( + function ($errno, $errstr) use ($name): bool { + throw new Exception("Serialization error in panel '{$name}': {$errstr}"); + }, + E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE, + ); + $handlerInstalled = true; $content = serialize($data); - - restore_error_handler(); } catch (Exception $e) { - restore_error_handler(); - $errorMessage = sprintf( 'Failed to serialize data for panel "%s": %s', $name, @@ -312,12 +314,16 @@ public function saveData(ServerRequest $request, ResponseInterface $response): R ); Log::warning($errorMessage); - Log::debug('Panel data type: ' . gettype($data ?? null)); + Log::debug('Panel data type: ' . gettype($data)); $content = serialize([ 'error' => $errorMessage, 'panel' => $name, ]); + } finally { + if ($handlerInstalled) { + restore_error_handler(); + } } $row->panels[] = $requests->Panels->newEntity([ 'panel' => $name, diff --git a/src/View/Helper/CredentialsHelper.php b/src/View/Helper/CredentialsHelper.php index f51f2c788..2e47832b7 100644 --- a/src/View/Helper/CredentialsHelper.php +++ b/src/View/Helper/CredentialsHelper.php @@ -59,7 +59,7 @@ public function filter(mixed $in): mixed $link = $this->Html->tag('a', '******', [ 'class' => 'filtered-credentials', 'title' => h($credentials), - 'onclick' => 'this.innerHTML = this.title', + 'onclick' => 'this.textContent = this.title', ]); return h($protocol) . $link . '@' . h($tail); diff --git a/templates/element/packages_panel.php b/templates/element/packages_panel.php index 90be6d741..34579ef17 100644 --- a/templates/element/packages_panel.php +++ b/templates/element/packages_panel.php @@ -54,7 +54,11 @@ - + - +
@@ -36,8 +37,8 @@

diff --git a/templates/element/routes_panel.php b/templates/element/routes_panel.php index f382d03a1..bae904ea4 100644 --- a/templates/element/routes_panel.php +++ b/templates/element/routes_panel.php @@ -45,7 +45,7 @@
diff --git a/templates/element/timer_panel.php b/templates/element/timer_panel.php index ba72f7f6a..31dda79cf 100644 --- a/templates/element/timer_panel.php +++ b/templates/element/timer_panel.php @@ -65,7 +65,7 @@ assertResponseContains('success'); } + /** + * Posting an unknown cache engine name 404s instead of returning a + * misleading 200 with `success: false`. + * + * @return void + */ + public function testClearCacheUnknownEngine() + { + $this->configRequest(['headers' => ['Accept' => 'application/json']]); + $this->post('/debug-kit/toolbar/clear-cache', ['name' => 'does-not-exist']); + $this->assertResponseCode(404); + } + /** * Test clearing the session. * diff --git a/tests/TestCase/Mailer/Transport/DebugKitTransportTest.php b/tests/TestCase/Mailer/Transport/DebugKitTransportTest.php index 218c7bbf5..f0bc0ce08 100644 --- a/tests/TestCase/Mailer/Transport/DebugKitTransportTest.php +++ b/tests/TestCase/Mailer/Transport/DebugKitTransportTest.php @@ -19,6 +19,7 @@ use Cake\Mailer\Message; use Cake\TestSuite\TestCase; use DebugKit\Mailer\Transport\DebugKitTransport; +use InvalidArgumentException; class DebugKitTransportTest extends TestCase { @@ -67,6 +68,21 @@ public function testMethodProxy() $this->assertSame('bloop', $this->transport->customMethod()); } + public function testConstructorRejectsMissingDebugKitLog() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('debugKitLog'); + + new DebugKitTransport([]); + } + + public function testConstructorRejectsWrongDebugKitLogType() + { + $this->expectException(InvalidArgumentException::class); + + new DebugKitTransport(['debugKitLog' => 'not-an-arrayobject']); + } + public function testEmailCapture() { $message = new Message(); diff --git a/tests/TestCase/View/Helper/CredentialsHelperTest.php b/tests/TestCase/View/Helper/CredentialsHelperTest.php index 904c97ddb..19de94cd3 100644 --- a/tests/TestCase/View/Helper/CredentialsHelperTest.php +++ b/tests/TestCase/View/Helper/CredentialsHelperTest.php @@ -84,8 +84,8 @@ public static function credentialsProvider() [['value'], ['value']], ['http://example.com', 'http://example.com'], ['ssh://ssh.example.com', 'ssh://ssh.example.com'], - ['http://user@example.com', 'http://
******@example.com'], - ['http://user:pass@example.com', 'http://******@example.com'], + ['http://user@example.com', 'http://******@example.com'], + ['http://user:pass@example.com', 'http://******@example.com'], ]; } }