fix(chatter): let callers post real HTML via message_post(body_is_html=) - #62
Open
glazperle wants to merge 1 commit into
Open
fix(chatter): let callers post real HTML via message_post(body_is_html=)#62glazperle wants to merge 1 commit into
glazperle wants to merge 1 commit into
Conversation
Any markup passed to chatter_post arrived in the chatter as literal tags.
Odoo escapes every `str` body handed to message_post — only a
markupsafe.Markup survives, and RPC cannot carry one.
Odoo 17 added `message_post(body_is_html=...)` for exactly this case
("to be used only for RPC calls", mail/models/mail_thread.py). chatter_post
now exposes it, forwards it on 17+, and omits it on 16, which stores bodies
verbatim anyway. Verified end-to-end against Odoo 17: the body comes back
byte-for-byte as sent.
The parameter is opt-in rather than auto-detected. Detection cannot separate
markup from prose — "Please forward to <a.schmidt@example.com>" and
"if a<b and b>c" are both valid tag syntax — and treating them as markup
silently deletes the text when Odoo renders the field. A body that looks like
markup without the flag therefore gets an advisory warning and is posted
unchanged; looks_like_html exists only to raise that warning, and its
accepted false positives are pinned by a test.
body_is_html is part of the canonical payload, so an approval previewed as
plain text cannot be executed as markup.
Co-Authored-By: Claude Opus 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.
Summary
Any markup passed to
chatter_postarrives in the chatter as literal tags. Odoo escapes everystrbody handed tomessage_post— only amarkupsafe.Markupsurvives, and RPC cannot carry one, so no caller of this tool can currently post a formatted message.Odoo 17 added
message_post(body_is_html=...)for exactly this case ("to be used only for RPC calls",mail/models/mail_thread.py).chatter_postnow exposes abody_is_htmlparameter, forwards it on Odoo 17+, and omits it on 16, which stores bodies verbatim anyway.User-facing behavior change: a new optional
body_is_htmlparameter (defaultfalse). With it unset, behavior is byte-for-byte unchanged; the only addition is an advisorywarningsentry when a body looks like markup but the flag is off.Why opt-in rather than auto-detected
Detection cannot separate markup from prose. Both of these are valid tag syntax:
Please forward to <a.schmidt@example.com>if a<b and b>cTreating either as HTML silently deletes the bracketed text when Odoo renders the field — a lossy failure worse than the escaped-tags one it would fix. So nothing is ever converted:
looks_like_htmlexists only to raise the warning, and its accepted false positives are pinned by a test.body_is_htmlis part of the canonical payload, so an approval previewed as plain text cannot be executed as markup.Coverage
Seven unit tests in
tests/test_server.py(forwarding on 17, omission on 16, unchanged plain-text path, warning path, preview warning, token binding, gated round-trip) and alooks_like_htmlgroup intests/test_tool_helpers.pyincluding the deliberately accepted false positives. They use the existing_ChatterClientpattern and need no live Odoo.Verification
mail.message.bodycomes back byte-for-byte as sent, unescaped. 16 is covered by unit test only, since the change there is the absence of the kwarg.The deselected test is
test_from_path_rejects_symlink_escape_within_upload_root, which needs the Windows symlink privilege and fails on this machine both before and after the change.Checklist
README.md,docs/troubleshooting.md)CHANGELOG.mdunder## UnreleasedRelation to #61
No conflict expected: the
tools_write.pyregions are disjoint (chatter_posthere,_execute_approved_write_gatedthere). Onlytests/test_server.pyandCHANGELOG.mdare shared, both append-only.