N°9574 - fix(email): Prevent CSS leaking into plain-text emails#898
Conversation
|
Hi Håkon, In the meantime, this issue seems to be a regression in iTop 3.2.3, could you base your PR on The fix seems pretty straightforward and uses SymfonyMailer better that what we did when migrating from Laminas, there's juste one questions, did you try you fix with synchronous or asynchronous email sending ? I see that |
daaa39c to
b81b955
Compare
|
Hey @hakonharnes, thank you for your PR (and for reporting that regression !). We'll perform a few tests to see if we want it in 3.2.4 or if it requires a patch (e.g. 3.2.3-1). |
b81b955 to
9a385a1
Compare
Done!
Yes, I tested both synchronous and asynchronous email sending. The old patch worked, but I adjusted the patch after your async comment. The plain-text alternative is now generated at the point where the MIME parts are built, using Symfony's That means both paths behave the same:
This avoids depending on whether CSS was already inlined before serialization. In both sync and async mode, the CKEditor CSS no longer appears in the plain-text part of the email. I amended it to the original commit so commit history is clean. Let me know if you need more adjustments or testing on my end. |
|
Hello @hakonharnes thanks for the PR! I just added unit tests to cover more emails cases, hopefully it will prevent this kind of regressions in the future. |
|
@greptileai review please |
|
| Filename | Overview |
|---|---|
| sources/Core/Email/EmailSymfony.php | Core fix: replaces strip_tags() with DefaultHtmlToTextConverter for the plain-text alternative, and corrects the RFC 2046 part order (plain first, HTML last). Two minor P2 style issues: typo in comment and text part built from the already-image-embedded body. |
| tests/php-unit-tests/unitary-tests/sources/core/Email/EmailSymfonyTest.php | Good test coverage added: part ordering, CSS absence in plain text (with and without custom styles), HTML part integrity, and the inline-images RelatedPart wrapping scenario. |
Sequence Diagram
sequenceDiagram
participant C as Caller
participant SB as SetBody()
participant IC as InlineCssIntoBodyContent()
participant EI as EmbedInlineImages()
participant HC as DefaultHtmlToTextConverter
C->>SB: SetBody(html, text/html, customStyles?)
SB->>IC: InlineCssIntoBodyContent(sBody, customStyles)
note over IC: Returns body unchanged if customStyles is null
IC-->>SB: sBody (CSS-inlined or unchanged)
SB->>EI: EmbedInlineImages(sBody) [modifies by ref]
EI-->>SB: aAdditionalParts, sBody with cid: srcs
SB->>HC: convert(sBody, utf-8)
HC-->>SB: plain text (style tags stripped)
note over SB: Build TextPart(plain), TextPart(html)
note over SB: AlternativePart(plain, html) — RFC 2046 order
alt inline images present
note over SB: Wrap in RelatedPart(alternative, ...images)
end
SB-->>C: m_oMessage body set
Reviews (1): Last reviewed commit: "N°9574 - Add unit tests" | Re-trigger Greptile
Co-authored-by: Molkobain <lajarige.guillaume@free.fr>
a550ee3 to
bb74a1b
Compare
|
Force pushed to rebase branch on |
|
Nevermind, I just saw that we already have them! |
Base information
Symptom (bug) / Objective (enhancement)
When an HTML email notification is sent, the generated
text/plainalternative can contain inlined stylesheet contents.After updating from iTop 3.2.2-1 to 3.2.3, public log update emails may expose CKEditor CSS such as
@keyframes ck-input-shake,@keyframes ck-dialog-fade-in,.ck-reset_all, etc. before the actual email text.Before fix:

After fix:

Reproduction procedure (bug)
text/plainalternative (e.g. Gmail)Expected result: the email body only contains the notification content.
Actual result: stylesheet content is visible before the notification content.
Cause (bug)
The Symfony mailer implementation builds the
text/plainalternative from$sBodyafterInlineCssIntoBodyContent()has injected CSS into the HTML body.The previous implementation used:
At that point,
$sBodycan contain<style>...</style>content.strip_tags()removes the tags but keeps their text content, so CSS rules leak into the plain-text part.Also, the
multipart/alternativeparts were ordered as HTML first and plain text second:For
multipart/alternative, the preferred representation must be last, so the expected order istext/plainfirst, thentext/html.This was introduced with the Symfony mailer migration in
428d2c6356a33c92e5b78c23559cfdd531a63b0f.Proposed solution (bug and enhancement)
Build the plain-text body from the original HTML before CSS inlining, using Symfony's
DefaultHtmlToTextConverter.Then inline CSS only for the HTML part.
Finally, build
multipart/alternativein the standard order:This prevents CSS from leaking into the plain-text part and makes HTML the preferred alternative for clients that support it.
Checklist before requesting a review
Unit test note: I did not add a unit test because this mail rendering path depends on the iTop email/MIME assembly and notification flow. The fix has been verified manually on an iTop instance by comparing the generated/received email before and after the change.