CVE-2026-47423 - DOMPurify selectedcontent re-clone XSS

DOMPurify 3.4.4 allows selectedcontent by default. In affected browsers, that creates a re-clone sequence where attacker-controlled markup can become unsafe again after DOMPurify has already walked the subtree. Applications that sanitize untrusted HTML with the string-input path and then inject the returned string into the page can still end up executing script.

This matters anywhere a product treats DOMPurify as the trust boundary for rich text: comments, knowledge bases, support messages, markdown previews, agent output rendering, CMS blocks, email templates, or admin content viewers. The patch is small, but the review should also verify that sanitized output is not being fed back into unsafe sinks or transformed again afterward.

When to use it

Use this recipe when a web application depends on DOMPurify as the trust boundary for rich text, markdown output, CMS content, support messages, email previews, model-generated HTML, or other attacker-controlled markup. It is designed for source-code remediation, sanitizer-to-sink review, frontend regression testing, and evidence that sanitized content remains inert after the application’s real render path.

Inputs

  • DOMPurify version, package manager and lockfile state, rich-text rendering surfaces, sanitizer wrapper code, Trusted Types policy, and deployment bundle inventory.
  • Source paths that call DOMPurify.sanitize, use innerHTML or dangerouslySetInnerHTML, transform markdown/HTML, render previews, or show privileged review/admin content.
  • Regression fixtures for benign rich text, selectedcontent-like markup, post-sanitization transforms, framework sink wrappers, and rejected executable behavior.
  • Boundary evidence: who can author HTML, who views it, tenant/admin crossing, CSP posture, generated bundles, SBOMs, dependency reports, and rollout owner.

Affected versions

Package Vulnerable versions Fixed versions
dompurify 3.4.4 3.4.5+

Indicator-of-exposure

  • The repository resolves dompurify 3.4.4.
  • Attacker-controlled or mixed-trust HTML is sanitized by DOMPurify and then inserted into the DOM using innerHTML, dangerouslySetInnerHTML, template rendering, or equivalent string-to-DOM sinks.
  • The app supports rich text, imported HTML, markdown-to-HTML pipelines, WYSIWYG editors, email or support rendering, documentation rendering, or model-generated HTML.
  • Sanitized content can be viewed by privileged users whose sessions, tokens, or cross-tenant data matter.

Quick checks:

rg -n "dompurify|DOMPurify|sanitize\\(|dangerouslySetInnerHTML|innerHTML|selectedcontent|trustedTypes|createHTML|markdown|rich text|WYSIWYG" .
npm ls dompurify
pnpm why dompurify
yarn why dompurify

Windows:

rg -n "dompurify|DOMPurify|sanitize\(|dangerouslySetInnerHTML|innerHTML|selectedcontent|trustedTypes|createHTML|markdown|rich text|WYSIWYG" .
npm ls dompurify
pnpm why dompurify
yarn why dompurify

Remediation strategy

  • Upgrade dompurify to 3.4.5+.
  • Regenerate lockfiles, SBOMs, bundles, and dependency reports.
  • Add regression tests with inert attacker-controlled HTML fixtures that assert sanitized output stays inert after the app’s full render path, not only after the first sanitize call.
  • Review HTML sinks and post-sanitization transforms. Avoid taking a sanitized string and then mutating or reinterpreting it in ways that bypass the sanitizer boundary.
  • If the upgrade cannot deploy immediately, disable high-risk rich-text rendering paths or render sanitized content as text in the highest-trust views.

The prompt

Model context: this prompt was generated by GPT 5.5 Extra High reasoning.

You are remediating CVE-2026-47423 / GHSA-87xg-pxx2-7hvx, a DOMPurify
sanitization bypass through `selectedcontent` re-cloning. Produce exactly one
output:

- A reviewer-ready PR/change request that upgrades DOMPurify, verifies safe
  rendering of attacker-controlled HTML, adds regression fixtures, refreshes
  generated artifacts, and documents operator actions, or
