Connect by JBRH Open Connect

Rendering HTML email safely

Connect renders HTML through mail_render.py, which strips the markup that could act — script, embedded objects, event handlers, styling that escapes the message frame — and rewrites every remote image to load through a proxy. What you read is the sender's words and layout, not the sender's code, and opening a message does not report back to whoever sent it.

Status
Available What this means
Audience
both
Channels
email
In the app
#/inbox
Last verified
Product version
6.3.2

The problem with rendering someone else's HTML#

An HTML email is a document written by a stranger and displayed inside your own authenticated session. Two separate things can go wrong with that. The first is execution: markup that runs, loads or navigates does work on your behalf inside a page that is already signed in. The second is disclosure: a document that fetches anything from the sender's server at render time tells the sender the exact moment a human looked at it.

Connect treats these as two problems with two answers, because they have different failure modes. Sanitising handles the first. Proxying handles the second, and is covered in full on Remote images in email.

What the sanitiser takes out#

GroupWhy it goesWhat survives
Anything that executesScript, event handlers and embedded objects run with the privileges of the page showing themNothing from this group; the text they surrounded is kept
Remote loadsEvery fetch at render time is a signal back to the senderImages, rewritten to a proxied address
Frame-escaping layoutPositioning and sizing that leaves the message region can cover the application's own controlsColour, weight, alignment and the table layout most senders rely on
Malformed structureUnbalanced markup renders differently in every parser, which is what a spoofed message exploitsA re-serialised, well-formed document

The exact rule set lives in mail_render.py and is the single place it lives — the Conversations screen does not have its own softer version, and neither does the Owner's. That matters more than the specific list: a sanitiser with two implementations is a sanitiser with one bypass.

What you end up looking at#

A message that used a normal marketing template still looks like that template: headings, columns, a logo, a coloured button. The button is text and a link rather than something that navigates on its own, and the logo is being served through the proxy. A message that was mostly a tracking pixel and a redirect looks like almost nothing, which is an accurate rendering of what it was.

Where a sender supplied both an HTML part and a plain-text part, the plain-text part is the more reliable thing to quote back into a reply, and it is what the model works from when the HTML carries little beyond presentation. The multipart structure that makes this possible is described in the MIME page under Technology reference.

The edges#

A message renders as a blank block
Its content was inside something removed — commonly an embedded object or a script-built layout. The plain-text part, if there is one, is the fallback.
A logo or signature image does not appear
The proxy could not retrieve it: the host is gone, is slow, or refuses a request that does not carry the original recipient's headers. The message is intact; one asset is not.
The layout is narrower than the sender intended
Frame-escaping width was removed. Nothing was lost from the text.
A link points somewhere unexpected
Sanitising does not rewrite link destinations to make them safe, and never claims to. The address you see is the address in the message; judge it as you would in any client.

Questions#

Can I turn the sanitiser off to see the original message?

No, and there is no per-message override. The stored canonical body still holds what arrived, so an administrator investigating a specific message has the original available; the reading surface does not offer an unsafe mode, because a switch that exists gets used on exactly the message where it should not be.

Does the model see the same thing I see?

It works from the text of the message, not from the rendered layout, so styling that was removed makes no difference to a draft. What does make a difference is a message whose meaning lived in an image, since there is no text to read; those are the threads worth checking before approving a reply.

Does opening a message mark it read at the provider?

Marking read is a write-back, handled separately and described on Writing back to Gmail. Rendering a message in Connect and changing its state at the provider are different actions.