From e05dfecfe98911acbcdb7b8b9d0805ebd6b2db70 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 3 Jun 2026 22:31:24 -0400 Subject: [PATCH 1/2] Constrain classes further in MailPreview controller This controller should not attempt to load classes with `\` in the name, nor should it attempt to load classes that do not extedn `MailPreview`. Thanks to Volker Dusch and the PHP Ecosystem security team for reporting this. --- src/Controller/MailPreviewController.php | 10 +++++++--- .../Controller/MailPreviewControllerTest.php | 14 ++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Controller/MailPreviewController.php b/src/Controller/MailPreviewController.php index 602eee77f..482d10aa0 100644 --- a/src/Controller/MailPreviewController.php +++ b/src/Controller/MailPreviewController.php @@ -23,6 +23,7 @@ use Cake\Routing\Router; use Cake\Utility\Inflector; use DebugKit\Mailer\AbstractResult; +use DebugKit\Mailer\MailPreview; use DebugKit\Mailer\PreviewResult; use DebugKit\Mailer\SentMailResult; use Psr\Http\Message\ResponseInterface; @@ -261,18 +262,21 @@ protected function findPreferredPart(AbstractResult $email, ?string $partType): * * @param string $previewName The Mailer name * @param string $emailName The mailer preview method - * @param string $plugin The plugin where the mailer preview should be found + * @param ?string $plugin The plugin where the mailer preview should be found * @return \DebugKit\Mailer\PreviewResult The result of the email preview * @throws \Cake\Http\Exception\NotFoundException */ - protected function findPreview(string $previewName, string $emailName, string $plugin = ''): PreviewResult + protected function findPreview(string $previewName, string $emailName, ?string $plugin = ''): PreviewResult { if ($plugin) { $plugin = "$plugin."; } + if (str_contains($previewName, '\\')) { + throw new NotFoundException("Mailer preview $previewName not found"); + } $realClass = App::className($plugin . $previewName, 'Mailer/Preview'); - if (!$realClass) { + if (!$realClass || !is_subclass_of($realClass, MailPreview::class, true)) { throw new NotFoundException("Mailer preview $previewName not found"); } /** @var \DebugKit\Mailer\MailPreview $mailPreview */ diff --git a/tests/TestCase/Controller/MailPreviewControllerTest.php b/tests/TestCase/Controller/MailPreviewControllerTest.php index ab5617f34..ac627a72b 100644 --- a/tests/TestCase/Controller/MailPreviewControllerTest.php +++ b/tests/TestCase/Controller/MailPreviewControllerTest.php @@ -40,6 +40,20 @@ public function testEmailPluginPassedToView() $this->assertResponseContains('src="?part=html&plugin=DebugkitTestPlugin'); } + /** + * Test that invalid classnames are rejected + * + * @return void + */ + public function testEmailRejectInvalidClassName() + { + $this->get('/debug-kit/mail-preview/preview/Cake\Utility\Inflector/slug'); + $this->assertResponseCode(404); + + $this->get('/debug-kit/mail-preview/preview/Invalid/hello'); + $this->assertResponseCode(404); + } + /** Test email template content * * @return void From dee580840b7f829887894921b1bf487626efe334 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Thu, 4 Jun 2026 13:36:14 -0400 Subject: [PATCH 2/2] Fix default value --- src/Controller/MailPreviewController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/MailPreviewController.php b/src/Controller/MailPreviewController.php index 482d10aa0..ec5c6bffa 100644 --- a/src/Controller/MailPreviewController.php +++ b/src/Controller/MailPreviewController.php @@ -266,7 +266,7 @@ protected function findPreferredPart(AbstractResult $email, ?string $partType): * @return \DebugKit\Mailer\PreviewResult The result of the email preview * @throws \Cake\Http\Exception\NotFoundException */ - protected function findPreview(string $previewName, string $emailName, ?string $plugin = ''): PreviewResult + protected function findPreview(string $previewName, string $emailName, ?string $plugin = null): PreviewResult { if ($plugin) { $plugin = "$plugin.";