From 5e35d950948646e288d95883dd444553aa1b1eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Molakvo=C3=A6?= <14975046+skjnldsv@users.noreply.github.com> Date: Tue, 6 Oct 2026 18:09:57 +0200 Subject: [PATCH] fix(sharebymail): do not escape the note mail heading twice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit addHeading() already escapes its title, so a sharer display name with & or ' showed up as &amp; in the note mail. DefaultShareProvider already passes the heading unescaped. Assisted-by: ClaudeCode:claude-opus-5-5 Signed-off-by: John Molakvoæ <14975046+skjnldsv@users.noreply.github.com> --- apps/sharebymail/lib/ShareByMailProvider.php | 2 +- .../tests/ShareByMailProviderTest.php | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php index 3edb54a45c643..7ebb27d2f60b5 100644 --- a/apps/sharebymail/lib/ShareByMailProvider.php +++ b/apps/sharebymail/lib/ShareByMailProvider.php @@ -547,7 +547,7 @@ protected function sendNote(IShare $share): void { $emailTemplate->setSubject($this->l->t('%s added a note to a file shared with you', [$initiatorDisplayName])); $emailTemplate->addHeader(); $emailTemplate->addBodySender($initiatorDisplayName, $this->settingsManager->replyToInitiator() ? ($initiatorEmailAddress ?? '') : ''); - $emailTemplate->addHeading(htmlspecialchars($heading), $heading); + $emailTemplate->addHeading($heading, $heading); $emailTemplate->addBodyNote($note, $this->l->t('Note')); $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', diff --git a/apps/sharebymail/tests/ShareByMailProviderTest.php b/apps/sharebymail/tests/ShareByMailProviderTest.php index f4f6340545d18..608e9693b391b 100644 --- a/apps/sharebymail/tests/ShareByMailProviderTest.php +++ b/apps/sharebymail/tests/ShareByMailProviderTest.php @@ -2004,4 +2004,39 @@ public function testSendNote(bool $replyToInitiator, string $senderSubline): voi self::invokePrivate($provider, 'sendNote', [$share]); } + + public function testSendNoteDoesNotEscapeHeading(): void { + $provider = $this->getInstance(); + $initiatorUser = $this->createMock(IUser::class); + $initiatorUser->method('getDisplayName')->willReturn('Tom & Jerry\'s'); + $initiatorUser->method('getEMailAddress')->willReturn(null); + $this->userManager->method('get')->with('InitiatorUser')->willReturn($initiatorUser); + $this->settingsManager->method('replyToInitiator')->willReturn(false); + $this->defaults->method('getName')->willReturn('UnitTestCloud'); + $this->urlGenerator->method('linkToRouteAbsolute')->willReturn('https://example.com/file.txt'); + + $message = $this->createMock(Message::class); + $this->mailer->method('createMessage')->willReturn($message); + $template = $this->createMock(IEMailTemplate::class); + $this->mailer->method('createEMailTemplate')->willReturn($template); + $template + ->expects($this->once()) + ->method('addHeading') + ->with( + 'Tom & Jerry\'s shared file.txt with you', + 'Tom & Jerry\'s shared file.txt with you' + ); + $this->mailer->expects($this->once())->method('send')->with($message); + + $node = $this->createMock(File::class); + $node->method('getName')->willReturn('file.txt'); + $share = $this->createMock(IShare::class); + $share->method('getSharedBy')->willReturn('InitiatorUser'); + $share->method('getSharedWith')->willReturn('john@doe.com'); + $share->method('getNode')->willReturn($node); + $share->method('getNote')->willReturn('Some note'); + $share->method('getToken')->willReturn('token'); + + self::invokePrivate($provider, 'sendNote', [$share]); + } }