Skip to content

v1.1.4

Choose a tag to compare

@M-Chris M-Chris released this 13 Jul 22:10
· 14 commits to main since this release
bc961de

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_t carries its length
    in a 32-bit field and uv_write/uv_try_write count bytes with an int, so a
    single response buffer ≥ 4 GiB truncated (silent corruption plus a hung client,
    since Content-Length then 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 one uv_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 the 101 was 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_tint overflow guards on V8 boundaries. The String::NewFromUtf8
    length, the Array::New capacity hint, and the inbound WebSocket message
    length — each casts a size_t to an int for 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.ts now declares ssl.ecdhCurve and ssl.ciphersuites. Both
    shipped functionally in 1.1.3 (documented there, runtime in src/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 —
    154 node --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.