Skip to main content

Boteraser | Website and Server Security Solutions

🛡️ CVE-2026-49458 — dompurify

🟡 CVSS 6.1 — Medium ⚠️ Exploit Public CWE-501 OSV
6.1
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

DOMPurify: Cross-realm IN_PLACE sanitization leaves executable markup intact via realm-bound instanceof checks

# Cross-realm IN_PLACE sanitization leaves executable markup intact via realm-bound instanceof checks

CWE: CWE-79 (XSS — Improper Neutralization of Input During Web Page Generation) via CWE-693 (Protection Mechanism Failure — realm-bound instanceof checks fail-open on foreign-realm DOM nodes) and CWE-501 (Trust Boundary Violation — foreign-realm nodes accepted for sanitization but later checks are bound to the parent realm)

Summary

DOMPurify.sanitize(node, { IN_PLACE: true }) accepts a DOM node from any same-origin realm (e.g. a node owned by an application-created iframe document), but several follow-on security checks compare the node against constructors from the parent realm. Because constructors are per-realm, instanceof HTMLFormElement, instanceof NamedNodeMap, instanceof DocumentFragment, and instanceof Element all return false for nodes belonging to the iframe's realm. The library therefore proceeds as if the foreign-realm form is not clobberable, the foreign-realm <template>'s .content is not a document fragment, and the foreign-realm attached shadow root is not a document fragment — silently skipping the clobber/template-content/shadow-DOM sanitization branches that those checks gate. Attacker-controlled markup survives in form attributes, template content, and attached shadow roots, and executes when the application later inserts or activates the sanitized node.

Affected

  • DOMPurify ≤ 3.4.5, including main at 89da34e03ec17868e561f87f3747a9371b61a9e7
  • Any caller that constructs or parses untrusted DOM in a same-origin iframe (or any other same-origin realm — popup window, opened tab, programmatically-created <iframe srcdoc>) and then calls DOMPurify.sanitize(foreignNode, { IN_PLACE: true }) against a sanitizer instance bound to a different realm

Not affected:

  • String-input DOMPurify.sanitize(dirtyString) — the library calls its own parser inside _initDocument, the resulting nodes belong to the sanitizer's own realm, and the instanceof checks resolve as expected
  • IN_PLACE calls where the input node was created in the same realm as the DOMPurify instance

Vulnerability details

The unifying defect is that _isClobbered, _sanitizeShadowDOM's template-content recursion, and _sanitizeAttachedShadowRoots all use realm-bound instanceof checks against the parent-realm constructors. Each branch fails-open for foreign-realm objects.

[A] — _isClobbered gates on element instanceof HTMLFormElement

src/purify.ts:1120-1140:

```ts

const _isClobbered = function (element: Element): boolean {

return (

element instanceof HTMLFormElement && // [A] realm-bound — false for any

// iframe-realm <form> element

(typeof element.nodeName !== 'string' ||

typeof element.textContent !== 'string' ||

typeof element.removeChild !== 'function' ||

!(element.attributes instanceof NamedNodeMap) || // [A'] also realm-bound

typeof element.removeAttribute !== 'function' ||

typeof element.setAttribute !== 'function' ||

typeof element.namespaceURI !== 'string' ||

typeof element.insertBefore !== 'function' ||

typeof element.hasChildNodes !== 'function' ||

!(element.childNodes && typeof element.childNodes.length === 'number'))

);

};

```

A foreign-realm <form> is an instance of the foreign realm's HTMLFormElement, not the parent realm's. The leading instanceof short-circuits to false, so _isClobbered returns false regardless of the named-property clobbering present on the form. The follow-on _sanitizeAttributes then iterates currentNode.attributes — which itself can be a clobbered value (a foreign-realm <input> whose name="attributes" shadows the form's real NamedNodeMap). The attribute walk traverses the wrong collection and never reaches the actual onmouseover / onclick / action=javascript: attributes on the form root.

[B] — _sanitizeShadowDOM gates template recursion on content instanceof DocumentFragment

src/purify.ts:1660-1662:

```ts

while ((shadowNode = shadowIterator.nextNode())) {

...

_sanitizeElements(shadowNode);

_sanitizeAttributes(shadowNode);

/* Deep shadow DOM detected */

if (shadowNode.content instanceof DocumentFragment) { // [B] realm-bound

_sanitizeShadowDOM(shadowNode.content);

}

}

```

The same check exists in the main iterator at :1861-1862:

```ts

if (currentNode.content instanceof DocumentFragment) { // [B'] realm-bound

_sanitizeShadowDOM(currentNode.content);

}

```

For a <template> element constructed in a foreign realm, template.content is a DocumentFragment from that realm — not from the parent realm. Both checks miss it, and the template's contents (which carry attacker-controlled <img src=x onerror=...> etc.) are never walked. The sani

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. A user must be tricked into taking some action. The scope is changed, meaning a successful attack can affect components beyond the vulnerable one. Rated impact: confidentiality low, integrity low, availability none.

CVSS metrics in full

The score comes from this vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N

  • Attack vector: Network — reachable from anywhere that can route to the service.
  • Attack complexity: Low — the attack works reliably, with no preparation.
  • Privileges required: None — an unauthenticated stranger can try it.
  • User interaction: Required — someone has to click, open or visit something.
  • Scope: Changed — a successful attack reaches components beyond the vulnerable one.
  • Confidentiality impact: Low — limited, and the attacker does not choose what is affected.
  • Integrity impact: Low — limited, and the attacker does not choose what is affected.
  • Availability impact: None.

Weakness class

CVE-2026-49458 is classified as CWE-501: Trust Boundary Violation. The product mixes trusted and untrusted data in the same data structure or structured message.

Affected software

CVE-2026-49458 is recorded against 1 package.

  • dompurify

Timeline and source

Published on 15 June 2026 and last revised on 21 July 2026. A public exploit is known to exist, which raises the urgency of patching considerably. A vendor advisory or fix has been published. Record sourced from OSV.

References

github.com (Web)
github.com (Package)

Other advisories for this package

dompurify has other advisories on record. If you are patching this one, these are worth checking on the same host:

Same weakness in other software

These advisories are the same class of weakness (CWE-501: Trust Boundary Violation) in other software:

Details

Severity MEDIUM
CVSS Score 6.1
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N
CWE CWE-501
Public Exploit ⚠️ Yes
Source OSV
Published 2026-06-15
Updated 2026-08-20
Modified 2026-07-21

Affected Packages

Software From version Fixed in
dompurify

Exploit Protection

Are you running dompurify?

CVE-2026-49458 carries CVSS 6.1 Medium rating and a public exploit already exists. BotEraser checks your installation against this and other known CVE records, and blocks IPs associated with exploit activity.

Check My Site For CVE-2026-49458 →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.

Browse related advisories

All advisoriesCVECVE 2026