fix: stop replies and forwards loading the remote images the viewer blocked

The composer's editor page had no Content-Security-Policy, so quoting a
message loaded every remote image and tracking pixel in it, even when
the viewer had shown that message with remote content blocked.

The editor page now carries a CSP that mirrors the viewer's: nothing
loads but our inline script, styles and images, and remote images and
fonts are blocked whenever the quoted original is one the viewer would
block (untrusted sender and a policy other than "allow"). A reopened
draft that carries a quote counts as untrusted. Inline and pasted
images (`data:`, `blob:`) keep working, and the sent HTML is unchanged.
This commit is contained in:
Linus Rath
2026-09-24 19:10:44 +02:00
parent 92b42e4a4f
commit 235f4ea77f
4 changed files with 138 additions and 4 deletions
+4 -2
View File
@@ -68,6 +68,8 @@ export interface RichTextEditorHandle {
interface Props {
initialHtml?: string;
placeholder?: string;
/** Keep remote images and fonts from loading (read once, on mount). */
blockRemoteImages?: boolean;
onChange?: (html: string) => void;
onSelectionChange?: (state: RichTextSelectionState) => void;
onFocus?: () => void;
@@ -75,7 +77,7 @@ interface Props {
}
const RichTextEditor = React.forwardRef<RichTextEditorHandle, Props>(function RichTextEditor(
{ initialHtml = '', placeholder = '', onChange, onSelectionChange, onFocus, onBlur },
{ initialHtml = '', placeholder = '', blockRemoteImages = false, onChange, onSelectionChange, onFocus, onBlur },
ref,
) {
const c = useColors();
@@ -94,7 +96,7 @@ const RichTextEditor = React.forwardRef<RichTextEditorHandle, Props>(function Ri
// Build the source HTML once. Theming changes won't auto-rebuild the page,
// which is fine: themes change rarely and the user can recompose if needed.
const html = React.useMemo(
() => buildEditorHtml({ initialHtml, placeholder, c }),
() => buildEditorHtml({ initialHtml, placeholder, c, blockRemoteImages }),
// c reference changes per theme switch but we don't want to lose user
// edits on a theme change. Bake in the initial colors only.
// eslint-disable-next-line react-hooks/exhaustive-deps