fix: validate relay URL before calling load() to prevent remote DoS via unhandled rejection #22

Merged
matt merged 1 commit from mailship-iij-post-notify-with-non-wss-relay-url-crash-655 into main 2026-09-18 15:38:47 +00:00

1 commit

Author SHA1 Message Date
Agent
0a5b7ad14d fix: validate relay URL before calling load() and soften unhandledRejection handler
All checks were successful
CI / checks (pull_request) Successful in 34s
POST /notify/:id accepted an attacker-supplied relay URL with a non-ws://
scheme (e.g. http://…). The URL was passed straight to @welshman/net's
load(), whose internal batcher throws 'Invalid relay url' asynchronously
(via setTimeout). That error escaped the route's try/catch as an unhandled
rejection, triggering process.exit(1) in src/index.ts — the entire server
died. Any registered subscriber could crash the service repeatedly.

Two fixes applied (either alone breaks the attack):

1. Validate relay scheme in the route handler before calling load()
   (src/server.ts). Uses isRelayUrl() from @welshman/util, which accepts
   only wss:// and ws:// schemes. Returns 400 immediately for invalid
   URLs, preventing the bad URL from ever reaching the batcher.

2. Soften process.on('unhandledRejection') in src/index.ts to log and
   continue instead of calling process.exit(1). Defence in depth: if any
   other async error escapes a try/catch, the server stays up.

Test: test/notify-non-wss-relay-crash.test.ts — starts an ephemeral
server, creates a confirmed subscription, POSTs with http://127.0.0.1:9,
expects 400 with an error message.
2026-09-18 10:39:04 -04:00