Repository navigation
Escape merge-tag values in transactional HTML; one-line subjects; validated List-Unsubscribe - #9
Merged
Merged
Conversation
… filter
TemplateMailable put every merge-tag value into the HTML exactly as it was given, so a value a customer typed could add markup or a link to mail sent from the workspace's domain. Values from the data are now HTML-escaped where they land in the HTML; a template author puts trusted markup in with {{ tag | raw }}, and their own default fallbacks are left alone. The subject is always one line. The List-Unsubscribe header is only sent for an http(s) or mailto address with nothing that could end the value.
MailBuilder::interpolate() and MergeTagInterpolator::interpolate() keep not escaping unless escape: true is passed, so callers that escape their own values are unaffected.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found in the Odden Cloud review:
TemplateMailableput every merge-tag value into the message exactly as given. Through the transactional template API the values come from the caller's own users, so a value could add markup or a link to mail that goes out from the workspace's verified domain.Changes
MergeTagInterpolator::interpolate($content, $context, bool $escape = false): with$escape, values that come from the context are HTML-escaped where they are put into the text. The author's own text, including adefault:fallback, is left as written. A newrawfilter ({{ order_table | raw }}) is the explicit way to put trusted markup in.MergeTagRegistry::interpolate()andMailBuilder::interpolate()pass the argument through. Default staysfalse, so callers that escape their own values (campaign and workflow compilers in crm-pro) are unchanged.TemplateMailable: the HTML is interpolated withescape: true; the plain-text part and the subject are not escaped; the subject has control characters (CR/LF) removed and is one line.List-Unsubscribeis only sent for an http(s) or mailto address with no whitespace, angle brackets, quotes or control characters (it was any non-empty string).Behaviour change (release-note it)
Anyone passing HTML in
datatoTemplateMailablemust now write{{ tag | raw }}for it. Everything else renders the same except&,<,>,"and'in values are escaped in HTML (which is what anhref="{{ url }}"needs).Compatibility note
MergeTagInterpolator::interpolate()gained a third optional parameter. A subclass that overrides it must accept it (Odden Cloud has one; it is retired when this ships).Tests
tests/Unit/MergeTagEscapingTest.php: escape on and off, author text and defaults untouched,raw, other filters before escaping, TemplateMailable HTML vs subject vs plain text, List-Unsubscribe table (https, mailto, javascript:, data:, header-ending input, newline, space). 128 passed (it was 115); PHPStan and Pint clean. Run locally.🤖 Generated with Claude Code