XSS (DOM-based HTML injection)

HIGH
facebook/react
Commit: 99e86060ac35
Affected: 19.2.0 - 19.2.3
2026-06-23 20:20 UTC

Description

The fix mitigates a potential XSS in the standalone DevTools error rendering by replacing innerHTML-based HTML construction with DOM nodes and textContent. Previously, error messages were interpolated directly into an HTML string and assigned to innerHTML, which could allow HTML/JS injection if the error message came from an untrusted source. The patch now builds the error box using DOM elements and sets textContent for header and content, ensuring any embedded HTML is treated as text.

Proof of Concept

PoC steps to illustrate the risk prior to the fix: 1) In a running DevTools standalone environment, trigger an error path that calls onError with an attacker-controlled message, for example: onError({ code: 'UNKNOWN', message: '<img src=x onerror="alert(1)">Injected' }); 2) Prior to the fix, the onError code would construct a string like: `<div class="box"><div class="box-header">Unknown error</div><div class="box-content">${message}</div></div>` and assign it to innerHTML. The attacker-controlled content would be parsed as HTML/JS, causing potential XSS (e.g., the image tag executing onerror). 3) This would result in an injected script running in the page rendering the DevTools UI. 4) With the fix in place, the same message is placed into the DOM via textContent (not HTML), so any HTML tags are treated as text and do not execute, preventing the XSS.

Commit Details

Author: Minh Vu

Date: 2026-06-23 10:28 UTC

Message:

Avoid HTML injection in standalone errors (#36839) ## Summary - Render standalone DevTools server errors with DOM nodes instead of HTML strings. - Preserve the existing error box classes and copy while inserting error text with `textContent`. ## How did you test this? - `corepack yarn prettier` - `corepack yarn lint packages/react-devtools-core/src/standalone.js`

Triage Assessment

Vulnerability Type: XSS

Confidence: HIGH

Reasoning:

The commit replaces string-based HTML construction with DOM nodes and textContent for error messages, preventing HTML injection in error output. This directly mitigates potential XSS/HTML injection vulnerabilities when rendering error text.

Verification Assessment

Vulnerability Type: XSS (DOM-based HTML injection)

Confidence: HIGH

Affected Versions: 19.2.0 - 19.2.3

Code Diff

diff --git a/packages/react-devtools-core/src/standalone.js b/packages/react-devtools-core/src/standalone.js index 3e2538d47750..32b9566bf626 100644 --- a/packages/react-devtools-core/src/standalone.js +++ b/packages/react-devtools-core/src/standalone.js @@ -176,31 +176,34 @@ function onDisconnected() { disconnectedCallback(); } +function showErrorMessage(headerText: string, contentText: string) { + const box = document.createElement('div'); + box.className = 'box'; + + const header = document.createElement('div'); + header.className = 'box-header'; + header.textContent = headerText; + box.appendChild(header); + + const content = document.createElement('div'); + content.className = 'box-content'; + content.textContent = contentText; + box.appendChild(content); + + node.textContent = ''; + node.appendChild(box); +} + function onError({code, message}: $FlowFixMe) { safeUnmount(); if (code === 'EADDRINUSE') { - node.innerHTML = ` - <div class="box"> - <div class="box-header"> - Another instance of DevTools is running. - </div> - <div class="box-content"> - Only one copy of DevTools can be used at a time. - </div> - </div> - `; + showErrorMessage( + 'Another instance of DevTools is running.', + 'Only one copy of DevTools can be used at a time.', + ); } else { - node.innerHTML = ` - <div class="box"> - <div class="box-header"> - Unknown error - </div> - <div class="box-content"> - ${message} - </div> - </div> - `; + showErrorMessage('Unknown error', String(message)); } }
← Back to Alerts View on GitHub →