Skip to content

fix(html): prevent invalid UTF-8 from blanking mail body#13352

Merged
kesselb merged 1 commit into
mainfrom
fix/blank-body-invalid-utf8
Jul 24, 2026
Merged

fix(html): prevent invalid UTF-8 from blanking mail body#13352
kesselb merged 1 commit into
mainfrom
fix/blank-body-invalid-utf8

Conversation

@ChristophWurst

Copy link
Copy Markdown
Member

Html::convertLinks() passes the plain-text body to UrlLinker::escapeHtml(), which calls htmlspecialchars() without ENT_SUBSTITUTE/ENT_IGNORE. On any invalid UTF-8 byte, that call returns an empty string for the entire input, not just the bad character - so one malformed byte anywhere in a message wiped the whole visible body.

Reproduced with a real report: a Domino/Notes-originated reply contained an emoji encoded as a raw UTF-16 surrogate pair (CESU-8, each half UTF-8-encoded separately instead of combined into one 4-byte sequence), which is invalid UTF-8 and triggered the bug.

Repair the body via mb_convert_encoding() before it reaches convertLinks() when it isn't valid UTF-8, so only the malformed bytes degrade instead of the entire message.

Assisted-by: Claude:claude-sonnet-4-6

🤖 AI (if applicable)

  • The content of this PR was partly or fully generated using AI

Html::convertLinks() passes the plain-text body to
UrlLinker::escapeHtml(), which calls htmlspecialchars() without
ENT_SUBSTITUTE/ENT_IGNORE. On any invalid UTF-8 byte, that call
returns an empty string for the entire input, not just the bad
character - so one malformed byte anywhere in a message wiped the
whole visible body.

Reproduced with a real report: a Domino/Notes-originated reply
contained an emoji encoded as a raw UTF-16 surrogate pair (CESU-8,
each half UTF-8-encoded separately instead of combined into one
4-byte sequence), which is invalid UTF-8 and triggered the bug.

Repair the body via mb_convert_encoding() before it reaches
convertLinks() when it isn't valid UTF-8, so only the malformed
bytes degrade instead of the entire message.

Assisted-by: Claude:claude-sonnet-4-6
Signed-off-by: Christoph Wurst <1374172+ChristophWurst@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a mail-rendering edge case in OCA\Mail\Service\Html::convertLinks() where invalid UTF-8 bytes could cause the entire plain-text body to become empty during HTML escaping/linkification, improving resilience against malformed real-world message encodings (e.g., CESU-8 / surrogate pairs).

Changes:

  • Add UTF-8 validity check and attempt to “repair” invalid input before passing it into UrlLinker/htmlspecialchars.
  • Add unit tests covering several invalid UTF-8 scenarios and a valid multibyte control case.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
lib/Service/Html.php Adds UTF-8 validation and re-encoding before running linkification/escaping to prevent full-body blanking on invalid bytes.
tests/Unit/Service/HtmlTest.php Adds regression tests for invalid UTF-8 inputs to ensure link conversion does not produce an empty output and preserves visible content.

Comment thread lib/Service/Html.php
Comment on lines +62 to +68
if (!mb_check_encoding($data, 'UTF-8')) {
// Some senders (e.g. Lotus Notes/Domino) declare a message as UTF-8 while
// actually sending a different charset. UrlLinker's escapeHtml() calls
// htmlspecialchars() without ENT_SUBSTITUTE/ENT_IGNORE, which returns an
// empty string for the whole input on the first invalid byte it hits.
$data = mb_convert_encoding($data, 'UTF-8', 'UTF-8');
}
@kesselb
kesselb merged commit 55b8da3 into main Jul 24, 2026
44 checks passed
@kesselb
kesselb deleted the fix/blank-body-invalid-utf8 branch July 24, 2026 15:47
@kesselb

kesselb commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

/backport to stable5.10

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants