diff --git a/static/js/emailLibrary/signatureFold.js b/static/js/emailLibrary/signatureFold.js index 4c3868e..4cd932b 100644 --- a/static/js/emailLibrary/signatureFold.js +++ b/static/js/emailLibrary/signatureFold.js @@ -110,13 +110,18 @@ export function _foldSummary(label, iconSvg, meta) { subMeta = ''; } } + // `meta` is derived from _extractQuoteMeta, which strips tags but then + // un-escapes entities (to recover `` for bubble alignment) — + // so it can carry attacker-controlled angle brackets from a quoted block. + // This summary is built into innerHTML, so escape both parts to stop a + // crafted quote (e.g. `From: `) from running script. const metaSpan = subMeta - ? `${subMeta}` + ? `${_esc(subMeta)}` : ''; return ( '' + iconSvg - + `` + + `` + metaSpan + '' + '' @@ -158,9 +163,12 @@ export function _extractQuoteMeta(html) { if (from.length > 60) from = from.slice(0, 57) + '…'; if (date.length > 28) date = date.slice(0, 25) + '…'; - if (from && date) return `${_esc(from)} · ${_esc(date)}`; - if (from) return _esc(from); - if (date) return _esc(date); + // Return the raw sender/date text; `_foldSummary` is the single sink that + // builds these into HTML, so it owns escaping. Escaping here too would + // double-encode (e.g. "Ben & Jerry" -> "Ben & Jerry"). + if (from && date) return `${from} · ${date}`; + if (from) return from; + if (date) return date; return ''; }