The Hidden Attack Surface of React Flight
React Server Components do not send HTML or JSON to the browser. They use a custom streaming protocol called Flight. This line-delimited format contains module pointers and reference resolution rules that the React runtime reassembles into a live component tree. While powerful, this mechanism introduces dangerous deserialization sinks.
Understanding the React2Shell Vulnerability
The severity of this attack surface was proven by the React2Shell vulnerability, a CVSS 10.0 remote code execution flaw. The vulnerability resided in the property traversal logic of the Flight deserializer. An attacker could supply a crafted reference, which walked up the prototype chain and executed arbitrary code.
This was not a simple parsing bug. It was a symptom of a protocol that reconstructs executable references, lazy-loaded components, and async state from a stream of text. The parser's control flow was driven entirely by the content of the stream, giving attackers immense power if they could influence it.
The Framework Patch and Its Limits
The React team responded with a targeted patch, caching the genuine hasOwnProperty method at module load time to block prototype chain traversal. This fix shipped in React 19.0.1, 19.1.2, and 19.2.1. While correct, the patch treats the symptom. The property traversal model remains intact, leaving the door open for future edge-case vulnerabilities.
Practical Defenses for Developers
Relying solely on the framework is insufficient. Developers must implement strict schema validation at the very top of every Server Action, before any business logic or logging occurs. Libraries like Zod are essential for validating types, shapes, and numeric bounds.
Additionally, importing server-only at the top of files containing sensitive logic prevents Client Components from accidentally importing server-side code. For high-value operations, layer custom CSRF protection on top of framework defaults, and never allow null in allowedOrigins configurations.
The React Flight protocol solves a genuinely hard problem. However, trusting the structure of a network-facing stream is a historical pattern that repeatedly leads to security failures. Defense in depth is not optional; it is mandatory.