Sanitization HTML
Helpers `sanitizeHtml` (DOMPurify) et `escapeHtml` pour rendre du contenu user-authored sans XSS.
lib/sanitize.ts expose deux helpers complémentaires pour traiter le
contenu user-authored avant persistance ou rendu.
sanitizeHtml(html) — pour rich text
Wraps isomorphic-dompurify avec une whitelist conservative de balises
adaptées au rich text (broadcasts, email templates, notes user).
import { sanitizeHtml } from "@/lib/sanitize";
const cleanBody = sanitizeHtml(userInput);
// → strip script tags, on* attrs, javascript: URLs, etc.Balises autorisées :
p, br, strong, em, u, s, code, pre,
h1-h6, ul, ol, li, blockquote, a, img, hr, span, divAttributs autorisés : href, target, rel, src, alt, title, class.
URI scheme : https?:, mailto:, tel: uniquement (pas javascript:,
data: ni file:).
sanitizeLocalized(map) — pour les Json multi-locale
Helper pour les champs Record<string, string> (subject/body
broadcasts par locale) :
import { sanitizeLocalized } from "@/lib/sanitize";
await prisma.broadcast.create({
data: {
subject: sanitizeLocalized(parsed.data.subject),
body: sanitizeLocalized(parsed.data.body),
// ...
},
});Sanitize chaque valeur du Record indépendamment.
escapeHtml(s) — pour le texte plain dans HTML
Pour les champs user-controlled qui ne devraient jamais contenir de markup (org name, username, etc.) mais qu'on interpole dans un template HTML (email d'invitation par ex.) :
import { escapeHtml } from "@/lib/sanitize";
const html = `<p>Tu es invité à rejoindre <strong>${escapeHtml(org.name)}</strong>.</p>`;Encode &, <, >, ", '. Léger comparé à DOMPurify, suffisant
pour du texte qui transite en attribut ou en contenu.
Où c'est utilisé dans le boilerplate
| Fichier | Usage |
|---|---|
app/api/admin/broadcasts/route.ts | sanitizeLocalized sur subject/body au create |
app/api/admin/broadcasts/[id]/route.ts | idem au PATCH |
app/api/admin/broadcasts/test/route.ts | sanitizeHtml sur le body avant rendu HTML |
lib/email-templates.ts → saveEmailTemplate | sanitizeHtml(data.body) à la persistance |
app/api/organizations/[id]/invite/route.ts | escapeHtml(organization.name) dans le mail d'invitation |
Quand l'utiliser dans ton code
Règle simple :
- Le contenu vient d'un user et tu vas l'interpoler dans du HTML destiné à un autre user → sanitize
- C'est du texte plain (nom, email, code) interpolé dans HTML → escapeHtml
- C'est du markdown rendu par un parser (qui lui-même produit du HTML safe) → laisse le parser faire, ne sanitize pas après
Ne jamais afficher de HTML user-authored avec dangerouslySetInnerHTML
sans avoir d'abord passé par sanitizeHtml.