Skip to content

v22.0.1: Hardening pass (SSDP/HTTPU dispatch, parser, Advertiser)

Latest

Choose a tag to compare

@brandonlehmann brandonlehmann released this 24 May 04:37
4f91225

Overview

This release tightens the inbound HTTPU dispatcher and parser to RFC 9112 conformance, fixes several spec deviations in the Advertiser M-SEARCH responder, and exposes new options for explicit control over multicast inbound filtering and outbound advertisement.

Breaking changes

  • SSDP.Options.ttl is removed. The @gibme/multicast 22.0.1 dependency hardcodes IP_MULTICAST_TTL=255 per RFC 6762 outbound hardening; the field had become silently ignored. Consumers passing ttl will see a TypeScript error at compile time.
  • Default Advertiser UUID is now RFC 9562 v4 (was v7). UUID v7 encodes the device-creation ms timestamp in the first 48 bits, which was broadcast to every host on the multicast scope via every USN header. If you need cross-reboot identity stability, generate a UUID once at install time, persist it, and pass it via the uuid option.

New options

  • SSDP.Options.linkLocalOnly (default false). When true, drops inbound packets whose source is not on a local-link subnet. SSDP is not mDNS: RFC 6762 §11 normatively governs only the mDNS group, so the default keeps SSDP's traditional cross-subnet behavior. Single-segment deployments can opt into the stricter check.
  • Advertiser.Options.silentMode (default false). When true, suppresses periodic multicast NOTIFY ssdp:alive and ssdp:byebye traffic; the advertiser only answers unicast M-SEARCH requests that pass authenticationProvider. Useful for pull-only / private-discovery deployments.

Dispatch and parser hardening

  • The start-line of incoming datagrams is now matched exactly against three permitted shapes (M-SEARCH * HTTP/1.1, NOTIFY * HTTP/1.1, HTTP/1.1 <status>). Garbage UDP no longer surfaces as a 'reply' event with attacker-controlled headers.
  • Header parser rejects whitespace between field name and colon per RFC 9112 §5.1.
  • Line splitting tolerates both CRLF and bare-LF terminators.
  • getHeader() now returns an empty string for present-but-empty headers, agreeing with hasHeader().
  • The 'drop' event from the underlying multicast layer is re-emitted by SSDP and Browser so consumers have observability into packets filtered by linkLocalOnly.

Advertiser semantics

  • M-SEARCH responder now decides whether the target ST is one we advertise BEFORE calling authenticationProvider. Unknown STs are silently dropped per SSDP convention instead of emitting an attacker-controllable error event.
  • ssdp:all responses now correctly include upnp:rootdevice and uuid:<UUID> in addition to each registered service.
  • Replies are now delayed by a random interval in [0, MX] seconds per UPnP DA to mitigate the multicast reply-storm amplification.
  • authenticationProvider throws are caught and treated as a deny; the failure is surfaced via the 'error' event instead of producing an unhandled promise rejection.
  • An empty-string uuid (if explicitly passed) is replaced with a fresh v4 instead of falling open on uuid: matching.

CI / supply chain

  • Pinned s0/git-publish-subdir-action to a verified commit SHA (was @develop).
  • Pinned yarn to 1.22.22 in all install steps.
  • All workflows now use yarn install --frozen-lockfile.
  • Restored the npm install -g npm@latest upgrade in the publish job; Node 22.x ships npm 10.x which is too old for npm-CLI-side OIDC trusted publishing (requires npm 11.5.1+).

Packaging

  • package.json "types" field now points to ./dist/index.d.ts (was ./dist/index.ts, which is never emitted by tsc).

Tests

  • Test suite expanded from 8 to 17 tests; adds regression coverage for parser, dispatcher, and Advertiser hardening.
  • Dispatcher hardening tests reworked to use per-test marker-filtered assertions with positive-control packets, eliminating a multicast-loopback race that intermittently caused false failures on Windows runners when the suite-level advertiser's periodic NOTIFY frames landed inside the test window.

Security considerations

A new "Security Considerations" section in the README enumerates the SSDP threat model: the protocol has no authentication, the authenticationProvider gates unicast replies only (not outbound NOTIFY; use silentMode to suppress those), the random MX delay mitigates reply-storm amplification, and inbound parser hardening is documented.