From 752291cce164a1b30cbc342f7fd719cc45bcc070 Mon Sep 17 00:00:00 2001 From: IanM Date: Fri, 3 Oct 2025 14:33:21 +0100 Subject: [PATCH 1/2] fix: validate mail settings for whitespace --- framework/core/src/Mail/MailgunDriver.php | 4 +- framework/core/src/Mail/SmtpDriver.php | 10 +- .../core/src/Mail/ValidatesMailSettings.php | 27 ++++ .../api/settings/MailSettingsTest.php | 145 ++++++++++++++++++ 4 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 framework/core/src/Mail/ValidatesMailSettings.php create mode 100644 framework/core/tests/integration/api/settings/MailSettingsTest.php diff --git a/framework/core/src/Mail/MailgunDriver.php b/framework/core/src/Mail/MailgunDriver.php index 16a57a07f8..13700c77e8 100644 --- a/framework/core/src/Mail/MailgunDriver.php +++ b/framework/core/src/Mail/MailgunDriver.php @@ -18,6 +18,8 @@ class MailgunDriver implements DriverInterface { + use ValidatesMailSettings; + public function availableSettings(): array { return [ @@ -33,7 +35,7 @@ public function availableSettings(): array public function validate(SettingsRepositoryInterface $settings, Factory $validator): MessageBag { return $validator->make($settings->all(), [ - 'mail_mailgun_secret' => 'required', + 'mail_mailgun_secret' => ['required', $this->noWhiteSpace()], 'mail_mailgun_domain' => 'required|regex:/^(?!\-)(?:[a-zA-Z\d\-]{0,62}[a-zA-Z\d]\.){1,126}(?!\d+)[a-zA-Z\d]{1,63}$/', 'mail_mailgun_region' => 'required|in:api.mailgun.net,api.eu.mailgun.net', ])->errors(); diff --git a/framework/core/src/Mail/SmtpDriver.php b/framework/core/src/Mail/SmtpDriver.php index 0158f173e0..5999cb1379 100644 --- a/framework/core/src/Mail/SmtpDriver.php +++ b/framework/core/src/Mail/SmtpDriver.php @@ -18,6 +18,8 @@ class SmtpDriver implements DriverInterface { + use ValidatesMailSettings; + public function __construct( protected EsmtpTransportFactory $factory ) { @@ -37,11 +39,11 @@ public function availableSettings(): array public function validate(SettingsRepositoryInterface $settings, Factory $validator): MessageBag { return $validator->make($settings->all(), [ - 'mail_host' => 'required', - 'mail_port' => 'nullable|integer', + 'mail_host' => ['required', $this->noWhiteSpace()], + 'mail_port' => ['nullable', 'integer', $this->noWhiteSpace()], 'mail_encryption' => 'nullable|in:tls,ssl,TLS,SSL', - 'mail_username' => 'nullable|string', - 'mail_password' => 'nullable|string', + 'mail_username' => ['nullable', 'string', $this->noWhiteSpace()], + 'mail_password' => ['nullable', 'string', $this->noWhiteSpace()], ])->errors(); } diff --git a/framework/core/src/Mail/ValidatesMailSettings.php b/framework/core/src/Mail/ValidatesMailSettings.php new file mode 100644 index 0000000000..c49b9fb26c --- /dev/null +++ b/framework/core/src/Mail/ValidatesMailSettings.php @@ -0,0 +1,27 @@ +prepareDatabase([ + 'users' => [ + $this->normalUser(), + ], + ]); + } + + #[Test] + public function smtpDriverWithWhitespaceIsInvalidated() + { + $this->setting('mail_driver', 'smtp'); + $this->setting('mail_host', ' world'); + $this->setting('mail_port', ' 587 '); + $this->setting('mail_encryption', 'tls'); + $this->setting('mail_username', 'user '); + $this->setting('mail_password', ' password'); + + $mailSettingsResponse = $this->send( + $this->request('GET', '/api/mail/settings', [ + 'authenticatedAs' => 1, + ]) + ); + + $this->assertEquals(200, $mailSettingsResponse->getStatusCode()); + + $data = json_decode((string) $mailSettingsResponse->getBody(), true); + + $this->assertFalse($data['data']['attributes']['sending']); + + $this->assertArrayHasKey('errors', $data['data']['attributes']); + + $this->assertArrayHasKey('mail_host', $data['data']['attributes']['errors']); + $this->assertEquals('The mail host must not contain leading or trailing whitespace.', $data['data']['attributes']['errors']['mail_host'][0]); + + $this->assertArrayHasKey('mail_port', $data['data']['attributes']['errors']); + $this->assertEquals('The mail port must not contain leading or trailing whitespace.', $data['data']['attributes']['errors']['mail_port'][0]); + + $this->assertArrayHasKey('mail_username', $data['data']['attributes']['errors']); + $this->assertEquals('The mail username must not contain leading or trailing whitespace.', $data['data']['attributes']['errors']['mail_username'][0]); + + $this->assertArrayHasKey('mail_password', $data['data']['attributes']['errors']); + $this->assertEquals('The mail password must not contain leading or trailing whitespace.', $data['data']['attributes']['errors']['mail_password'][0]); + } + + #[Test] + public function smtpDriverWithValidSettingsIsNotInvalidated() + { + $this->setting('mail_driver', 'smtp'); + $this->setting('mail_host', 'mail.example.com'); + $this->setting('mail_port', '587'); + $this->setting('mail_encryption', 'tls'); + $this->setting('mail_username', 'user'); + $this->setting('mail_password', 'password'); + + $mailSettingsResponse = $this->send( + $this->request('GET', '/api/mail/settings', [ + 'authenticatedAs' => 1, + ]) + ); + + $this->assertEquals(200, $mailSettingsResponse->getStatusCode()); + + $data = json_decode((string) $mailSettingsResponse->getBody(), true); + + $this->assertEmpty($data['data']['attributes']['errors']); + $this->assertTrue($data['data']['attributes']['sending']); + } + + #[Test] + public function mailgunDriverWithWhitespaceIsInvalidated() + { + $this->setting('mail_driver', 'mailgun'); + $this->setting('mail_mailgun_secret', 'key '); + $this->setting('mail_mailgun_domain', ' example.com'); + $this->setting('mail_mailgun_region', 'api.mailgun.net'); + + $mailSettingsResponse = $this->send( + $this->request('GET', '/api/mail/settings', [ + 'authenticatedAs' => 1, + ]) + ); + + $this->assertEquals(200, $mailSettingsResponse->getStatusCode()); + + $data = json_decode((string) $mailSettingsResponse->getBody(), true); + + $this->assertFalse($data['data']['attributes']['sending']); + + $this->assertArrayHasKey('errors', $data['data']['attributes']); + + $this->assertArrayHasKey('mail_mailgun_secret', $data['data']['attributes']['errors']); + $this->assertEquals('The mail mailgun secret must not contain leading or trailing whitespace.', $data['data']['attributes']['errors']['mail_mailgun_secret'][0]); + + $this->assertArrayHasKey('mail_mailgun_domain', $data['data']['attributes']['errors']); + $this->assertEquals('The mail mailgun domain field format is invalid.', $data['data']['attributes']['errors']['mail_mailgun_domain'][0]); + } + + #[Test] + public function mailgunDriverWithValidSettingsIsNotInvalidated() + { + $this->setting('mail_driver', 'mailgun'); + $this->setting('mail_mailgun_secret', 'key'); + $this->setting('mail_mailgun_domain', 'example.com'); + $this->setting('mail_mailgun_region', 'api.mailgun.net'); + + $mailSettingsResponse = $this->send( + $this->request('GET', '/api/mail/settings', [ + 'authenticatedAs' => 1, + ]) + ); + + $this->assertEquals(200, $mailSettingsResponse->getStatusCode()); + + $data = json_decode((string) $mailSettingsResponse->getBody(), true); + + $this->assertEmpty($data['data']['attributes']['errors']); + $this->assertTrue($data['data']['attributes']['sending']); + } +} From aaf7b701effa4a6eb9f730805a5c107c010c8cbd Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 3 Oct 2025 13:33:35 +0000 Subject: [PATCH 2/2] Apply fixes from StyleCI --- framework/core/src/Mail/MailgunDriver.php | 2 +- framework/core/src/Mail/SmtpDriver.php | 2 +- framework/core/src/Mail/ValidatesMailSettings.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/core/src/Mail/MailgunDriver.php b/framework/core/src/Mail/MailgunDriver.php index 13700c77e8..69554fa543 100644 --- a/framework/core/src/Mail/MailgunDriver.php +++ b/framework/core/src/Mail/MailgunDriver.php @@ -19,7 +19,7 @@ class MailgunDriver implements DriverInterface { use ValidatesMailSettings; - + public function availableSettings(): array { return [ diff --git a/framework/core/src/Mail/SmtpDriver.php b/framework/core/src/Mail/SmtpDriver.php index 5999cb1379..748759a2e6 100644 --- a/framework/core/src/Mail/SmtpDriver.php +++ b/framework/core/src/Mail/SmtpDriver.php @@ -19,7 +19,7 @@ class SmtpDriver implements DriverInterface { use ValidatesMailSettings; - + public function __construct( protected EsmtpTransportFactory $factory ) { diff --git a/framework/core/src/Mail/ValidatesMailSettings.php b/framework/core/src/Mail/ValidatesMailSettings.php index c49b9fb26c..c96b8a3402 100644 --- a/framework/core/src/Mail/ValidatesMailSettings.php +++ b/framework/core/src/Mail/ValidatesMailSettings.php @@ -20,7 +20,7 @@ protected function noWhitespace(): callable { return function ($attribute, $value, $fail) { if ($value !== trim($value)) { - $fail('The ' . str_replace('_', ' ', $attribute) . ' must not contain leading or trailing whitespace.'); + $fail('The '.str_replace('_', ' ', $attribute).' must not contain leading or trailing whitespace.'); } }; }