Next.js shipped an out of band patch on September 22, 2026 for a critical remote code execution bug in next/og, and the patch is not the interesting part. The same vulnerability carries two different severity scores depending on which package you look at. Next.js rates it 9.5. The library underneath rates it 5.3. If you triage security work from a dependency scanner, you saw the smaller number, or you saw nothing at all.
What Next.js patched on September 22
CVE-2026-94545, a critical remote code execution flaw in the Node.js implementation of ImageResponse from next/og, fixed in 16.3.6. The Next.js advisory scores it 9.5 and lists every release from 16.2.0 up to 16.3.6 as affected. Version 15.5.26 went out the same day on the Maintenance LTS line, but it carries hardening only. The 15.x line is not affected by the remote code execution path, per Vercel's release note. Two external researchers, credited as RaghavMaheshwari124 and rafabd1, found it.
The mechanism is short. ImageResponse turns JSX and CSS into a PNG by way of Satori and Resvg, as the API reference describes. Satori builds an SVG in the middle of that pipeline, and it did not escape everything it placed into that SVG. A value an attacker could influence could therefore stop being text and start being markup. Further down the chain, other dependencies turned that crafted markup into code execution on the server. The fix is a dependency upgrade, not a rewrite of Next.js.
CVSS 9.5 vs 5.3: one CVE, scored twice
Severity is a property of how a dependency gets used, not of the dependency itself, which is why one CVE ended up with two very different numbers. The Satori advisory for CVE-2026-94545 scores it 5.3, moderate, covering versions from 0.0.27 up to the 0.33.5 fix, and states that the impact depends on what consumes the generated SVG. Satori on its own produces a string. A string is not an exploit. Push that string through the rest of the next/og pipeline on a Node.js server and it becomes a 9.5.
That gap is an operations problem, not a scoring quibble. Satori is a transitive dependency. It arrives underneath next, nobody installed it deliberately, and it surfaces in a scanner report as a moderate finding in a package most teams could not name. Moderate transitive findings are precisely what gets batched into next sprint. The critical rating exists only one level up, in an advisory published against next itself.
The practical rule follows from that. For a framework, the advisory that matters is the framework's own, not the one attached to the library underneath it. Watch the next advisory feed directly rather than inferring urgency from package-level scores. Any scanner that resolves severity from the package where a bug lives will systematically under-report the class of bugs whose blast radius is created by the consumer.
Node.js runtime vs Edge runtime for next/og
The Edge implementation of ImageResponse is not affected. Only the Node.js one is. That single line in the advisory decides whether this is an incident or a routine version bump for your storefront, and most teams will have to go and look, because the runtime choice was usually made for reasons unrelated to security.
Teams move OG image routes onto the Node.js runtime for ordinary practical reasons. Reading a font file from disk at module scope is the common one, and it is the pattern Next.js documents for custom fonts. A Node API, an existing database client, a shared helper that imports something Edge cannot run: any of those pushes the route across. Nobody writes that line thinking about attack surface. Here it is the entire attack surface.
Be precise about what is actually exposed. A static card that renders the same two strings on every request has no attacker-controlled input. The risk sits in dynamic cards, which on a commerce site means nearly all of them: the product title, the collection name, the price, a review snippet, a search term echoed back from a query parameter. That last one is the sharpest, because it takes input straight off the URL and paints it into an image. On the commerce storefronts we build, per product OG cards are standard, and their content comes from the catalogue rather than from a designer.
What we would change this week
Upgrade, then grep for ImageResponse and read every hit. The upgrade is next@16.3.6 on the 16.3 line. Anywhere between 16.2.0 and 16.3.5 puts you in range, and on 15.x you are patching for hardening rather than for this.
The audit is the part worth budgeting. For each ImageResponse route, answer three questions. Which runtime does it run on. Does any value in the JSX come from the request, from a URL parameter, or from a content source a third party can write to. And is that value rendered into text, into an attribute, or into a style, because the advisory names all three positions as carriers. Until you are patched, the stated mitigation is to keep untrusted values out of those positions, and the Satori advisory is blunt that no complete workaround exists short of upgrading.
Then fix whatever let this reach you late. Treat the framework's own security advisories as a first class feed inside your release automation, separate from the dependency scanner, and make a version mismatch a build signal rather than a ticket somebody grooms. This is the second time in a month that a Next.js critical has come from a library underneath the framework rather than from framework code. In August it was libheif under sharp under next/image, which we covered when 16.3.3 landed. Different package, different image surface, same shape: Next.js owns the advisory, somebody else owns the bug. Image pipelines are where a JavaScript framework quietly becomes a native code dependency tree, and that is now a pattern rather than an incident.