The real reason React got hacked again, brought to you by Vercel
To be fair, the past 20 years of JavaScript fatigue is purely an artificial problem, and PHP or Ruby developers who people used to mock in the past were the ones actually laughing on the side lines. JavaScript fatigue did not appear naturally. It came from the ecosystem choosing complexity over clarity. The recent React Server Components vulnerability is another example of how these choices keep exploding in public.React Server Components split your application into two different worlds. Client components run in the browser. Server components run on the server. You do not mark server components manually. They are server by default. You only add a directive at the top when you want a file to run on the client. That directive is the use client line.
The Original Story was published here, and it needs your support:
The past 20 years of JavaScript fatigue
Server components can access data, run backend logic, and handle any server-only work. None of that code ever ships to the browser. The browser only receives a serialized version of the rendered server components. This serialized output is not HTML and not JSX. It is a custom-structured payload that React understands. The client takes this payload and stitches it into the rest of the tree.
This serialization format is called the Flight protocol, and this is where the vulnerability happened.
The server trusted incoming Flight data without enough validation. If the packet looked like React’s internal structure, the server accepted it. The attacker only needed to create a fake Flight packet that pointed to a server action. Once the server deserialized it, the backend executed whatever was inside.
A very small example shows how simple the mistake was:
function unsafeDeserialize(input) {const packet = JSON.parse(input);
if (packet.type === "server_action") {
return serverActions[packet.action](...packet.args);
}
return packet;
}
The flaw is not complexity, the flaw is trust.
An attacker sends a fake packet:
{ "type": "server_action", "action": "runCommand", "args": ["cat /etc/passwd"]}
If that action exists on the backend, the server will run it without authentication because it thinks the request came from React.
This is how a feature became a remote code execution vulnerability.
The real fatigue is not JavaScript itself. It is the constant reinvention of tools, protocols, and abstractions that pretend to reduce complexity while actually multiplying risk.
- Never trust the client.
- Never trust incoming serialized data.
- Never assume structure means authenticity.
These basics keep getting ignored, and every time the ecosystem pays for it.
Top comments (0)