- TRIAGE.md if this repository does not control an affected DOMPurify-based
  rich-text rendering path.

## Rules

- Scope only CVE-2026-47423 / GHSA-87xg-pxx2-7hvx and directly related
  sanitizer-to-sink rendering paths.
- Do not execute live XSS payloads in a browser session with real cookies,
  tokens, customer data, or privileged accounts.
- Treat sessions, JWTs, CSRF tokens, tenant data, support content, and model
  output as sensitive.
- Do not remove sanitization entirely to get tests passing.
- Do not auto-merge.

## Steps

1. Inventory every `dompurify` dependency, lockfile, bundle, workspace, image,
   SBOM, and generated dependency report controlled by this repository.
2. Determine whether `dompurify 3.4.4` is resolved and where it sanitizes
   attacker-controlled or mixed-trust HTML.
3. Trace the full rendering path after sanitization: template rendering,
   markdown transforms, React/Vue wrappers, `innerHTML`,
   `dangerouslySetInnerHTML`, Trusted Types policies, or editor preview code.
4. If the repository does not control an affected rich-text rendering path,
   stop with `TRIAGE.md` listing checked files and resolved versions.
5. Upgrade `dompurify` to `3.4.5+` and refresh lockfiles, generated dependency
   output, bundles, images, and SBOMs.
6. Add safe regression fixtures that prove attacker-controlled HTML remains
   inert after the application's real sanitize-and-render path:
   - sanitize input using repository code paths;
   - assert the rendered result omits executable behavior;
   - cover `innerHTML` or framework sink wrappers used here;
   - avoid live payload execution and real-session browsers.
7. Review and fix adjacent sink issues:
   - avoid post-sanitization DOM mutation that can reintroduce unsafe nodes;
   - avoid serializing and reparsing sanitized HTML in a less-constrained path;
   - prefer explicit allow-lists for supported rich-text features.
8. Add a PR body section named `CVE-2026-47423 operator actions` that states:
   - DOMPurify versions before and after;
   - which user-controlled HTML surfaces were reviewed;
   - which sink wrappers or rendering helpers were tested;
   - whether privileged viewers or tenant-crossing surfaces existed;
   - whether temporary rendering restrictions remain until full rollout.
9. Run available validation: package install, unit tests, sanitizer fixtures,
   frontend tests, build, lint/typecheck, dependency/security scans, and SBOM
   refresh.
10. Use PR title:
    `fix(sec): remediate CVE-2026-47423 in DOMPurify usage`.

## Stop conditions

- No affected `dompurify 3.4.4` resolution exists in a controlled rich-text
  surface.
- The repository only references DOMPurify in docs, examples, or non-executed
  code.
- Meaningful verification would require live XSS execution in a privileged
  environment.
- Validation fails for unrelated pre-existing reasons; document those failures
  instead of broadening scope.

Output contract

  • A reviewer-ready PR or change request that upgrades DOMPurify, refreshes dependency artifacts, verifies the real sanitize-to-render path, and documents rich-text surface review.
  • Or a TRIAGE.md file that lists inspected package files, rendered bundles, sanitizer wrappers, rich-text surfaces, owner, observed version, required fix, and residual risk.
  • The output must include exact validation commands and must not execute live XSS payloads with real sessions, expose customer-authored content, or remove sanitization to make tests pass.

Verification - what the reviewer looks for

  • No controlled lockfile or bundle resolves dompurify 3.4.4.
  • Tests cover the real sanitize-to-render path, not only a direct call to DOMPurify.sanitize.
  • High-trust viewer surfaces and string-to-DOM sinks were explicitly reviewed.
  • Generated artifacts and dependency reports reflect the fixed version.

Watch for

  • Updating package.json while a workspace lockfile or vendor bundle still ships 3.4.4.
  • Sanitizing correctly but later feeding the result into a second unsafe HTML transform.
  • Fixing public comment views while admin-only review panes still use stale DOMPurify builds.
  • Tests that assert only string equality without exercising the app’s actual rendering helper.

References