# Risk map — facebook/react

> Canonical page: https://codevetter.com/unpack/react/risk-map

# Risk map — facebook/react

Scanned commit 59aff3e18cb5b3a336c280bbfa57ec37999511b9 (2026-09-18). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

The genuinely security-sensitive surface is small but sharp: Flight's Server Actions turn attacker-controlled FormData into a module lookup and function invocation, the taint registry is the only guard against serialising server secrets to the client and it is experimental-only, and the DevTools extension bridges page-world `postMessage` to privileged `chrome.runtime` ports while also running string-`eval` payloads through `chrome.devtools.inspectedWindow.eval`. Operationally the biggest fragility is the fork/shim system — `ReactFiberConfig` and `ReactFeatureFlags` resolve differently under Rollup, Jest and Flow, so a change that passes locally can break a channel nobody ran. `ReactFiberWorkLoop.js` at 5,683 lines with ~60 exported functions and module-level mutable state is the highest blast-radius file in the repo, and the Rust port is a live second implementation of the compiler that must stay snapshot-identical to the TypeScript one.
- `loadServerReference` resolves a client-supplied string id straight into `resolveServerReference(bundlerConfig, id)` → `preloadModule` → `requireModule`; the only validation is `typeof id !== 'string'` (which silently returns `null as any`), so the security boundary rests entirely on the bundler-generated `serverManifest`. — [packages/react-server/src/ReactFlightActionServer.js#L46-L75](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-server/src/ReactFlightActionServer.js#L46-L75)
- `decodeAction` distinguishes action fields purely by string prefix (`$ACTION_REF_`, `$ACTION_ID_`, `$ACTION_`) on attacker-controlled FormData keys, and a mismatch falls through to `throw new Error('Cannot handle action key. This is a bug in React.')` — a user-reachable throw framed as an internal invariant. — [packages/react-server/src/ReactFlightActionServer.js#L107-L160](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-server/src/ReactFlightActionServer.js#L107-L160)
- The taint protection against leaking secrets to the client is gated off by default outside experimental builds (`export const enableTaint = __EXPERIMENTAL__`), and `taintUniqueValue` early-returns when the flag is false — stable consumers get no taint enforcement. — [packages/shared/ReactFeatureFlags.js#L94](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/shared/ReactFeatureFlags.js#L94) [packages/react/src/ReactTaint.js#L56-L62](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react/src/ReactTaint.js#L56-L62)
- `TaintRegistryValues` is a plain `Map<string|bigint, TaintEntry>` with manual refcounting in `cleanup()`, relying on `FinalizationRegistry`; the code explicitly notes that if `FinalizationRegistry` is absent it 'assume[s] that objects live forever' — a correctness cliff on older runtimes. — [packages/react/src/ReactTaintRegistry.js#L17-L28](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react/src/ReactTaintRegistry.js#L17-L28) [packages/react/src/ReactTaint.js#L33-L54](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react/src/ReactTaint.js#L33-L54)
- The DevTools extension executes code as strings in the inspected page: `evalScripts.js` defines a `.code` variant for each of `checkIfReactPresentInInspectedWindow`, `reload`, `viewElementSource`, `viewAttributeSource` and the selection bridges, intended for `chrome.devtools.inspectedWindow.eval()`. — [packages/react-devtools-extensions/src/evalScripts.js#L11-L40](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-extensions/src/evalScripts.js#L11-L40)
- The extension's content scripts relay untrusted page-world messages into privileged extension ports: `prepareInjection.js` listens on `window.addEventListener('message', ...)` and `proxy.js` forwards across `chrome.runtime.connect(...)` / `chrome.runtime.onMessage` and back out via `window.postMessage` — a classic content-script trust boundary. — [packages/react-devtools-extensions/src/contentScripts/prepareInjection.js#L11](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-extensions/src/contentScripts/prepareInjection.js#L11) [packages/react-devtools-extensions/src/contentScripts/proxy.js#L123-L255](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-extensions/src/contentScripts/proxy.js#L123-L255) [packages/react-devtools-extensions/src/contentScripts/backendManager.js#L188-L258](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-extensions/src/contentScripts/backendManager.js#L188-L258)
- The DevTools bridge lets the frontend write arbitrary values into a live app — `overrideProps`, `overrideState`, `overrideHookState`, `overrideContext` are first-class bridge events — so anything that can speak the bridge protocol can mutate application state. — [packages/react-devtools-shared/src/bridge.js#L485-L525](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/bridge.js#L485-L525) [packages/react-devtools-shared/src/bridge.js#L207-L230](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/bridge.js#L207-L230)
- `__REACT_DEVTOOLS_GLOBAL_HOOK__` is installed as a page global and documented in-source as 'a de facto public API', giving any script on the page a handle on every mounted renderer. — [packages/react-devtools-shared/src/hook.js#L1-L15](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/hook.js#L1-L15) [packages/react-devtools-shared/src/hook.js#L59-L72](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-devtools-shared/src/hook.js#L59-L72)
- `ReactFiberWorkLoop.js` is 5,683 lines with roughly 60 exports and heavy module-level mutable state (`workInProgressRoot`, `workInProgressSuspendedReason`, `workInProgressRootExitStatus`, `executionContext`), making it the single highest blast-radius file — almost every reconciler behaviour change lands here. — [packages/react-reconciler/src/ReactFiberWorkLoop.js#L538-L5683](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberWorkLoop.js#L538-L5683)
- The fork system is fragile by design: `scripts/rollup/forks.js` substitutes 13 module paths (shared internals, feature flags, fiber config, stream/fizz/flight configs, `EventListener`, `useSyncExternalStore`) and Jest substitutes them differently — so a module can resolve three ways (Rollup, Jest, Flow) and only CI exercises all of them. — [scripts/rollup/forks.js#L55-L470](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/scripts/rollup/forks.js#L55-L470) [scripts/jest/setupHostConfigs.js#L137-L220](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/scripts/jest/setupHostConfigs.js#L137-L220)
- There are 13 `ReactFeatureFlags` forks (`www`, `www-dynamic`, `native-fb`, `native-oss`, `test-renderer`, `eslint-plugin.www`, `readonly`, …); a flag added to the base file without updating every fork is a known failure mode, which is exactly why CI has a dedicated `flags` job and a `/flags` skill. — [packages/shared/forks](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/shared/forks) [.github/workflows/runtime_build_and_test.yml#L192-L215](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/.github/workflows/runtime_build_and_test.yml#L192-L215) [.claude/skills/feature-flags/SKILL.md](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/.claude/skills/feature-flags/SKILL.md)
- `scripts/release/ci-npmrc` writes `//registry.npmjs.org/:_authToken=${NPM_TOKEN}` and three compiler prerelease workflows inject `NPM_TOKEN` from repository secrets — any workflow change that widens trigger conditions on those files widens publish access. — [scripts/release/ci-npmrc](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/scripts/release/ci-npmrc) [.github/workflows/compiler_prereleases.yml#L29-L36](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/.github/workflows/compiler_prereleases.yml#L29-L36) [.github/workflows/compiler_prereleases_manual.yml#L24-L41](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/.github/workflows/compiler_prereleases_manual.yml#L24-L41) [.github/workflows/compiler_prereleases_nightly.yml#L8-L24](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/.github/workflows/compiler_prereleases_nightly.yml#L8-L24)
- The MCP server's runtime-perf tool launches a real browser (`puppeteer.launch()`) and runs `page.evaluate` on supplied component code — that is code execution on the developer's machine driven by MCP tool input. — [compiler/packages/react-mcp-server/src/tools/runtimePerf.ts#L2-L195](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/compiler/packages/react-mcp-server/src/tools/runtimePerf.ts#L2-L195)
- An Algolia app id and search API key are hard-coded in `compiler/packages/react-mcp-server/src/utils/algolia.ts` (with a source comment pointing at react.dev's public config) — low severity since they are public search-only credentials, but they are real credentials living in the tree. — [compiler/packages/react-mcp-server/src/utils/algolia.ts#L12-L22](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/compiler/packages/react-mcp-server/src/utils/algolia.ts#L12-L22)
- The Rust port is a duplicated implementation of a complex pipeline: `compiler/crates/react_compiler/src/entrypoint/pipeline.rs` re-implements the same ~40 passes as `Pipeline.ts`, and `react_compiler/src/lib.rs` re-exports crates 'for backwards compatibility' — divergence between the two is caught only by `yarn snap --rust` in CI. — [compiler/crates/react_compiler/src/entrypoint/pipeline.rs#L37-L640](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/compiler/crates/react_compiler/src/entrypoint/pipeline.rs#L37-L640) [compiler/crates/react_compiler/src/lib.rs#L1-L11](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/compiler/crates/react_compiler/src/lib.rs#L1-L11) [.github/workflows/compiler_rust.yml#L62-L68](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/.github/workflows/compiler_rust.yml#L62-L68)
- The napi bridge converts Rust panics into `napi::Error` strings rather than structured diagnostics, and relies on a hand-tuned 64 MB thread stack to avoid overflow on deep ASTs — both are heuristic mitigations rather than guarantees. — [compiler/packages/babel-plugin-react-compiler-rust/native/src/lib.rs#L26-L48](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/compiler/packages/babel-plugin-react-compiler-rust/native/src/lib.rs#L26-L48)
- Legacy code is still resident and flag-guarded rather than removed: `disableLegacyMode`, `LegacyRoot`, `flushSyncWorkOnLegacyRootsOnly`, `isAlreadyFailedLegacyErrorBoundary`, `enableLegacyFBSupport` and `enableLegacyCache` all remain live branches in the work loop and flag file. — [packages/react-reconciler/src/ReactFiberWorkLoop.js#L1092-L1110](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberWorkLoop.js#L1092-L1110) [packages/react-reconciler/src/ReactFiberWorkLoop.js#L4877-L4890](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/react-reconciler/src/ReactFiberWorkLoop.js#L4877-L4890) [packages/shared/ReactFeatureFlags.js#L62-L78](https://github.com/facebook/react/blob/59aff3e18cb5b3a336c280bbfa57ec37999511b9/packages/shared/ReactFeatureFlags.js#L62-L78)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
