Skip to content

Fix autoFocus for all elements and ReDoS in devtools stack parsing #465

Closed
everettbu wants to merge 5 commits into
mainfrom
main-35693
Closed

Fix autoFocus for all elements and ReDoS in devtools stack parsing #465
everettbu wants to merge 5 commits into
mainfrom
main-35693

Conversation

@everettbu

Copy link
Copy Markdown

Mirror of facebook/react#35693
Original author: d1maash


Summary

This PR addresses two open issues:

1. Support autoFocus as a global HTML attribute

(#35656)

autoFocus is a [global HTML
attribute](https://developer.mozilla.org/en-US/docs/W
eb/HTML/Global_attributes/autofocus) per the spec,
but React only handled it for button, input,
select, and textarea. This change treats
autoFocus as a global attribute so it works on
any focusable element — anchor tags, <div tabIndex={0}>, <dialog>, <details>, etc.

Changes:

  • finalizeInitialChildren: default case now returns
    !!props.autoFocus
  • commitMount: default case now calls .focus()
    when autoFocus is set
  • Added tests for <a autoFocus> and <div tabIndex={0} autoFocus>

2. Fix ReDoS vulnerabilities in stack trace

parsing (#35490)

Two regex patterns in parseStackTrace.js are
vulnerable to catastrophic backtracking:

  • firefoxFrameRegExp: (?:.*".+")?[^@]*
    contains overlapping quantifiers. Replaced with
    [^@"]*(?:"[^"]*"[^@"]*)* using non-overlapping
    character classes.
  • CHROME_STACK_REGEXP: .*(\S+:\d+|\(native\))
    causes O(n²) backtracking. Simplified to /^\s*at /m.

d1maash and others added 3 commits February 5, 2026 02:43
…35656)

autoFocus is a global HTML attribute per the spec, but React only
handled it for button, input, select, and textarea. This change treats
autoFocus as a global attribute so it works on any focusable element
including anchor tags, div with tabIndex, dialog, details, etc.

Changes:
- finalizeInitialChildren: default case now returns !!props.autoFocus
- commitMount: default case now calls .focus() when autoFocus is set
- Added tests for <a autoFocus> and <div tabIndex={0} autoFocus>
Fix two regex patterns in parseStackTrace.js vulnerable to catastrophic
backtracking:

1. firefoxFrameRegExp: replace overlapping .*".+" with non-overlapping
   character classes [^@"]*(?:"[^"]*"[^@"]*)*

2. CHROME_STACK_REGEXP: simplify detection regex from
   /^\s*at .*(\S+:\d+|\(native\))/m to /^\s*at /m to eliminate the
   O(n^2) backtracking between .* and \S+
@greptile-apps

greptile-apps Bot commented Feb 4, 2026

Copy link
Copy Markdown

Greptile Overview

Greptile Summary

This PR does two things:

  • React DOM: expands autoFocus handling beyond form controls by scheduling a mount update for any element with autoFocus and focusing it in commitMount.
  • DevTools shared: adjusts stack parsing regexes in parseStackTrace.js to avoid ReDoS from catastrophic backtracking.

Main merge-blockers are around correctness regressions from the updated heuristics:

  • parseStackTraceFromString now treats any stack containing a line starting with "at " as Chrome/V8, which can route non-Chrome stacks into the Chrome parser and drop frames.
  • autoFocus is now treated as global, but <img autoFocus> still won’t focus because commitMount’s img case returns before the new default focus logic runs.

Confidence Score: 3/5

  • This PR needs fixes before merging due to a couple of concrete behavior regressions in parsing and autoFocus semantics.
  • Regex changes address ReDoS, but the Chrome stack detector is now overly permissive and can drop frames for non-V8/custom stacks. React DOM changes largely work, but will not focus because commitMount returns from the img case before the new default focus logic runs.
  • packages/react-devtools-shared/src/backend/utils/parseStackTrace.js, packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

Important Files Changed

Filename Overview
packages/react-devtools-shared/src/backend/utils/parseStackTrace.js Updated Firefox/Chrome stack-detection regexes to avoid ReDoS; Chrome detector is now overly broad and can mis-route non-Chrome stacks to the Chrome parser, dropping frames.
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js Made autoFocus global by scheduling mount updates for any element and focusing in commitMount default; introduces mismatch where never focuses due to img-specific commitMount return.
packages/react-dom/src/tests/ReactDOM-test.js Added tests for autoFocus on and focusable
; main issue in this file was already noted in prior thread (missing cleanup), no new definite issues beyond that.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines +389 to +418
it('calls focus() on autoFocus anchor elements after they have been mounted to the DOM', async () => {
const originalFocus = HTMLElement.prototype.focus;

try {
let focusedElement;
let anchorFocusedAfterMount = false;

HTMLElement.prototype.focus = function () {
focusedElement = this;
anchorFocusedAfterMount = !!this.parentNode;
};

const container = document.createElement('div');
document.body.appendChild(container);
const root = ReactDOMClient.createRoot(container);
await act(() => {
root.render(
<div>
<h1>Auto-focus Test</h1>
<a href="https://react.dev" autoFocus={true}>
Link
</a>
<p>The above anchor should be focused after mount.</p>
</div>,
);
});

expect(anchorFocusedAfterMount).toBe(true);
expect(focusedElement.tagName).toBe('A');
} finally {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prototype focus override leaks DOM

These two new autoFocus tests append a container to document.body but never remove it (unlike neighboring tests, e.g. preserves focus), so they will leak DOM across the file and can affect later assertions that depend on document.body contents or focus behavior. Wrap them in a try/finally that also document.body.removeChild(container) (and ideally root.unmount() before removal) to keep test isolation deterministic.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-dom/src/__tests__/ReactDOM-test.js
Line: 389:418

Comment:
**Prototype focus override leaks DOM**

These two new autoFocus tests append a container to `document.body` but never remove it (unlike neighboring tests, e.g. `preserves focus`), so they will leak DOM across the file and can affect later assertions that depend on `document.body` contents or focus behavior. Wrap them in a `try/finally` that also `document.body.removeChild(container)` (and ideally `root.unmount()` before removal) to keep test isolation deterministic.

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps

greptile-apps Bot commented Feb 5, 2026

Copy link
Copy Markdown
Additional Comments (1)

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
autoFocus can throw on SVG

In commitMount's default branch, domElement is cast to HTMLElement and .focus() is called when newProps.autoFocus is truthy. For non-HTML host instances (e.g. SVG elements), domElement is an SVGElement, and SVGElement.prototype.focus is not guaranteed to exist in all supported environments. That can turn autoFocus into a runtime TypeError for <svg autoFocus> / <circle autoFocus>.

A safer pattern is to guard on focusability, e.g. if (newProps.autoFocus && typeof (domElement: any).focus === 'function') { ... } (or restrict this behavior to HTML elements only).

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Line: 933:935

Comment:
**autoFocus can throw on SVG**

In `commitMount`'s default branch, `domElement` is cast to `HTMLElement` and `.focus()` is called when `newProps.autoFocus` is truthy. For non-HTML host instances (e.g. SVG elements), `domElement` is an `SVGElement`, and `SVGElement.prototype.focus` is not guaranteed to exist in all supported environments. That can turn `autoFocus` into a runtime `TypeError` for `<svg autoFocus>` / `<circle autoFocus>`.

A safer pattern is to guard on focusability, e.g. `if (newProps.autoFocus && typeof (domElement: any).focus === 'function') { ... }` (or restrict this behavior to HTML elements only).

How can I resolve this? If you propose a fix, please make it concise.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps

greptile-apps Bot commented Feb 5, 2026

Copy link
Copy Markdown
Additional Comments (2)

packages/react-devtools-shared/src/backend/utils/parseStackTrace.js
Over-broad Chrome stack detection

CHROME_STACK_REGEXP was loosened to /^\s*at /m (parseStackTraceFromString at packages/react-devtools-shared/src/backend/utils/parseStackTrace.js:97-105). This will classify any stack containing a line starting with at as “Chrome”, even if it isn’t in the format chromeFrameRegExp can parse. In that case, parseStackTraceFromChromeStack will skip those frames entirely (because chromeFrameRegExp.exec(...) returns null), and the function will return fewer/empty frames where the previous Firefox fallback would have parsed something. This is a concrete regression for non-V8/custom stacks that include "at " lines but aren’t Chrome-formatted.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-devtools-shared/src/backend/utils/parseStackTrace.js
Line: 97:103

Comment:
**Over-broad Chrome stack detection**

`CHROME_STACK_REGEXP` was loosened to `/^\s*at /m` (parseStackTraceFromString at `packages/react-devtools-shared/src/backend/utils/parseStackTrace.js:97-105`). This will classify *any* stack containing a line starting with `at ` as “Chrome”, even if it isn’t in the format `chromeFrameRegExp` can parse. In that case, `parseStackTraceFromChromeStack` will skip those frames entirely (because `chromeFrameRegExp.exec(...)` returns null), and the function will return fewer/empty frames where the previous Firefox fallback would have parsed something. This is a concrete regression for non-V8/custom stacks that include `"at "` lines but aren’t Chrome-formatted.

How can I resolve this? If you propose a fix, please make it concise.

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
autoFocus on no-op

finalizeInitialChildren now returns !!props.autoFocus for the default case (packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js:648-666), but commitMount’s new default handler is unreachable for type === 'img' (commitMount hits case 'img' and returns at :862-894). As a result, <img autoFocus /> schedules an Update effect (because finalizeInitialChildren returns true for all <img>), but will never call .focus() in commitMount, so autoFocus silently doesn’t work for images despite now being treated as global.

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
Line: 648:666

Comment:
**autoFocus on <img> no-op**

`finalizeInitialChildren` now returns `!!props.autoFocus` for the `default` case (`packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js:648-666`), but `commitMount`’s new default handler is unreachable for `type === 'img'` (`commitMount` hits `case 'img'` and returns at `:862-894`). As a result, `<img autoFocus />` schedules an Update effect (because `finalizeInitialChildren` returns `true` for all `<img>`), but will never call `.focus()` in `commitMount`, so `autoFocus` silently doesn’t work for images despite now being treated as global.

How can I resolve this? If you propose a fix, please make it concise.

@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown

This pull request has been automatically marked as stale. If this pull request is still relevant, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize reviewing it yet. Your contribution is very much appreciated.

@github-actions github-actions Bot added the Resolution: Stale Automatically closed due to inactivity label May 6, 2026
@github-actions

Copy link
Copy Markdown

Closing this pull request after a prolonged period of inactivity. If this issue is still present in the latest release, please ask for this pull request to be reopened. Thank you!

@github-actions github-actions Bot closed this May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Resolution: Stale Automatically closed due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants