server.js's genReqId trusts any incoming X-Request-Id header whose length is 1..128 chars. That includes:
- Whitespace (
"has spaces") — accepted by Node's setHeader but invalid per RFC 7230 token grammar; foothold for header-continuation smuggling through proxies.
- Tabs / control chars / CR / LF — Node's
res.setHeader throws ERR_INVALID_CHAR. Inside the pino-http request hook, that throw surfaces as an unhandled 500 from a client-controlled header.
Fix: tighten the guard to printable ASCII [0x21..0x7e] (no spaces, no controls — the W3C trace-id alphabet). Anything outside that range falls through to the UUID fallback the same way an empty or oversized incoming value already does.
Update both server.js and the test-scaffolding genReqId in tests/api/request-id.test.js (which duplicates the logic to isolate the hook).
Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/
server.js'sgenReqIdtrusts any incomingX-Request-Idheader whose length is 1..128 chars. That includes:"has spaces") — accepted by Node's setHeader but invalid per RFC 7230 token grammar; foothold for header-continuation smuggling through proxies.res.setHeaderthrowsERR_INVALID_CHAR. Inside the pino-http request hook, that throw surfaces as an unhandled 500 from a client-controlled header.Fix: tighten the guard to printable ASCII [0x21..0x7e] (no spaces, no controls — the W3C trace-id alphabet). Anything outside that range falls through to the UUID fallback the same way an empty or oversized incoming value already does.
Update both
server.jsand the test-scaffoldinggenReqIdintests/api/request-id.test.js(which duplicates the logic to isolate the hook).Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/