Node.js November 2026 Security Releases: 16 Flaws Focused on HTTP/2 and TLS
Contents
TL;DR
Impact
16 vulnerabilities fixed across Node.js 26.x, 24.x, and 22.x (7 HIGH, 6 MEDIUM, 3 LOW). The issues divide primarily into process crashes (DoS), resource exhaustion (unreleased descriptors and memory), data leakage (HTTPS servers incorporating memory from other sessions into request bodies), and authentication bypasses (circumventing mTLS client certificate rules).
Action
Upgrade to v26.13.1, v24.22.3, or v22.23.5. End-of-life (EOL) release lines like v25 and v20 receive no security patches.
Hands-on Verification
Comparing unpatched v26.12.1 and patched v26.13.1 on macOS: verified failure and resolution on 5 CVEs; observed no differential reproduction on 6 CVEs (which reflects local test limitations, not absence of vulnerability).
High-Risk Conditions
Running an HTTP/2 server, accepting request bodies on an HTTPS server, or configuring per-host client certificate validation (mTLS).
On November 11, 2026, the Node.js project shipped security releases across the 26.x, 24.x, and 22.x release lines (Node.js — Wednesday, November 11, 2026 Security Releases).
The update resolves 16 vulnerabilities: 7 rated HIGH, 6 MEDIUM, and 3 LOW.
This follows the release from three weeks earlier, covered in Node.js v24.22.1 fixes four HIGH CVEs, tested against v24.22.0 and EOL v25.3.0.
On this testing environment (an M4 Mac mini), checking node -v showed v26.12.1. While it included the October fixes, it did not have the November patches.
Of the 16 fixes, 7 involve HTTP/2, 5 involve TLS and HTTPS, and the remaining 4 relate to proxy configuration error reporting, zlib, and the permission model.
HTTP/2 is a protocol that multiplexes multiple concurrent requests and responses (“streams”) over a single connection, provided in Node.js via the node:http2 module.
The 16 Fixed Vulnerabilities and Affected Lines
The affected release lines match those stated in the official security advisory.
The 5 CVEs that do not affect the 22.x line comprise 4 HTTP/2 issues and 1 TLS server crash issue.
| CVE | Summary | Severity | Affected |
|---|---|---|---|
| CVE-2026-123863 | Internal assertion failure during HTTP/2 session termination crashes the server process | HIGH | 26.x, 24.x |
| CVE-2026-123864 | HTTPS server treats memory from other connections as part of the incoming request body | HIGH | 26.x, 24.x, 22.x |
| CVE-2026-123865 | Protocol error during HTTP/2 shutdown prevents cleanup, leaking memory and file descriptors | HIGH | 26.x, 24.x, 22.x |
| CVE-2026-123866 | Terminating an HTTP/2 stream causes previously freed response memory to be sent across the socket | HIGH | 26.x, 24.x, 22.x |
| CVE-2026-126051 | Uncaught exception during TLS server handshake crashes the process | HIGH | 26.x, 24.x |
| CVE-2026-123872 | TLS 1.2 renegotiation allows changing SNI host, bypassing client certificate authentication | HIGH | 26.x, 24.x, 22.x |
| CVE-2026-123874 | Resetting an HTTP/2 stream during pending write in respondWithFile() triggers use-after-free | HIGH | 26.x, 24.x |
| CVE-2026-123828 | Passing a long origin to HTTP/2 session.altsvc() causes a 1-byte out-of-bounds write | MEDIUM | 26.x, 24.x |
| CVE-2026-123867 | Per-host client certificate CA settings fail to take effect, falling back to default CA | MEDIUM | 26.x, 24.x, 22.x |
| CVE-2026-123868 | Unread incoming HTTP/2 DATA frames are omitted from maxSessionMemory accounting | MEDIUM | 26.x, 24.x |
| CVE-2026-123871 | ERR_PROXY_INVALID_CONFIG error message exposes proxy credentials | MEDIUM | 26.x, 24.x, 22.x |
| CVE-2026-123877 | https.Agent pools and reuses TLS sessions across requests with differing CA options | MEDIUM | 26.x, 24.x, 22.x |
| CVE-2026-123875 | Cancelling an HTTP/2 respondWithFile() download dereferences a NULL pointer and crashes | MEDIUM | 26.x, 24.x, 22.x |
| CVE-2026-123829 | Permission model recursive fs.watch on macOS and Windows leaks forbidden filenames | LOW | 26.x, 24.x, 22.x |
| CVE-2026-123862 | Calling deflate.params() during active zlib compression crashes on abrupt stream teardown | LOW | 26.x, 24.x, 22.x |
| CVE-2026-106071 | Permission model wildcard read permissions grant overly broad access to root paths | LOW | 26.x, 24.x, 22.x |
The permission model restricts file and network access when Node.js runs with --permission. Normal application runs without --permission are not exposed to CVE-2026-123829 or CVE-2026-106071.
CVE-2026-123829 exposes file names and modification timestamps, not file contents. It triggers when an external process creates files inside a monitored directory on macOS or Windows.
CVE-2026-106071 is an issue where wildcard access rules such as --allow-fs-read=/etc/passwd.* inadvertently grant read access to /etc/passwd itself, occurring when that rule sits at the top of the permission tree. While previously addressed for 26.x in October, 24.x and 22.x are included in this release.
For CVE-2026-123862, the advisory notes that calling deflate.params() while actively streaming data to a peer represents an uncommon usage pattern.
In the advisory credits, 5 of the 16 vulnerabilities (CVE-2026-123863, 123864, 123874, 123875, and 123862) were reported by “Chris Rohlf in collaboration with Claude and Anthropic Research.”
Matteo Collina implemented the majority of the fixes.
Prerequisites for Vulnerability Exposure
Remote exploitation requires that the target server utilizes the corresponding specific feature.
The conditions detailed in the advisory break down as follows:
| Usage Scenario | Relevant CVEs | Documented Prerequisites |
|---|---|---|
Running a server with node:http2 | 123863, 123865, 123866, 123868 | Applies to default HTTP/2 server configurations. 123863 triggers under common patterns where handlers send responses before the request body has completed |
Serving files via HTTP/2 respondWithFile() or respondWithFD() | 123874, 123875 | Serving file transfers via either method satisfies the trigger condition |
Passing external inputs like :authority headers to HTTP/2 session.altsvc() | 123828 | A single request sending a long but syntactically valid :authority value triggers the issue |
Running a node:https server that parses request bodies | 123864 | Handlers calling res.flushHeaders() before body completion and subsequently supplying non-empty data to res.end(). Exfiltrating memory requires an endpoint that echoes or persists the request body. Plain unencrypted node:http is unaffected |
TLS servers using SNICallback, OCSPRequest, newSession, resumeSession, or server.addContext() | 126051 | Using any of these hooks allows a crafted TLS handshake to crash the server |
| Enforcing per-host client certificate rules (mTLS) | 123872, 123867 | 123872 alters the SNI host during TLS 1.2 renegotiation. 123867 triggers when server.addContext(host, { ca }) specifies a custom CA without supplying server certificates |
Mixing requests with and without explicit ca on https.Agent | 123877 | Omitting ca and passing ca: [] share the same connection pool key and reuse sessions |
| Supplying proxy credentials inside URLs via environment variables | 123871 | Malformed proxy configurations cause error messages to print the raw username and password |
Applications limited to local tooling, frontend build pipelines, or CLI utilities without an exposed network listener face minimal risk from these issues.
Nonetheless, keeping obsolete versions around after public patch release offers no advantage, making a prompt upgrade advisable.
HTTPS Server Exposing Memory from Other Connections (CVE-2026-123864)
This vulnerability carries notable severity: the advisory states that an unauthenticated remote client can read memory from other connections, potentially including TLS private keys and request data submitted by other users.
In an HTTPS server, encrypted incoming packets are decrypted in the TLS layer before passing to the HTTP parser (llhttp).
When a 0-byte write occurs from a callback while the parser is still processing the request body, the TLS layer immediately attempts to decrypt subsequent ciphertexts. This causes re-entrant execution of the parser. The nested invocation leads to use-after-free conditions and out-of-bounds heap reads. As a result, the server incorporates freed or unrelated memory buffers into the active request payload.
“Freed memory” refers to memory returned by the runtime to the allocator. Because returned memory is not zeroed immediately, residual data from prior connections—such as private keys or session payloads—may remain intact. When treated as request body content, this data leaks back to the attacker if the application logs, stores, or echoes incoming requests.
The fix, committed as tls,http: prevent nested parser execution from empty writes, prevents re-entrant parser invocations triggered by empty writes (CHANGELOG_V24).
HTTP/2 Stream Teardown and Use-After-Free (CVE-2026-123866, 123874)
In HTTP/2, sending an RST_STREAM frame terminates an individual stream within a shared connection.
Under CVE-2026-123866, if a stream terminates while its outbound write is queued and waiting, the response memory is freed while references to it remain active. Freed or unrelated memory gets flushed to the underlying network socket, leaking data from other connections to the peer.
According to the advisory, window size adjustments introduced in 26.9.0 and 24.22.0 allowed remote peers to induce this termination condition purely via client-side frame manipulation. On all lines, including 22.x, application-level stream cancellations (such as idle timeouts) encounter the same vulnerability.
CVE-2026-123874 operates similarly: resetting an active respondWithFile() transfer leaves pending write callbacks referencing deallocated stream structures. This results in process memory corruption rather than direct data leakage.
Bypassing Per-Host mTLS Validation (CVE-2026-123872, 123867)
mTLS (mutual TLS) requires both the server and client to exchange and verify certificates. When hosting multiple domains on a single listener, the client transmits the desired target domain as SNI (Server Name Indication) during the TLS handshake, guiding the server’s certificate selection.
CVE-2026-123872 occurred because clients could switch the SNI hostname during a TLS 1.2 renegotiation. An attacker could authenticate against a public hostname with standard credentials, then renegotiate to an administrative hostname requiring strict client certificates, bypassing the restriction while preserving initial access. The patch forbids altering SNI across renegotiations or session resumptions, rejecting resumption entirely when configurations conflict (CHANGELOG_V24).
CVE-2026-123867 occurs when server.addContext(host, { ca: protectedCA }) specifies a verification CA without attaching corresponding server certificates. In this setup, the per-host CA is ignored, falling back to the default root CA store. Clients holding certificates trusted by the default CA can access the restricted host even with requestCert and rejectUnauthorized enabled.
Hands-on Verification Against Unpatched Releases
Using an M4 Mac mini running macOS (ARM64), unpatched v26.12.1 was compared against official v26.13.1 binaries verified against SHASUMS256.txt. Comparisons across 24.x used v24.22.2 and v24.22.3. All test servers bound to 127.0.0.1.
Five vulnerabilities exhibited observable failure before patching and clean resolution on patched builds:
| CVE | v26.12.1 Behavior | v26.13.1 Behavior |
|---|---|---|
| CVE-2026-123865 | Protocol errors during stream termination hung the server process, requiring SIGKILL (reproduced with core regression suite) | Test finished normally with exit code 0 |
| CVE-2026-123877 | Agent.getName() produced identical keys for omitted ca and ca: [], reusing default-CA sessions for restricted calls | Pool keys separated; ca: [] requests failed as intended |
| CVE-2026-123867 | On server.addContext('protected.test', { ca: CA2 }), certificates issued by the default authority (CA1) were accepted instead of requiring CA2 | Certificates from CA1 were rejected; only certificates signed by CA2 passed authentication |
| CVE-2026-123871 | Invalid URLs passed to http.setGlobalProxyFromEnv() printed http://proxy-user:sup3rS3cret@[::1 in plaintext | Masked as http://[redacted]@[::1 |
| CVE-2026-123862 | Calling deflate.params() before stream destruction bypassed callbacks and emitted uncaught ERR_INTERNAL_ASSERTION, terminating the process | Error routed cleanly to callback; process exited with code 0 |
Six vulnerabilities showed no differential behavior under local test harnesses:
| CVE | Test Results and Observations |
|---|---|
| CVE-2026-123875 | Running the upstream regression test completed cleanly on both pre-patch and patched binaries without crashing |
| CVE-2026-123868 | Streaming 1-byte DATA frames into an unreading HTTP/2 server caused 150–260 bytes of memory retention per frame (10–20x wire volume), but exceeding maxSessionMemory=2 MiB did not block subsequent streams on either build |
| CVE-2026-106071 | Testing wildcard rules like --allow-fs-read=<dir>/passwd.* produced matching pass/fail evaluations on both builds without broader leakage |
| CVE-2026-123829 | On macOS, combining --permission and --allow-fs-read permitted readdirSync on nested subdirectories, preventing creation of a strictly forbidden subpath; file notifications behaved identically on both builds |
| CVE-2026-123828 | Supplying a 4,000-character :authority to session.altsvc() threw ERR_INVALID_URL early on both versions, never reaching the C++ buffer |
| CVE-2026-123864 | Testing concurrent res.flushHeaders() with zero-byte writes to intercept payload leaks could not finish reliably and was terminated |
These six issues could not be reproduced under these specific local scripts, but that does not mean the vulnerabilities are absent. Real-world exposure depends on whether your application matches the trigger conditions outlined in the official security advisory.
Discrepancies Between Advisory and CHANGELOG
Cross-referencing the advisory against CHANGELOG documents (22.23.5, 24.22.3, 26.13.1) highlights several inconsistencies:
| Issue | Advisory Listing | CHANGELOG Listing |
|---|---|---|
CVE-2026-123873 (permission: check heap snapshot destination, LOW) | Omitted | Listed under notable changes across all three lines |
| CVE-2026-106071 (Wildcard permission access) | Listed as LOW affecting 26.x, 24.x, and 22.x | Omitted from notable changes; commit src: fix the fs permission tree node on wildcard nodes appears in 22.23.5 and 24.22.3 lists, but not in 26.13.1 |
| HTTP/2 CVEs on 22.x (123863, 123874, 123828, 123868) | Marked as not affecting 22.x | Present in notable changes for 22.23.5, though commits other than 123863 are absent from commit logs |
Available public documentation does not clarify which record is definitive. However, upgrading to the latest release incorporates all commits regardless of changelog discrepancies.
Bundled Dependency Updates
Core dependencies compiled directly into the Node.js binary were updated alongside these releases. Versions reflect CHANGELOG records:
| Dependency | Purpose | 22.23.5 | 24.22.3 | 26.13.1 |
|---|---|---|---|---|
| llhttp | HTTP parser | 9.4.4 | 9.4.4 | 9.4.4 |
| undici | fetch implementation | Unchanged | 7.30.2 | 8.12.4 |
| SQLite | node:sqlite | 3.54.1 | 3.54.1 | 3.54.1 |
| Brotli | Compression engine | Unchanged | 1.2.1 | 1.2.1 |
The advisory lists llhttp (22.x, 24.x, 26.x), undici (24.x, 26.x), and SQLite (22.x, 24.x) as updated for public vulnerabilities. However, the 26.13.1 changelog also contains deps: update sqlite to 3.54.1, diverging slightly from the advisory’s line summary.
Upgrade Procedures
Patched releases are available as v26.13.1, v24.22.3, and v22.23.5.
Check your active version using node -v. End-of-life lines like v25, v23, or v20 receive no patches and require migrating to an active LTS or maintenance release.
Per the Node.js release schedule, v26 transitioned to Active LTS on October 28, 2026; v24 entered Maintenance LTS on October 20; and v22 remains supported until April 30, 2027 (Node.js Release schedule).
Package availability checked on the evening of November 12, 2026 (JST):
| Distribution | Upgrade Command / Method | Status (Nov 12 Evening JST) |
|---|---|---|
| nodejs.org Binaries | Download from the Downloads page | Published for all 3 lines |
| nvm (Node Version Manager) | nvm install 24 (fetches latest 24.x) | Available from upstream mirrors |
| Docker | Rebuild using FROM node:24.22.3 | node:22.23.5, node:24.22.3, node:26.13.1 tags live on Docker Hub |
| Homebrew | brew upgrade node (or node@24, node@22) | Updated to node 26.13.1, node@24 24.22.3, node@22 22.23.5 |
When using rolling Docker tags like node:24, cached base images will retain unpatched builds unless refreshed via docker pull prior to rebuild.