Skip to content

fix(server): validate X-Request-Id charset, not just length (#314)#315

Merged
CryptoJones merged 1 commit into
masterfrom
fix/x-request-id-charset-validation
May 19, 2026
Merged

fix(server): validate X-Request-Id charset, not just length (#314)#315
CryptoJones merged 1 commit into
masterfrom
fix/x-request-id-charset-validation

Conversation

@CryptoJones
Copy link
Copy Markdown
Owner

Closes #314.

Summary

Tightens genReqId in server.js (and the duplicated logic in tests/api/request-id.test.js) to validate the incoming X-Request-Id against the W3C trace-id alphabet — printable ASCII [0x21..0x7e], no spaces, no controls. Anything outside falls back to a UUID.

Old behavior accepted whitespace (smuggling foothold) and control chars (Node's setHeader throws → unhandled 500).

Test plan

  • npm run lint && npm test — 780 passing (was 778). 1 new case covering the whitespace/tab path; the existing oversized + missing tests still cover length boundaries.

Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/

The `genReqId` in server.js + the duplicated test scaffolding in
tests/api/request-id.test.js previously trusted any incoming
`X-Request-Id` header whose length was 1..128 chars. That includes:

  - Whitespace (`"has spaces"`) — accepted by Node's setHeader but
    invalid per RFC 7230 token grammar and a real foothold for
    header-continuation smuggling through proxies that don't
    sanitize.
  - Tabs / control chars / CR / LF — these cause Node's
    `res.setHeader` to throw `ERR_INVALID_CHAR`. Inside the
    pino-http request hook, that throw surfaces as an unhandled
    500 from a header the client controlled. Worse, the absence
    of a proper request id in the response makes the resulting
    server log line harder to correlate.

Tighten the guard to the W3C trace-id alphabet (printable ASCII
[0x21..0x7e], no spaces, no controls). Anything outside that range
falls through to the UUID fallback the same way an empty or
oversized incoming value already does.

Tests:
  - Replace the inline `genReqId` in tests/api/request-id.test.js
    to mirror the new logic (the duplication is documented in the
    file — it stands up its own app harness to isolate the hook).
  - Add a test for the whitespace + tab cases that drive the new
    regex branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@CryptoJones CryptoJones merged commit 09e9f5c into master May 19, 2026
3 checks passed
@CryptoJones CryptoJones deleted the fix/x-request-id-charset-validation branch May 19, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

X-Request-Id: validate charset, not just length (control chars cause 500 in res.setHeader)

1 participant