Documentation

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, div

Attributs 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

FichierUsage
app/api/admin/broadcasts/route.tssanitizeLocalized sur subject/body au create
app/api/admin/broadcasts/[id]/route.tsidem au PATCH
app/api/admin/broadcasts/test/route.tssanitizeHtml sur le body avant rendu HTML
lib/email-templates.ts → saveEmailTemplatesanitizeHtml(data.body) à la persistance
app/api/organizations/[id]/invite/route.tsescapeHtml(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.

On this page