diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index fa99df6c2..aab65936c 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -10,6 +10,7 @@ namespace OCA\Notifications\Controller; use OCA\Notifications\AppInfo\Application; +use OCA\Notifications\Model\Settings; use OCA\Notifications\Model\SettingsMapper; use OCP\AppFramework\Http; use OCP\AppFramework\Http\Attribute\ApiRoute; @@ -34,7 +35,8 @@ public function __construct( /** * Update personal notification settings * - * @param int $batchSetting How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4) + * @param int<0, 4> $batchSetting How often E-mails about missed notifications should be sent (off: 0; hourly: 1; every three hours: 2; daily: 3; weekly: 4) + * @psalm-param Settings::EMAIL_SEND_* $batchSetting * @param string $soundNotification Enable sound for notifications ('yes' or 'no') * @param string $soundTalk Enable sound for Talk notifications ('yes' or 'no') * @return DataResponse, array{}> @@ -56,7 +58,8 @@ public function personal(int $batchSetting, string $soundNotification, string $s /** * Update default notification settings for new users * - * @param int $batchSetting How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4) + * @param int<0, 4> $batchSetting How often E-mails about missed notifications should be sent (off: 0; hourly: 1; every three hours: 2; daily: 3; weekly: 4) + * @psalm-param Settings::EMAIL_SEND_* $batchSetting * @param string $soundNotification Enable sound for notifications ('yes' or 'no') * @param string $soundTalk Enable sound for Talk notifications ('yes' or 'no') * @return DataResponse, array{}> diff --git a/lib/MailNotifications.php b/lib/MailNotifications.php index c14afd37b..77fbc3899 100644 --- a/lib/MailNotifications.php +++ b/lib/MailNotifications.php @@ -284,7 +284,11 @@ protected function getHTMLContents(INotification $notification): string { $HTMLSubject = $this->getHTMLSubject($notification); $link = $notification->getLink(); if ($link !== '') { - $HTMLSubject = '' . $HTMLSubject . ''; + // Only render absolute links + $scheme = strtolower((string)parse_url($link, PHP_URL_SCHEME)); + if ($scheme === 'http' || $scheme === 'https') { + $HTMLSubject = '' . $HTMLSubject . ''; + } } return $HTMLSubject . '
' . $this->getHTMLMessage($notification); @@ -338,8 +342,17 @@ protected function replaceRichParameters(array $parameters, string $contentStrin $replacement = $parameter['name']; } - if (isset($parameter['link'])) { - $replacements[] = '' . htmlspecialchars($replacement) . ''; + // Only render absolute links + $href = ''; + if (isset($parameter['link']) && is_string($parameter['link'])) { + $scheme = strtolower((string)parse_url($parameter['link'], PHP_URL_SCHEME)); + if ($scheme === 'http' || $scheme === 'https') { + $href = htmlspecialchars($parameter['link']); + } + } + + if ($href !== '') { + $replacements[] = '' . htmlspecialchars($replacement) . ''; } else { $replacements[] = '' . htmlspecialchars($replacement) . ''; } diff --git a/openapi-administration.json b/openapi-administration.json index e86bb9972..ca93a1441 100644 --- a/openapi-administration.json +++ b/openapi-administration.json @@ -696,7 +696,9 @@ "batchSetting": { "type": "integer", "format": "int64", - "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)" + "description": "How often E-mails about missed notifications should be sent (off: 0; hourly: 1; every three hours: 2; daily: 3; weekly: 4)", + "minimum": 0, + "maximum": 4 }, "soundNotification": { "type": "string", diff --git a/openapi-full.json b/openapi-full.json index 6e4379483..83de133ad 100644 --- a/openapi-full.json +++ b/openapi-full.json @@ -807,7 +807,9 @@ "batchSetting": { "type": "integer", "format": "int64", - "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)" + "description": "How often E-mails about missed notifications should be sent (off: 0; hourly: 1; every three hours: 2; daily: 3; weekly: 4)", + "minimum": 0, + "maximum": 4 }, "soundNotification": { "type": "string", @@ -2251,7 +2253,9 @@ "batchSetting": { "type": "integer", "format": "int64", - "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)" + "description": "How often E-mails about missed notifications should be sent (off: 0; hourly: 1; every three hours: 2; daily: 3; weekly: 4)", + "minimum": 0, + "maximum": 4 }, "soundNotification": { "type": "string", diff --git a/openapi.json b/openapi.json index f54f69fab..e33aa9db1 100644 --- a/openapi.json +++ b/openapi.json @@ -1045,7 +1045,9 @@ "batchSetting": { "type": "integer", "format": "int64", - "description": "How often E-mails about missed notifications should be sent (hourly: 1; every three hours: 2; daily: 3; weekly: 4)" + "description": "How often E-mails about missed notifications should be sent (off: 0; hourly: 1; every three hours: 2; daily: 3; weekly: 4)", + "minimum": 0, + "maximum": 4 }, "soundNotification": { "type": "string", diff --git a/tests/Unit/HandlerTest.php b/tests/Unit/HandlerTest.php index 37ee5732a..508a179ca 100644 --- a/tests/Unit/HandlerTest.php +++ b/tests/Unit/HandlerTest.php @@ -64,12 +64,12 @@ public function testFull(): void { 'getSubjectParameters' => [], 'getMessage' => 'message', 'getMessageParameters' => [], - 'getLink' => 'https://link', - 'getIcon' => 'https://icon', + 'getLink' => 'https://example.tld/notification', + 'getIcon' => 'https://example.tld/icon', 'getActions' => [ [ 'getLabel' => 'action_label', - 'getLink' => 'https://action_link', + 'getLink' => 'https://example.tld/action', 'getRequestType' => 'GET', 'isPrimary' => false, ] @@ -120,12 +120,12 @@ public function testFullEmptyMessageForOracle(): void { 'getSubjectParameters' => [], 'getMessage' => '', 'getMessageParameters' => [], - 'getLink' => 'https://link', - 'getIcon' => 'https://icon', + 'getLink' => 'https://example.tld/notification', + 'getIcon' => 'https://example.tld/icon', 'getActions' => [ [ 'getLabel' => 'action_label', - 'getLink' => 'https://action_link', + 'getLink' => 'https://example.tld/action', 'getRequestType' => 'GET', 'isPrimary' => false, ] @@ -176,12 +176,12 @@ public function testDeleteById(): void { 'getSubjectParameters' => [], 'getMessage' => 'message', 'getMessageParameters' => [], - 'getLink' => 'https://link', - 'getIcon' => 'https://icon', + 'getLink' => 'https://example.tld/notification', + 'getIcon' => 'https://example.tld/icon', 'getActions' => [ [ 'getLabel' => 'action_label', - 'getLink' => 'https://action_link', + 'getLink' => 'https://example.tld/action', 'getRequestType' => 'GET', 'isPrimary' => true, ]