[API] Reject unknown routes, non-GET SPA fallback, and lenient integer parsing - #168
Merged
Conversation
…ient integer parsing (#137) - createApiRouter now ends with a terminal handler returning a JSON 404 for any method/path under /api that no route matched, instead of falling through to the SPA HTML. - Extract the SPA fallback into createSpaFallback() so it can be unit tested; it now answers only GET/HEAD and returns a plain 404 for other methods (POST/PUT/DELETE/...). - parseSyncLimit/parseAfterId in routes.ts and clampInteger in config.ts now reject values with trailing garbage (e.g. "50abc", "9001x") instead of silently truncating them with parseInt.
Mirror the production wiring in index.ts, where a rate limiter always sits in front of the SPA fallback's sendFile() call, so the test app does not expose an unbounded filesystem-reading handler.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



📖 Description
Three small, related hygiene gaps in the server:
/api/*routes returned SPA HTML with 200. A request likeGET /api/nopefell through the API router, missedexpress.static, and landed in the catch-allsendFile(index.html), so a caller expecting JSON got a200 text/htmlresponse instead of a404.app.use,POST /,PUT /whatever,DELETE /xall received the HTML shell with200instead of an error status.Number.parseInt('50abc', 10)returns50, solimit=50abc/afterId=7xyzwere silently accepted by/api/bootstrapand/api/resync, andPORT=9001xwas silently accepted byclampIntegerinconfig.ts.Fixes:
createApiRouter(src/server/routes.ts) now ends with a terminalrouter.use(...)handler that returns a JSON404 { "error": "Not found" }for any method/path under/apithat no route above matched.createSpaFallback()(src/server/spaFallback.ts) so it is independently unit-testable; it now answers onlyGET/HEADand returns a plain404(no body) for other methods.src/server/index.tsnow wires this in instead of an inline handler.parseStrictNonNegativeInt()helper inroutes.tsrequires the raw query value to match/^\d+$/before parsing, and is used byparseSyncLimit/parseAfterId.clampInteger()insrc/server/config.tsapplies the same pre-check (with a[config]warning) before falling back to the default, so an invalid env value is rejected rather than truncated.🎫 Issues
Closes #137
👩💻 Reviewer Notes
createSpaFallback()mirrors the existingcreateHostValidator()pattern (small middleware factory extracted into its own module) so the previously-untested inline catch-all inindex.tscan be exercised directly.POST /api/healthnow hits the new terminal 404 handler (covered by a new test) since onlyGETwas ever registered for that route.📑 Test Plan
src/server/routes.test.ts: added adescribe('unknown /api routes', ...)block covering a JSON 404 for an unmatched/apipath and for a non-GET method on a known route; added regression tests forlimit=50abcandafterId=7xyzreturning 400 (issue API hygiene: unknown /api/* routes return SPA HTML with 200, fallback answers all HTTP methods, numeric params accept trailing garbage #137).src/server/spaFallback.test.ts(new): covers servingindex.htmlforGET/HEAD, and a bodyless404forPOST/PUT/DELETE(issue API hygiene: unknown /api/* routes return SPA HTML with 200, fallback answers all HTTP methods, numeric params accept trailing garbage #137).src/server/config.test.ts: added a regression test assertingPORT=9001xfalls back to the default9001(issue API hygiene: unknown /api/* routes return SPA HTML with 200, fallback answers all HTTP methods, numeric params accept trailing garbage #137).npm test(147 passed),npm run typecheck, andnpm run buildlocally.✅ Checklist
General
npm testandnpm run typechecklocally and they pass.Server-specific (
src/server)src/server(tailer, parser, buffer, routes, SSE hub, config, etc.).src/server/types.tsin sync withsrc/client/state.tsfor any shared payload change (see ARCHITECTURE.md) — not applicable, no shared payload shapes changed.⏭ Next Steps
None.