Blog / 03

NestJS 12.0.3 / 11.2.5 Fix Three Security Advisories

WebRestart

NestJS published three security advisories on September 15, 2026. One is an authentication-bypass class bug in the Fastify adapter; the other two let an unauthenticated peer kill or exhaust a TCP microservice. The fixes are split across two patch releases on each line — 12.0.2 / 11.2.4 (September 14) and 12.0.3 / 11.2.5 (September 15) — so go straight to 12.0.3 or 11.2.5 and you have all three.

None of them carry a CVE from NestJS itself, which makes them easy to miss in a npm audit habit built around CVE feeds.

The Fastify middleware bypass

GHSA-9c5c-9qcx-q35q is rated High, CVSS 8.1, against @nestjs/platform-fastify before 11.2.4 and 12.0.0–12.0.1. The advisory's own wording:

an HTTP request that uses an absolute-form request target reaches the route handler without running the path-scoped Nest middleware bound to that route

Absolute-form is the legacy proxy request line — GET http://host/private/secrets HTTP/1.1 instead of GET /private/secrets HTTP/1.1. It is still valid HTTP/1.1. The bug is an interpretation conflict between two components that both read the request target: the middleware engine matched the raw target, while Fastify's router (find-my-way) normalised it to a path first. The two strings disagree, so the router happily dispatched the route while the middleware's path match missed.

The important scoping detail is that the request still reaches the handler, so guards, interceptors and pipes on that route run as normal. What gets skipped is Nest middleware bound to a path — which, in a lot of codebases, is exactly where session or API-key checks live. The advisory classifies it as CWE-288, authentication bypass using an alternate path.

The fix in #17737 deletes the inlined @fastify/middie fork that NestJS had been carrying and depends on upstream @fastify/middie@9.3.4 instead. You can see the swap in the published package metadata: 11.2.3 lists fastify-plugin and reusify with no middie dependency, 11.2.4 lists "@fastify/middie": "9.3.4". That upstream release, dated September 4, is itself a security release — GHSA-hx87-8wv7-pjv8 / CVE-2026-85184, critical, affecting middie >= 9.1.0, < 9.3.4. So this is one bug with two blast radii: everyone on middie directly, and everyone on the Nest adapter that had forked it.

Two ways to take down a TCP microservice

The second advisory, GHSA-m8vh-jmq9-5rjg (High, CVSS 7.5, reported by ZeroVuln Labs), is a one-packet process kill. The server stringifies a client-supplied message pattern with JSON.stringify and no depth guard. Feed it a sufficiently nested object and you get RangeError: Maximum call stack size exceeded, which propagated as an unhandled rejection because handleMessage had no rejection handler — and the Node process exits. It affects the TCP and RabbitMQ transports. Patched in 12.0.2 / 11.2.4: patterns now go through a guarded Server#getPatternAsString, over-deep patterns fall back to a sentinel that matches no handler, and rejections route to handleError.

The third, GHSA-96h4-vgxj-gvm2 (Moderate, 6.5 — the advisory notes 7.5 if the port is reachable from untrusted networks), is unbounded memory growth, and it needs no valid message at all. The TCP transport frames messages as <length>#<payload>. Two paths:

  • Receive side. Declare a length, send half the payload, go quiet. The partial packet sat in the buffer with no idle timeout and no connection cap. Open many connections and repeat.
  • Send side. JsonSocket#handleSend discarded the return value of socket.write. A client that issues requests and never reads the replies makes the server queue every response in heap.

#17746 fixes both in 12.0.3 / 11.2.5 and adds two options you can tune on the server and the client:

ServerTCP / ClientTCP options:
  incompleteMessageTimeout  // default 30_000 ms, 0 disables
  maxSendBufferSize         // default 128 * 1024 * 1024 bytes, 0 disables

Those defaults are in the shipped helpers/json-socket.js, and the violations throw the new IncompleteMessageTimeoutException and MaxSendBufferSizeExceededException. Reads now also pause when a peer's outgoing buffer backs up.

Treat this as a behaviour change, not just a patch. A peer that stalls mid-packet for 30 seconds now gets dropped where it used to hang around forever. The timer refreshes on every read, so a slow-but-progressing transfer is never interrupted — but if you move large payloads over a genuinely bad link, benchmark before you assume the default fits.

The shutdown fix worth noticing

Shipped alongside, not flagged as a vulnerability: #17745 makes ServerTCP#close() destroy accepted sockets instead of only refusing new ones. Before, net.Server#close() left established connections open, so the process outlived await app.close() and handlers kept running on pre-existing connections after shutdown resolved. If you have ever had a Nest microservice refuse to exit in CI, that was probably this.

Upgrading

npm install @nestjs/microservices@12.0.3 @nestjs/platform-fastify@12.0.3
# 11.x line
npm install @nestjs/microservices@11.2.5 @nestjs/platform-fastify@11.2.5

Both lines are maintained: 12.0.3 is the latest dist-tag, 11.2.5 is legacy. If you are still weighing the jump between lines, we covered what changed in NestJS 12's ESM and Standard Schema release.

Two practical notes. If you run Nest microservices over TCP on anything other than a private network segment, the two transport advisories are the ones to act on today — the transport has no authentication by default, so "reachable" is the whole exploit prerequisite. And if you are on the Fastify adapter, check whether any auth is enforced by path-scoped middleware rather than a guard before you decide how urgent 11.2.4 is.


Not sure whether your Nest microservices are exposed, or which of your auth checks are middleware versus guards? Get in touch — we audit and upgrade Node backends for a living.