GuardVibe
News
· 5 min read

Next.js og-image RCE Leads a Week of axios and fastify Fixes

Next.js 16.3.6 fixes a critical RCE in next/og ImageResponse. Axios 1.20.0 closes seven advisories, fastify 5.12.2 an auth bypass, Nodemailer two DoS bugs.

Next.js shipped 16.3.6 to fix a critical remote code execution bug in the Node.js ImageResponse from next/og, the API most apps use for Open Graph images. The same 48 hours brought seven high-severity axios advisories, four in fastify, two more Nodemailer DoS bugs and a path traversal in webpack-dev-middleware. If you run Next.js 16.2 or 16.3, start with the first section.

What shipped

Next.js: RCE in next/og ImageResponse

  • Package: next >= 16.2.0, < 16.3.6. Fixed: 16.3.6
  • Severity: critical, CVSS v4 9.5 (GHSA-vcvr-r3jv-pc5j)

The Node.js ImageResponse inherits a vulnerability from an upstream dependency. If your app passes attacker-controlled values into SVG content, attributes or styles while generating an image, an attacker can get remote code execution on the server. The Edge ImageResponse is not affected, and neither are apps that never put untrusted input into the SVG.

npm install next@16.3.6

If you can't upgrade today, the advisory's workaround is to stop passing attacker-controlled values into SVG rendered by the Node.js ImageResponse.

axios: seven high-severity advisories, one fix

The two SSRF-adjacent ones matter most. With httpVersion: 2, axios skips your configured lookup and proxy, so any DNS-based SSRF guard you built on them stops applying. The fetch adapter ignores maxRedirects: 0, so a redirect can still reach internal hosts. Two more are prototype-pollution gadgets. If something else in the process has already polluted Object.prototype, axios can be pointed at an attacker's socket, or serialize form bodies differently. The other three are denial of service: two ReDoS bugs and an unhandled HTTP/2 error event.

npm install axios@^1.20.0

fastify: auth bypass and validation bypasses

A malformed URL under a public prefix can reach a sibling plugin's not-found handler and skip that handler's authentication preHandler. That matters if a protected fallback returns data. The other three let requests get past schema validation: false boolean schemas were skipped, header case normalization was incomplete, and an async validation result collision could swap the request body.

npm install fastify@^5.12.2

Nodemailer: two more addressparser DoS bugs

Both are quadratic-time parsing bugs. According to the advisory, a 273 KB header value blocks the event loop for about 43 seconds. They can be reached without authentication wherever user-supplied or inbound headers hit the parser, including through mailparser. The 9.1.0 fix from earlier this month does not cover them.

npm install nodemailer@^10.0.6

webpack-dev-middleware: path traversal

  • Package: webpack-dev-middleware. Fixed: 8.3.0
  • Advisory: GHSA-g84c-rxfj-3j2c (CVE-2026-76844, CVSS 7.4)

When publicPath has no trailing slash, a request like /assets../secret reads files outside the output directory. The advisory's version metadata and its text disagree on 7.x: the metadata marks 7.4.6 as patched, while the text says there is no 7.x backport. Treat 8.3.0 as the fix. Until you upgrade, add a trailing slash to publicPath and keep dev servers bound to localhost.

SVG injection, explained

SVG looks like an image format, but it is an XML document that a renderer interprets. Elements, attributes and inline styles all get parsed, and some of them make the renderer resolve references or run more parsing. When you build SVG by interpolating strings, a user-supplied value can become markup or CSS rather than plain text. That is the same mistake as HTML injection, in a format people rarely think of as code.

It turns up often in AI-generated code because OG image routes look like harmless boilerplate. Ask an agent for a "dynamic social card" and you usually get a route that reads ?title= and drops it into JSX or a template string. Nobody reviews it as an input-handling endpoint. It is also public and unauthenticated by design, so crawlers can fetch it.

Vulnerable:

// app/api/og/route.tsx
import { ImageResponse } from "next/og";

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const color = searchParams.get("color") ?? "#000";
  const title = searchParams.get("title") ?? "";
  return new ImageResponse(
    <svg width="1200" height="630">
      <rect width="1200" height="630" style={{ fill: color }} />
      <text x="60" y="320">{title}</text>
    </svg>
  );
}

Both values flow straight into the SVG: one into a style, one into content. That is exactly the shape the Next.js advisory describes.

Fixed:

const COLORS = { dark: "#0b0b0f", light: "#ffffff" } as const;

export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const color = COLORS[searchParams.get("theme") as keyof typeof COLORS] ?? COLORS.dark;
  const title = (searchParams.get("title") ?? "")
    .replace(/[^\p{L}\p{N} .,:!?-]/gu, "")
    .slice(0, 80);
  return new ImageResponse(
    <div style={{ background: color, fontSize: 64, display: "flex" }}>{title}</div>
  );
}

Pick styling from an allowlist. Don't pass it through. Strip text down to the characters a title actually needs and cap its length. Better still, look the title up by ID (?post=123) instead of accepting it as a raw string.

In review, ask one question about any generated image, PDF or SVG endpoint: which parts of this document come from the request? If the answer includes anything other than an ID you resolve server-side, treat the route like a form handler. Validate the input, allowlist it, and patch the renderer.

Check your own repo

npm ls next axios fastify nodemailer webpack-dev-middleware   # which of these are in your tree, and at what version
grep -rn "next/og" app src                                    # do you use ImageResponse at all?
npm audit                                                     # advisory-database check of your lockfile
npx guardvibe@3.46.0 audit .   # 3.46.0 adds rules for the affected next, nodemailer and webpack-dev-middleware pins (VG1175, VG1176, VG1178)

Sources

Get the next one in your feed reader

Follow GuardVibe in your feed reader. No account, no email.