
Loading

Loading
We use strictly necessary cookies to run this site, and analytics cookies to understand how it's used. See our Privacy Policy for details.
A GitHub-reviewed security advisory, not necessarily under active exploitation the way our CISA KEV advisories are, but a real, disclosed vulnerability in an open-source dependency.
Affected packages
Vulnerable: >= 2.0.0, < 2.170.0
Patched in 2.170.0
Vulnerable: >= 2.0.0, < 2.170.0
Patched in 2.170.0
Vulnerable: >= 2.0.0, < 2.170.0
Patched in 2.170.0
Details
A combination of bugs in node-opcua causes unlimited TCP socket accumulation (FIN-WAIT-2 state) during automatic reconnection, leading to memory exhaustion and eventual container/process crash (OOM kill). The issue is triggered by the default configuration (keepSessionAlive: true) when the OPC UA server has clock skew relative to the client.
Affected version: Tested on 2.169.0 (latest as of April 2026).
Bug #1 - ClientTCP_transport._on_ACK_response() uses socket.end() instead of socket.destroy()
File: node-opcua-transport/src/client_tcp_transport.ts, _on_ACK_response() method
When the HEL/ACK handshake fails during a reconnection attempt, the error handler calls socket.end():
if (err || !data) {
externalCallback(err || new Error("no data"));
if (this._socket) {
this._socket.end(); // <- sends TCP FIN, leaves socket in FIN-WAIT-2
}
}
socket.end() sends a TCP FIN and waits for the peer to close its side. If the peer doesn't respond (common with PLCs), the socket remains in FIN-WAIT-2 state indefinitely, leaking file descriptors and memory. During rapid reconnection cycles (triggered by Bug #2 below), every failed HEL/ACK creates a new leaked socket.
Bug #2 - ClientSessionKeepAliveManager._ping_server() treats BadInvalidTimestamp as network outage
File: node-opcua-client/src/client_session_keepalive_manager.ts, _ping_server() method
The keepalive manager reads Server.ServerStatus.CurrentTime on each ping cycle. If the server responds with BadInvalidTimestamp (because the client's RequestHeader.timestamp falls outside the server's tolerance window due to clock skew), the manager treats this as a fatal network error:
// Any error -> emit("failure") -> terminateConnection() -> forceConnectionBreak()
This triggers a full transport-level reconnection on every keepalive cycle (every keepAliveInterval ms). Combined with Bug #1, each reconnection attempt leaks one TCP socket in FIN-WAIT-2. Impact amplification: With keepAliveInterval: 3000 (3 seconds), the client leaks ~20 sockets/minute, ~1200/hour, exhausting resources in hours.
For Bug #1 (_on_ACK_response):
// Replace socket.end() with socket.destroy()
if (this._socket) {
this._socket.destroy();
}
For Bug #2 (_ping_server): Distinguish between transport-level errors (actual network outage) and application-level OPC UA status codes like BadInvalidTimestamp. The latter indicates the server is reachable and the session is alive - only the timestamp validation failed. The keepalive should not trigger reconnection.
Marco Velluso @Velluso [email protected] Requesting CVE assignment and credit as reporter upon fix publication.
EPSS, exploit probability
Low0.44%
estimated chance of real-world exploitation in the next 30 days, higher than 38.5% of every CVE FIRST.org scores
Refreshed 11/1/2026, via FIRST.org's EPSS model, not CVSS, this measures likelihood of exploitation, not how severe it would be.
Talk to us about this
Find out whether this dependency (or others like it) is actually reachable in your codebase.
Get help patching, or building dependency-scanning into your pipeline going forward.
A secure code review or supply chain review finds this before an advisory does.