Ir al contenido

Seguridad y sanitización

CapaQué haceDónde
1. Schema TiptapSolo permite nodos/marks del preset activoEditor (cliente)
2. Output sanitizadobuildOutput() sanitiza el HTML emitidoEditor (cliente)
3. ViewerDOMPurify antes de innerHTMLViewer (cliente)
4. BackendDebe validar/sanitizar antes de persistirServidor (responsabilidad de Prolibu)
  • Tags: <script>, <object>, <embed>, <form>, <textarea>, <style>
  • URLs: javascript: en href (DOMPurify default)
  • Atributos: todos los on* event handlers (onerror, onload, onclick, etc.)
  • Iframes: solo se permiten en el preset document, y solo con src https: (regex SAFE_IFRAME_SRC_RE; bloquea http:, data: y javascript:). No hay whitelist de dominios — cualquier URL https: la satisface (YouTube, Vimeo, Loom, Google Slides…).
  • style: solo en email/document, y capado a color, background-color y text-align; se descartan declaraciones con url()/expression() y el atributo entero si queda vacío.
PresetTags permitidosAtributos extras
commentp, br, strong, em, a, spanhref, target, rel, data-mention-*
task+ u, s, code, ul, ol, li, blockquote, input, label+ type, checked, disabled, data-type, data-checked, data-variant
email+ h2, h3, div, img+ style, data-variable, src, alt, width, height, data-file-id, data-image-layout
document+ h1–h4, hr, div, mark, img, table, thead, tbody, tr, th, td, input, label, iframe+ style, data-variable, colspan, rowspan, src, alt, width, height, type, checked, disabled, allowfullscreen, frameborder, allow, loading

Los <input type="checkbox"> de las task lists se permiten mediante un hook de DOMPurify que filtra por tipo. Cualquier otro <input> se elimina.

  • Actualmente: allowBase64: true — pegar una imagen produce <img src="data:image/...">.
  • Para producción: se recomienda usar imageUpload para subir a CDN y reemplazar el base64 por una URL https:.
<co-rich-text-editor
preset="document"
.imageUpload=${async (file) => {
const url = await uploadToCDN(file);
return url; // reemplaza el base64 placeholder
}}
></co-rich-text-editor>

El sanitizador se puede importar sin cargar Tiptap:

import { sanitize } from '@prolibu-suite/cobalt-rich-text';
const clean = sanitize(dirtyHtml, 'task');

Debe hacerse en dos lugares simultáneamente:

  1. sanitizer.tsSANITIZE_CONFIGS[preset].ALLOWED_TAGS / ALLOWED_ATTR
  2. co-rich-text-viewer.tsx → su copia local de ALLOWED_TAGS / ALLOWED_ATTR

Además, el nodo debe estar registrado en el schema (preset o customExtensions) o el output lo eliminará.