import DOMPurify, { type Config as DOMPurifyConfig } from 'dompurify'
/**
* Options de sanitization du corps HTML d'un mail.
*/
export type SanitizeMailHtmlOptions = {
/**
* Si true, les images distantes (http/https) sont affichées directement.
* Par défaut false — les images distantes sont remplacées par un placeholder
* cliquable pour éviter le tracking par pixel.
*/
allowImages?: boolean
}
/**
* Configuration DOMPurify bloquante pour les corps de mail.
* - Bloque les balises dangereuses : script, iframe, object, embed, style, link, meta, form, input
* - Bloque les attributs événements (on*) et les URI javascript:
* - Autorise les URI data: uniquement pour les images (PNG/JPEG/GIF/WEBP) — images inline CID
*/
const DOMPURIFY_CONFIG: DOMPurifyConfig = {
FORBID_TAGS: [
'script',
'iframe',
'object',
'embed',
'style',
'link',
'meta',
'form',
'input',
'button',
'textarea',
'select',
'base',
'applet',
],
FORBID_ATTR: [
'onerror',
'onload',
'onclick',
'onmouseover',
'onmouseout',
'onmouseenter',
'onmouseleave',
'onfocus',
'onblur',
'onchange',
'onsubmit',
'onreset',
'onkeydown',
'onkeyup',
'onkeypress',
'ondblclick',
'oncontextmenu',
'onwheel',
'ondrag',
'ondrop',
'oncopy',
'oncut',
'onpaste',
'action',
'formaction',
'xlink:href',
],
ALLOWED_URI_REGEXP: /^(?:https?|mailto|tel|cid|data:image\/(?:png|jpeg|gif|webp)(?:;base64,)?)(?::|$)/i,
FORCE_BODY: true,
WHOLE_DOCUMENT: false,
}
/**
* Remplace les balises avec src http(s):// par un bouton placeholder.
* Le src original est stocké en data-mail-image-src pour permettre l'affichage
* à la demande de l'utilisateur (Phase 5 — MailMessageViewer).
*/
function replaceRemoteImages(html: string): string {
// Utiliser un DOMParser côté client uniquement (SSR-safe : le guard process.client
// est géré par l'appelant dans un composant Vue — ce helper ne tourne que client-side)
const parser = new DOMParser()
const doc = parser.parseFromString(html, 'text/html')
const images = doc.querySelectorAll('img')
images.forEach((img) => {
const src = img.getAttribute('src') ?? ''
const isRemote = /^https?:\/\//i.test(src)
if (!isRemote) return
// Remplacer par un span cliquable (pas de