v1.1.4
Correctness-hardening release from the engine code-quality audit. No breaking
changes — every change is an internal robustness fix or a type-declaration
catch-up, so this is a drop-in for 1.1.x.
Fixed — correctness & robustness
- ≥4 GiB response truncation — closed. libuv's
uv_buf_tcarries its length
in a 32-bit field anduv_write/uv_try_writecount bytes with anint, so a
single response buffer ≥ 4 GiB truncated (silent corruption plus a hung client,
sinceContent-Lengththen lied) and a ≥ 2 GiB completion check wrapped.
Response bodies are app-controlled with no cap, so any payload past this
watermark is now split into ≤ 1 GiB segments handed to oneuv_write— one
write request, terminal bookkeeping still firing exactly once — and every
length/return cast below a segment boundary is exact. Applies across the
synchronous fast paths, the queued path, and the pipelined cork buffer. - WebSocket frames dropped on an async upgrade — fixed. Frames a client sent
before the101was written — bytes buffered in the HTTP parser from the
handshake segment, plus any frames that arrived while an async upgrade handler
had not yet responded (c->pending) — could be silently dropped once the
upgrade completed, desyncing the connection. They are now drained to the
WebSocket parser in receive order (copied out first, since a protocol error may
tear the connection down mid-feed). size_t→intoverflow guards on V8 boundaries. TheString::NewFromUtf8
length, theArray::Newcapacity hint, and the inbound WebSocket message
length — each casts asize_tto anintfor a V8 API — are now clamped to
INT_MAX, closing overflow paths reachable only when an operator raises a limit
past 2 GiB.- Cork-buffer capacity retention. The pipelined cork buffer now
shrink_to_fits once it grows past 64 KiB, so a one-off large batch no longer
retains its peak allocation for the life of the connection. - Connection-recovery invariant corrected. The libuv handle is recovered via
handle.data, never a struct-offset pointer cast, so its position in the
connection struct carries no layout invariant — a stale comment implying
otherwise has been removed.
Types
index.d.tsnow declaresssl.ecdhCurveandssl.ciphersuites. Both
shipped functionally in 1.1.3 (documented there, runtime insrc/tls.h) but
were missing from the type surface; TypeScript consumers can now set them
without a cast.
Compatibility
- No API or behavior changes for existing code — everything here is an
internal robustness fix or an additive type declaration. Drop-in for 1.1.x;
MoroJS picks it up via^1.1.0.
Verification
- Full socket-level wire matrix green: HTTP conformance + edge + hardening +
regression, WebSocket, limits, TLS + TLS-hardening, permessage-deflate —
154node --test, 0 failures. - C++ unit suites under the standalone harness: 593 checks (141 HTTP parser +
352 WebSocket + 100 permessage-deflate). - Native build clean on ABI 137 (Node 24, darwin/arm64); the full 6-ABI matrix
(Node 20–26) builds in CI with npm provenance. - libFuzzer HTTP/WS/TLS/deflate corpora run in CI; the permessage-deflate harness
gains a persisted seed corpus this release.
See docs/THREAT_MODEL.md for the full defense inventory.