fix(security): declare 204 response schema on PATCH /session - #295
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
birme
left a comment
There was a problem hiding this comment.
Code Review
Verdict: LGTM
Summary: A correct, minimal fix that aligns the TypeBox response schema (204: Type.Null()) with what the handler actually sends (reply.code(204).send()), restoring response validation on the success path. A focused server.inject() regression test is included. One pre-existing schema gap on the same handler is worth noting but is out of scope for this PR.
Blocking
None.
Warnings
src/api_productions.ts:759(schema block, ~line 782 in file) — The handler has a not-found branch that sendsreply.code(410)(session-not-found), but the response schema still declares only204/400/500. This is the exact class of undeclared-status-code / validation-bypass bug this PR fixes — the 410 path currently falls back to the default serializer and is unvalidated. It predates this PR and is not strictly in scope, but since the PR is specifically hardening this handler's response schema, adding410: Type.Object({ message: Type.String() })here would fully close the gap. At minimum, file a follow-up so it isn't lost.
Suggestions
src/api_validation.test.ts:243-259— Good regression test asserting bothstatusCode === 204and empty body. Consider a companion assertion that nocontent-type: application/jsonbody is serialized (204 responses should carry no body), and optionally a test covering the410not-found path to lock in the observation above.src/api_validation.test.ts:96-101— The addedrequireLinemock throwing on miss is a reasonable fidelity improvement, though it usesany[]typing consistent with the surrounding mock object; acceptable in a test double.
Domain Note
This change affects WHIP/WHEP session lifecycle (the PATCH /session SDP-answer finalization step). The change is a schema/status-code correction only and does not alter SDP handling or handleAnswerRequest behavior, so no broadcast-domain risk. If the 410 follow-up is pursued, the intercom-expert agent can confirm the correct client-facing status for a stale/expired session.
birme
left a comment
There was a problem hiding this comment.
Code Review
Verdict: LGTM
Summary: The core fix is correct, minimal, and precisely matches the remediation described in #289: the undeclared success status (200: Type.String()) is replaced with the actual 204: Type.Null() that the handler sends, restoring Fastify's schema-driven serialization/validation on the success path. A well-targeted regression test using server.inject() asserts both the 204 status and the empty body. No Blocking issues. One Warning noted below is pre-existing and adjacent, not introduced by this PR.
Blocking
None.
Warnings
src/api_productions.ts:781-785— ThePATCH /session/:sessionIdhandler also sendsreply.code(410).send({ message: ... })for the not-found case, but410is still not declared in theresponseschema (only204/400/500are). This is the exact same class of undeclared-status validation bypass that #289 targets: the410object response goes out unvalidated and with a default serializer. Since the aim of this PR is to close a response-schema-mismatch gap on this specific route, consider adding410: Type.Object({ message: Type.String() })in the same change (or track it as a fast follow). Note this response is also an object while400/500areType.String()— worth keeping error shapes consistent.
Suggestions
src/api_validation.test.ts:239-260— Good coverage of the success path. Consider a companion test asserting the410branch (session not found) returns410with amessagebody; combined with the schema fix above it would lock down the other undeclared-status path on this route.src/api_validation.test.ts:96-101— The newrequireLinemock correctly mirrors the handler's throw-on-missing contract; nicely done. Minor: the surrounding mocks useany[]/anywhich is consistent with the existing file style, so no action needed.
Domain Note
This change affects the WHIP/WHEP-adjacent session lifecycle (PATCH /session/:sessionId finalizes the connection via handleAnswerRequest). The change is schema-only and does not alter SDP handling or the 204 semantics on the wire, so no broadcast-domain regression is expected. If the 410 follow-up is pursued, consider consulting the intercom-expert agent to confirm 410 Gone remains the intended semantic for an expired/absent session.
|
daily-backlog-pr: code-reviewer verdict is LGTM and all CI checks pass, but this run cannot merge. The only available GitHub identity in this run ( |
birme
left a comment
There was a problem hiding this comment.
Code Review
Verdict: LGTM
Summary: A tight, correctly-scoped security fix. The PATCH /session/:sessionId response schema now declares 204: Type.Null() matching the handler's reply.code(204).send(), closing the validation-bypass gap where an undeclared 204 status caused Fastify to apply the wrong serializer and skip response validation. A focused regression test asserts the 204 + empty-body contract on the success path. No Blocking issues.
Blocking
None.
Warnings
None. (See Suggestions for a related, pre-existing schema gap that is out of scope for this PR.)
Suggestions
src/api_productions.ts:781-784— The same handler has an undeclared410response path (reply.code(410).send({ message: ... })) that is not present in theresponseschema (204/400/500only). This is the identical class of issue #289 addresses: an undeclared status code means Fastify applies no schema-specific serializer/validation for that branch, and the object shape{ message }diverges from theType.String()shape used by the other error codes. Out of scope for this PR, but worth a follow-up ticket to declare410(e.g.Type.Object({ message: Type.String() })) and align the error-body shapes. Recommend passing to bug-fixer.src/api_validation.test.ts:243-259— Good regression test. Consider also assertingContent-Length/absence of a body serializer artifact, or adding a negative test that a mistakenly-returned body on the 204 path is stripped, to lock the serializer behavior. Optional.src/api_validation.test.ts:96-100— The newly-addedrequireLinemock throws on a missing line; there is no test exercising that throw for this route (it would surface as a 500 via the catch block). Not required for this fix, but a small test would document the not-found behavior.
Domain Note
This change affects the WHEP/WebRTC session lifecycle (PATCH /session finalizes the SDP answer via handleAnswerRequest). The change is purely at the HTTP response-schema layer and does not alter SDP handling, so broadcast-domain risk is negligible. No intercom-expert consultation needed for this diff.
Notes for the record:
- Backend test hygiene satisfied:
jest.mock('./log', ...)is present atsrc/api_validation.test.ts:1; the test usesserver.inject()for the route (integration-style), and mocksgetSession,requireProduction, andhandleAnswerRequest. - No
anycasts, non-null assertions, DB-without-withRetry, SDP-string-manipulation, index-based media access, hardcoded secrets, or yarn/npm-migration regressions introduced by this diff. - CI: all four checks (Linting, Prettier, Type Check, Unit Tests) are green.
Next steps: none blocking. Optionally file a follow-up for the undeclared
410schema path and pass to bug-fixer.
|
daily-backlog-pr (Phase 3): code review verdict is LGTM and CI is green, but this PR cannot be auto-merged — the review bot account ( |
Summary
PATCH /session/:sessionIdhandler sendsreply.code(204).send()on success, but the TypeBoxresponseschema only declared200: Type.String(). Because 204 was undeclared, Fastify applied the wrong serializer and skipped response validation for the success path (a validation bypass).200: Type.String()entry with204: Type.Null(), keeping the existing400/500entries. Minimal one-line schema change.PATCH /session/:sessionIdreturns204with an empty body on success (backend tests mock./log).Test plan
npm test) — 244 passednpm run typecheck)npm run lint) — 0 errorsCloses #289
🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.6 noreply@anthropic.com