Tech 6 min read

The Mintlify Vulnerability Is Not React2Shell: Framework Responsibility vs. Implementation Responsibility

IkesanContents

Multiple Vulnerabilities Were Found in Mintlify

In December 2025, multiple serious vulnerabilities were discovered in Mintlify, the documentation platform used by companies such as Discord, Vercel, and Cursor.

The most serious issue was that arbitrary JavaScript could be executed during server-side rendering of MDX (Markdown + JSX) pages, tracked as CVE-2025-67843.

// Just writing MDX like this would execute code on the server
{process.env.SECRET_KEY}
{require('fs').readFileSync('/etc/passwd', 'utf8')}

That meant full access to environment variables and the filesystem, and the report also said Mintlify users could poison Next.js caches and even cause XSS or tampering on other customers’ sites.

See the discoverer’s report for details: how to hack discord, vercel and more with one easy trick

This Is a Different Story from React2Shell

Because there was a lot of noise recently around React and Next.js, especially “React2Shell” (CVE-2025-55182, CVSS 10.0), I initially assumed this Mintlify issue was related. It turned out to be completely separate.

React2ShellMintlify
CauseA deserialization vulnerability in the RSC protocolServer-side eval of user input
ResponsibilityReact core (framework side)Mintlify’s implementation (application side)
Attack conditionJust send an HTTP requestYou must be a Mintlify customer
ScopeAll React 19 + RSC implementationsMintlify only

React2Shell is a framework-side problem. There was a vulnerability in the deserialization logic of the Flight protocol used by React Server Components, and a special HTTP request was enough to trigger RCE. If you use React or Next.js, you could be affected just by virtue of using the framework.

Mintlify’s issue, by contrast, was an implementation-side problem. It was the classic mistake of evaluating user-written MDX on the server.

A Framework-Agnostic Problem

The Mintlify vulnerability is not specific to Next.js. If you make the same design choice, it can happen in Nuxt or SvelteKit too.

// The only point is that this should never be done on the server
eval(userProvidedCode)
new Function(userProvidedCode)()
vm.runInContext(userProvidedCode)

This is basic security hygiene: do not run untrusted code on the server. Mintlify happened to be built with Next.js, but the root cause has nothing to do with the framework itself.

What About Astro?

So what about Astro, the static site generator?

With Astro, MDX is processed at build time. In production, only static HTML is served, so the attack surface for “dynamically evaluating user input on the server” simply does not exist.

That said, if you use Astro in SSR mode and then dynamically eval user input on the server, you can create the same hole. The framework is not going to save you.

In short:

  • SSG by default -> smaller attack surface (Astro, Hugo, 11ty, and so on)
  • SSR by default -> larger attack surface (Next.js, Nuxt, SvelteKit, and so on)

That does not mean SSR is bad. SSR is fine when implemented correctly. The problem is the implementation decision to eval user input on the server.

My Honest Reaction

The discoverer received a $5,000 bounty. The discovery and disclosure were excellent work.

Still, my honest reaction was: “How did such a basic mistake get through?”

“Do not eval user input on the server” is security 101. The likely flow was something like this:

  1. “It would be convenient if MDX could use JS expressions”
  2. It worked in development, which felt great
  3. They shipped the same behavior to production
  4. No one noticed that it was running on the server

The convenience of “Markdown with JSX inside” may have blurred the line of “should this really be evaluated on the server?” This is a classic case of optimizing for DX and pushing security aside.

Whether nobody caught it in code review, or there was no review at all, it is still unsettling that this passed in a service used by Fortune 500 companies.

Conclusion: Separate the Responsibility

When a security incident happens, it is important to distinguish between a framework-level responsibility and an implementation-level responsibility.

Framework-level responsibility (e.g. React2Shell)

  • You are affected simply by using the framework
  • Response: apply patches, upgrade versions, and sometimes migrate away from the framework

Implementation-level responsibility (e.g. Mintlify)

  • Only affects the specific implementation pattern that was chosen
  • Response: review the implementation and perform a security review

Seeing Mintlify and concluding “Next.js is dangerous” is too simplistic. At the same time, seeing React2Shell and concluding “we do not use Next.js, so we are safe” is also dangerous; similar RSC-style features could appear in Nuxt or elsewhere in the future.

The important thing is to understand what caused the issue and whose responsibility it was. Once you know that, you can respond appropriately.

That Said, I Still Migrated to Astro

In theory, yes, you should separate responsibility from implementation. In practice, things are not that simple.

When React2Shell was discovered, I patched it immediately. A few days later there was another vulnerability because the initial fix was incomplete. I patched it again. Then a few days later: “still vulnerable.”

Honestly, it became a hassle.

In the end, the corporate site I was managing was migrated from Next.js to Astro. If you are only serving static HTML with SSG, there is no server-side attack surface in the first place. You also escape upgrade hell.

The Mintlify issue is the implementer’s responsibility, and React2Shell is the framework’s responsibility. The distinction matters. But if you do not even have time to think about whose responsibility it is, choosing the option with the smaller attack surface is also a valid decision.

I think the migration from a frontend background is pretty manageable in Astro. If you have the chance to move, I think it is worth considering. For large sites or ones with lots of dynamic requirements, use Next.js or Nuxt when it really makes sense.

Personally, I would probably keep Nuxt as an option, but never choose Next.js first.

Next.js and Nuxt have different design philosophies, and I simply prefer Nuxt less than Astro. I will leave that for another time, because it would take too long to explain here.