Harden the email sanitizer's CSS handling

Rewrite mail CSS in place instead of cutting pieces out, so a strip can no
longer join text into a closing </style>, and escape < last. Decode escaped
letters before checking, parse url() properly and drop CSS that cannot be
parsed, and disable @import and image-set() in every spelling. The body
element's style goes through the same path.

Give <area> links the same target, rel and click handling as <a>, strip
<style> blocks from HTML quoted into the composer, and contain the editor's
layout as .message-body already is.
This commit is contained in:
2026-09-16 07:07:26 -07:00
parent d0b13272f3
commit 55fcbf72f5
5 changed files with 228 additions and 24 deletions
+2 -1
View File
@@ -1594,7 +1594,8 @@ select optgroup { background-color: var(--bg-elev); color: var(--fg); }
.recipients .chip { height: 24px; }
.recipients input { flex: 1; min-width: 120px; border: 0; background: transparent; outline: none; height: 28px; }
.composer-editor { flex: 1; min-height: 0; display: flex; flex-direction: column; position: relative; }
.editor-area { flex: 1; min-height: 120px; overflow-y: auto; padding: 12px 16px; outline: none; line-height: 1.5; font-size: 14px; font-family: var(--font-sans); }
/* Quoted mail lands in the app document, so contain it as .message-body does: its positioning cannot reach past the editor. */
.editor-area { flex: 1; min-height: 120px; overflow-y: auto; contain: layout; padding: 12px 16px; outline: none; line-height: 1.5; font-size: 14px; font-family: var(--font-sans); }
.editor-area:empty::before, .editor-area[data-empty="true"]::before { content: attr(data-placeholder); color: var(--fg-faint); pointer-events: none; position: absolute; }
.editor-area blockquote { margin: 0 0 0 .8ex; border-left: 2px solid var(--border-strong); padding-left: 1ex; color: var(--fg-muted); }
.editor-area img { max-width: 100%; height: auto; }