Low severityOSV Advisory· Published May 8, 2025· Updated Apr 15, 2026
CVE-2025-46812
CVE-2025-46812
Description
Trix is a what-you-see-is-what-you-get rich text editor for everyday writing. Versions prior to 2.1.15 are vulnerable to XSS attacks when pasting malicious code. An attacker could trick a user to copy and paste malicious code that would execute arbitrary JavaScript code within the context of the user's session, potentially leading to unauthorized actions being performed or sensitive information being disclosed. This issue has been patched in version 2.1.15.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
trixnpm | < 2.1.15 | 2.1.15 |
Affected products
1Patches
25f8d56dca6184 files changed · +25 −7
src/test/system/pasting_test.js+15 −0 modified@@ -119,6 +119,21 @@ testGroup("Pasting", { template: "editor_empty" }, () => { delete window.unsanitized }) + test("paste data-trix-attachment unsafe html div overload", async () => { + window.unsanitized = [] + const pasteData = { + "text/plain": "x", + "text/html": `\ + <div data-trix-attachment="{"contentType":"text/html5","content":"<div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><div><a><svg><desc><svg><image><a><desc><svg><image></image></svg></desc></a></image><style><a data-trix-caca='</style><img src=x onerror=window.unsanitized.push(1)>'></a></style></svg></desc></svg></a>"}"></div> + `, + } + + await pasteContent(pasteData) + await delay(20) + assert.deepEqual(window.unsanitized, []) + delete window.unsanitized + }) + test("paste data-trix-attachment encoded mathml", async () => { window.unsanitized = [] const pasteData = {
src/trix/models/composition.js+1 −1 modified@@ -127,7 +127,7 @@ export default class Composition extends BasicObject { } insertHTML(html) { - const document = HTMLParser.parse(html).getDocument() + const document = HTMLParser.parse(html, { purifyOptions: { SAFE_FOR_XML: true } }).getDocument() const selectedRange = this.getSelectedRange() this.setDocument(this.document.mergeDocumentAtRange(document, selectedRange))
src/trix/models/html_parser.js+3 −2 modified@@ -66,10 +66,11 @@ export default class HTMLParser extends BasicObject { return parser } - constructor(html, { referenceElement } = {}) { + constructor(html, { referenceElement, purifyOptions } = {}) { super(...arguments) this.html = html this.referenceElement = referenceElement + this.purifyOptions = purifyOptions this.blocks = [] this.blockElements = [] this.processedElements = [] @@ -84,7 +85,7 @@ export default class HTMLParser extends BasicObject { parse() { try { this.createHiddenContainer() - HTMLSanitizer.setHTML(this.containerElement, this.html) + HTMLSanitizer.setHTML(this.containerElement, this.html, { purifyOptions: this.purifyOptions }) const walker = walkTree(this.containerElement, { usingFilter: nodeFilter }) while (walker.nextNode()) { this.processNode(walker.currentNode)
src/trix/models/html_sanitizer.js+6 −4 modified@@ -16,8 +16,8 @@ const DEFAULT_FORBIDDEN_PROTOCOLS = "javascript:".split(" ") const DEFAULT_FORBIDDEN_ELEMENTS = "script iframe form noscript".split(" ") export default class HTMLSanitizer extends BasicObject { - static setHTML(element, html) { - const sanitizedElement = new this(html).sanitize() + static setHTML(element, html, options) { + const sanitizedElement = new this(html, options).sanitize() const sanitizedHtml = sanitizedElement.getHTML ? sanitizedElement.getHTML() : sanitizedElement.outerHTML element.innerHTML = sanitizedHtml } @@ -28,18 +28,20 @@ export default class HTMLSanitizer extends BasicObject { return sanitizer } - constructor(html, { allowedAttributes, forbiddenProtocols, forbiddenElements } = {}) { + constructor(html, { allowedAttributes, forbiddenProtocols, forbiddenElements, purifyOptions } = {}) { super(...arguments) this.allowedAttributes = allowedAttributes || DEFAULT_ALLOWED_ATTRIBUTES this.forbiddenProtocols = forbiddenProtocols || DEFAULT_FORBIDDEN_PROTOCOLS this.forbiddenElements = forbiddenElements || DEFAULT_FORBIDDEN_ELEMENTS + this.purifyOptions = purifyOptions || {} this.body = createBodyElementForHTML(html) } sanitize() { this.sanitizeElements() this.normalizeListElementNesting() - DOMPurify.setConfig(config.dompurify) + const purifyConfig = Object.assign({}, config.dompurify, this.purifyOptions) + DOMPurify.setConfig(purifyConfig) this.body = DOMPurify.sanitize(this.body) return this.body
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4News mentions
0No linked articles in our index yet.