@NESTJS/MICROSERVICES · GHSA-96h4-vgxj-gvm2 · Package
FIX → 12.0.3 · 11.2.5
A peer that can open a TCP connection to a NestJS microservice using the built-in TCP transport can make the process allocate memory without limit until it is killed by the OS or its container limit. No credentials or valid messages are needed.
first public Sep 15, 2026 · 1 source
Could this affect your app?
Free first check from your website. Connect your repo for the full answer.
Applications are exposed only if they start a microservice with the built-in TCP transport and its port is reachable by an untrusted peer. Services on a trusted internal network behind a network policy or firewall are not exposed to outside peers. The sources say the same code exists in 10.x and earlier.
Not affected if l4-policy-in-front, port-trusted-peers-only, tcp-transport-not-used
Who cares kubernetes, microservices, nestjs, node, self-hosted
@nestjs/microservices in the project's lockfile or with npm ls @nestjs/microservices.Transport.TCP in microservice bootstrap code and confirm whether its port is reachable from untrusted networks.Upgrade to 12.0.3 · 11.2.5
@nestjs/microservices 12.x: upgrade to A peer that can open a TCP connection to a NestJS microservice using the built-in TCP transport can make the server process allocate memory without limit, on either side of the connection, until the process is killed by the OS or by its container memory limit. No authentication, no credentials, and no valid message are required.
Applications are affected only if they start a microservice with Transport.TCP and the
transport's port is reachable by an untrusted peer.
Proposed: Moderate — CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (6.5)
The TCP transport is documented for service-to-service communication on an internal
network, which is why the adjacent-network vector is proposed rather than the network one.
Operators who publish the transport's port to an untrusted network directly, without an L4
policy in front of it, should treat this as CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
(7.5, High) for their own deployment.
CWE-770 (Allocation of Resources Without Limits or Throttling), CWE-400 (Uncontrolled Resource Consumption).
| Package | Affected | Patched |
|---|---|---|
@nestjs/microservices |
Check it with your coding agent
It reads this page and tells you if your code is affected.
Are you affected?
Ask from here. InfraStack checks the affected versions against your stack and answers in your inbox.
InfraStack checks the affected versions against your stack and answers in Slack, with the evidence and the fix.
12.0.3@nestjs/microservices 11.x: upgrade to 11.2.5>= 12.0.0, < 12.0.3 |
| 12.0.3 |
@nestjs/microservices | < 11.2.5 | 11.2.5 |
The same code is present in the 10.x line and earlier. Those lines are end-of-life and will not receive a patch; upgrade to a supported major.
Only @nestjs/microservices is affected. No other package needs updating for this issue.
Two independent paths let one peer grow the heap without bound.
The TCP transport frames messages as <length>#<payload>. A peer may declare a length,
send part of the payload, and then stop. JsonSocket keeps the partial payload buffered
while it waits for the rest, and nothing ever reclaimed it: there was no socket timeout, no
cap on the number of connections, and no tracking of accepted sockets.
maxBufferSize did not close this. It caps a single connection, defaulting to 128M
characters, and is enforced per connection, so the effective ceiling was the number of
connections an attacker chose to open.
Ten connections that each send 20MB and then go silent:
| rss | heapUsed | |
|---|---|---|
| baseline | 152.3MB | 28.1MB |
| after 200MB sent, all stalled | 561.2MB | 229.5MB |
The memory was held for as long as the connections stayed open. Closing them released it, so a peer could hold it indefinitely at negligible cost to itself.
JsonSocket#handleSend discarded the return value of socket.write. A peer that issues
requests and never reads the responses therefore made the process queue every response in
memory, with no ceiling and no signal to stop producing more.
Thirty requests from a client whose stream is paused, against a handler returning an 8MB payload, grew rss from 152.4MB to 419.1MB. The amplification here is roughly 1:1, because the response size is set by the application rather than by the attacker; the defect is that the server buffers all of it rather than applying backpressure or giving up.
An unauthenticated peer that can reach the transport's port can drive the process to an out-of-memory kill. Under a container memory limit this is fast and repeatable, and it restarts the pod rather than merely degrading it.
There is no confidentiality or integrity impact. Nothing is read, written, or executed; the failure mode is availability only.
Applications are not affected if the TCP transport is not used, or if the transport's port is reachable only by trusted peers.
Upgrade @nestjs/microservices to 12.0.3 or 11.2.5.
Three changes landed together:
incompleteMessageTimeout, default 30000ms.drain, so a peer that stops reading cannot keep issuing requests.maxSendBufferSize, default 128MB.Both limits are configurable on the server and on the client, and either can be disabled by
setting it to 0.
Both limits are enabled by default, following the precedent set by maxBufferSize. A peer
that stops sending mid-packet for 30 seconds, or that lets more than 128MB of responses
queue unread on a single connection, is now disconnected where previously it was not.
The send cap can also drop a peer that is genuinely reading, if responses are produced
faster than the socket drains for long enough. The default is set high for that reason.
Applications that stream very large responses to deliberately slow consumers should raise
maxSendBufferSize or set it to 0.
For anyone who cannot upgrade immediately:
maxBufferSize. This reduces what a single connection can pin on the receive
side. It does not bound the total, because the limit is per connection and the number of
connections is not capped, and it does nothing for the response-queueing path.socketClass. An application-provided socket class can implement its
own idle timeout and backpressure handling. This is the only pre-patch mitigation that
addresses the sending side.Setting a process memory limit does not mitigate this. It converts the failure from an out-of-memory kill of the host into an out-of-memory kill of the process.
Reported by Salman Aljardan, who provided a detailed write-up and self-contained proofs of concept for each issue, and coordinated disclosure.