🐛 Fixed HTML entities appearing in email publication dates#27655
🐛 Fixed HTML entities appearing in email publication dates#27655schalkneethling wants to merge 2 commits intoTryGhost:mainfrom
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
✅ Files skipped from review due to trivial changes (3)
WalkthroughThis pull request adds normalization to prevent forward-slash hex entities from appearing in rendered email content. finalize.js now replaces hex-encoded forward slashes (e.g. /, /) with literal 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ast-grep (0.42.1)ghost/core/test/unit/server/services/email-service/email-renderer.test.js[] Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@ghost/core/test/unit/server/services/email-service/email-renderer.test.js`:
- Around line 2413-2423: The test currently only checks for 'Author/Name' and a
formatted date but doesn't ensure slashes in dates aren't HTML-encoded; update
the assertions in the email-renderer test that calls renderBody() to also verify
that slash-encoding is not present by asserting that response.html and
response.plaintext do NOT contain any slash HTML-entities (e.g. '/' or
'/' / case variants), or alternatively adjust the test input to pass a
publishedAt string that contains a literal '/' so renderBody(publishedAt) is
exercised for slash characters; reference response.html, response.plaintext and
the renderBody() invocation when making the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6571cb98-8f89-4e48-b3b2-7d171af69380
📒 Files selected for processing (4)
ghost/core/core/server/services/email-rendering/finalize.jsghost/core/core/server/services/email-service/email-renderer.jsghost/core/test/unit/server/services/email-service/email-renderer.test.jsghost/i18n/lib/i18n.js
76442b3 to
51828c6
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 51828c6. Configure here.
ref TryGhost#26905 - i18next's escapeValue:true encodes forward slashes as / hex entities in interpolated values. When dates or author names containing "/" flow through t() helpers into the email template, the resulting / sequences appear as literal text in plaintext email parts and inbox previews - added / to / hex entity decoding in the email rendering pipeline (renderBody and finalizeHtml) to clean up any slash entities before the email is sent, regardless of where they originate - reordered plaintext generation to run after entity fixes so the text/plain MIME part also receives clean, decoded text - kept i18next's escapeValue:true intact to preserve XSS protection for triple-brace {{{t ...}}} expressions that interpolate user-controlled values like site titles and author names
ref TryGhost#26905 - the hex-entity regex only matched / (hex) but not &TryGhost#47; (decimal) - added |47 alternative to handle the decimal form in case cheerio, juice, or any future serializer emits the decimal variant
51828c6 to
b952171
Compare
|
You have used all of your free Bugbot PR reviews. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |

ref #26905
i18next's default escapeValue:true was encoding forward slashes as / hex entities in interpolated values, causing dates like "19/03/2026" to appear as "19/03/2026" in plaintext email parts
changed i18n interpolation to escapeValue:false for all namespaces (was only set for theme), since Handlebars already handles HTML escaping in email templates and React/JSX handles it in client apps
added defense-in-depth / to / cleanup in the email rendering pipeline to catch any hex-encoded slashes from cheerio/juice serialization
reordered plaintext generation to run after entity fixes so the text/plain MIME part also receives clean, decoded text
I've read and followed the Contributor Guide
I've explained my change
I've written an automated test to prove my change works
Note
Low Risk
Low risk string-normalization change in the email rendering pipeline plus a unit test; primary risk is unintended replacement of legitimate numeric entities, but it’s narrowly scoped to forward-slash encodings.
Overview
Prevents forward slashes from being serialized as hex/numeric HTML entities (e.g.
/) during email HTML processing, which could show up literally in inbox previews andtext/plainparts.Adds a defense-in-depth cleanup in both
email-rendering/finalize.jsandEmailRenderer.renderBody, and reorders plaintext generation to run after entity fixes so the plaintext output is also clean. Includes a unit test asserting both HTML and plaintext contain raw/for interpolated values (issue#26905).Reviewed by Cursor Bugbot for commit 51828c6. Bugbot is set up for automated code reviews on this repo. Configure here.