{"id":"CVE-2026-75838","aliases":["GHSA-55q2-fjhq-7xh7"],"url":"https://o3.security/vulnerability/CVE-2026-75838","summary":"DOMPurify before 3.4.13 Cross-Site Scripting via IN_PLACE hook","details":"### Summary\n\nDuring `IN_PLACE` sanitization, a hook that removes an element can leave that element's detached descendants executable. A descendant image can retain its attacker-provided `onload` handler and fire after `sanitize()` returns, even though the returned root is clean and the image remains disconnected from the document.\n\n### Details\n\nIn DOMPurify 3.4.12, `_sanitizeElements()` in `src/purify.ts:1862-1904` runs the `beforeSanitizeElements` or `uponSanitizeElement` hook and returns immediately when the hook detached the current node. The return does not call `_neutralizeSubtree(currentNode)`.\n\nThe detached subtree is not added to `DOMPurify.removed`, so the post-walk `IN_PLACE` neutralization cannot reach it. If the browser queued a resource event while the application constructed the detached dirty root, a descendant can therefore retain its handler and execute after sanitization.\n\nThe hook only rejects the containing element and does not add or approve the event handler. DOMPurify's ordinary removal path de-arms the same queued event; only the hook-detachment early return skips the existing subtree neutralization.\n\n### PoC\n\nLoad the published `dompurify@3.4.12` `dist/purify.js` before this script in Chromium:\n\n```html\n<div id=\"result\">not fired</div>\n<script>\nconst root = document.createElement('div');\nroot.innerHTML = `\n  <footer>\n    <img src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\"\n         onload=\"result.textContent = 'XSS after sanitize'\">\n  </footer>\n  <div>safe</div>`;\n\nDOMPurify.setConfig({\n  ALLOWED_TAGS: ['div', '#text', 'footer'],\n  IN_PLACE: true\n});\nDOMPurify.addHook('uponSanitizeElement', node => {\n  if (node.tagName === 'FOOTER') node.remove();\n});\n\nDOMPurify.sanitize(root);\ndocument.body.append(root);\n</script>\n```\n\n`sanitize()` returns with no handler execution and the returned root contains only the safe `div`. After the event loop advances, the original image remains disconnected but its retained `onload` changes the page to `XSS after sanitize`.\n\nAs the claim-matched control, use the same detached input with `ALLOWED_TAGS: ['div', '#text']` and no hook. DOMPurify's ordinary removal path removes the original image's handler, the returned root is still `<div>safe</div>`, and the marker does not fire.\n\n### Impact\n\nIn an application that uses `IN_PLACE` with the documented element-removal hook pattern, an attacker who can supply HTML can execute JavaScript in the integrating application's origin after the application sanitizes and renders that content.\n\nThe required non-default configuration is `IN_PLACE` plus a hook that removes a containing element. The hook does not add or approve the event handler, and the dirty root never needs to be connected before sanitization.\n\n### Suggested fix\n\nReuse the existing `_neutralizeSubtree(currentNode)` helper before returning from both hook-detachment branches in `_sanitizeElements()`. Add regressions for `beforeSanitizeElements` and `uponSanitizeElement` that retain a reference to a descendant resource element and verify that its event handler is removed after the hook detaches its ancestor.","published":"2026-08-18T11:19:47.885Z","modified":"2026-10-28T02:30:21.793515794Z","cvss":null,"epss":{"score":0.003,"percentile":0.22183,"asOf":"2026-08-30"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"dompurify","fixedVersion":"3.4.13"}],"fix":{"url":"https://github.com/cure53/DOMPurify/pull/1557","label":"cure53/DOMPurify#1557"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/75xxx/CVE-2026-75838.json"},{"type":"ADVISORY","url":"https://github.com/cure53/DOMPurify/security/advisories/GHSA-55q2-fjhq-7xh7"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-75838"},{"type":"ADVISORY","url":"https://www.vulncheck.com/advisories/dompurify-before-cross-site-scripting-via-in-place-hook"},{"type":"WEB","url":"https://github.com/cure53/DOMPurify/pull/1557"},{"type":"WEB","url":"https://github.com/cure53/DOMPurify/commit/3067f7746769"},{"type":"PACKAGE","url":"https://github.com/cure53/DOMPurify"},{"type":"WEB","url":"https://github.com/cure53/DOMPurify/releases/tag/3.4.13"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:30:21.793515794Z"}}