4 min
––– views

We Were Affected by Recent React RSC Vulnerabilities: Code Exposure (CVE-2025-55183) and React2Shell

A vulnerability chain around React Server Components (RSC) and its Flight protocol was disclosed recently. The first one was the critical, widely discussed React2Shell (CVE-2025-55182): unauthenticated remote code execution (RCE) with a single request, exploiting unsafe deserialization.

After that patch, researchers kept digging (as always happens), and two additional vulnerabilities showed up in the same RSC ecosystem:

  • CVE-2025-55184 / CVE-2025-67779: Denial of Service (DoS) via payloads that can hang the process.
  • CVE-2025-55183: Source Code Exposure, where a malicious request can make a Server Function return compiled code from other Server Functions.

In Vercel’s words:

A specially crafted HTTP request can cause a Server Function to return the compiled source code of other Server Functions in your application. This could reveal business logic. Secrets may also be exposed if they are defined directly in your code (rather than accessed via runtime environment variables) and are referenced within a Server Function. Depending on your bundler configuration, these values could be inlined into the function’s compiled output.

Source: https://nextjs.org/blog/security-update-2025-12-11 (CVE-2025-55183)

What happened to us (and what did NOT happen)link

We use RSC and Server Functions in Next.js. In the logs we saw clearly malicious requests trying to trigger the behavior described above, and yes secrets leaked.

However, as much as it may sound like they read our secrets directly at runtime, there’s an important technical detail:

  • It wasn’t reading runtime environment variables (like process.env.SECRET_XYZ).
  • Some secrets that originally came from a .env ended up injected by the bundler (Turbopack) and were literally burned into the compiled Server Function code.

In other words: to the attacker, these weren’t environment variables. They were secrets baked into transpiled JavaScript. And with CVE-2025-55183, that output can end up inside an HTTP response.

The old trick, 2025 editionlink

The industry keeps rediscovering the same fires under new names (remember Log4Shell and all the aftershock CVEs?). Is this JavaScript’s fault? Not necessarily. This happens everywhere. But FullStack JS frameworks are pushing a level of complexity and “magic” that abstracts responsibilities away from developers: SSR, RSC, Server Actions and now also brand-new protocols (Flight). The attack surface grows, and so does the likelihood of design or implementation mistakes.

And as if that weren’t enough: DoS with CPU pegged at 200%link

On top of the secret leakage, our site was unavailable for a while: our server hit ~200% CPU for several minutes. It wasn’t a normal traffic spike it was pinned CPU, like the process had gotten stuck in a loop.

That’s exactly the kind of symptom described by the RSC DoS (CVE-2025-55184, and the complete fix CVE-2025-67779): a malicious request can trigger a “hang” state during payload deserialization and leave the process burning CPU.

The most revealing, telltale sign of where this came from: CPU went back to normal right when I deployed the Next.js update with the patches. Not perfect forensic proof, but the pattern lines up way too neatly with the CVE descriptions to ignore.

CPU hang

Why “non-root” helped us (but didn’t fully save us)link

In our case, we were lucky not to run the process as a fully privileged user:

dockerfileRUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs

COPY --from=builder /app/public ./public

# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next

# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

That reduces the blast radius if an attacker manages to reach code execution (and limits collateral damage), but if they can run code inside the Node process, they can still read that process’s process.env and any files that user can access. At least they didn’t use our server to mine crypto.

The afternoon nobody wantslink

We didn’t detect command execution on the host/container (we didn’t see post-exploitation behavior like downloads, shells, etc.). But we did see enough exfiltration to treat it as an incident:

  • Invalidate and rotate API keys.
  • Rotate credentials and tokens.
  • Update Next.js to a fixed version.
  • Audit how we handle secrets so they don’t get baked in by the bundler.

Conclusionlink

More convinced than ever that RSC was a mistake, and that the state of the art in FullStack JavaScript frameworks is heading into a dead end. Let’s go back to explicitness, reject the abstractions, and demand less magic. Long live SPAs + REST, or traditional MPAs.