fix(api): harden the anonymous public surface (system info, rate limits, form DoS) - #1065
Draft
mmcintosh wants to merge 1 commit into
Draft
fix(api): harden the anonymous public surface (system info, rate limits, form DoS)#1065mmcintosh wants to merge 1 commit into
mmcintosh wants to merge 1 commit into
Conversation
Three fixes to the unauthenticated surface: - Info disclosure: GET /api/system/stats (total user/content/media counts) and GET /api/system/env (which bindings/integrations are configured) now require authentication. /health, /info, /ping stay public for probes. - Rate limiting: POST /api/forms/:id/submit (20/min/IP) and POST /api/events (60/min/IP) are now per-IP rate-limited, bounding anonymous write amplification. (The limiter fail-open-skips when CACHE_KV is unbound, as before.) - Form-submit DoS: reject bodies over 512KB by Content-Length before parsing, and bound the recursive sanitizer (depth 32, 10k nodes) — a hostile deeply nested / oversized JSON payload now returns 413 instead of exhausting the stack or CPU. Deferred (noted in the review, not in this PR): stripping error.message from public read handlers (low-severity, shared error handling) and core account lockout (entangled with the security-audit plugin). Tests: api-system anon-401 cases; public-forms depth/node/size 413 cases; E2E spec 110. Full core suite green.
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.
Summary
Hardens the anonymous, unauthenticated public surface. Three independent fixes:
1. Info disclosure on
/api/system/*GET /api/system/statsreturned aggregate content/media counts and the total user count;GET /api/system/envrevealed which bindings/integrations are configured (DB, cache, R2, email queue, SendGrid, Cloudflare Images) — an infrastructure fingerprint. Both were anonymous. They now require authentication./health,/info, and/pingstay public for probes.2. Missing rate limits on public writes
The per-IP rate limiter was applied only to auth routes. Added it to:
POST /api/forms/:id/submit— 20/min/IPPOST /api/events— 60/min/IP(The limiter fail-open-skips when
CACHE_KVis unbound, unchanged behavior.)3. Form-submit DoS
POST /api/forms/:id/submitparsed an unbounded body and then ran a depth-unbounded recursive sanitizer — a stack-exhaustion / CPU DoS from a hostile deeply-nested or oversized JSON payload. Now:Content-Length) are rejected before parsing (413)413and nothing is persistedDeferred (called out in the review, not in this PR)
error.messagefrom public read handlers (low severity; shared error handling — wants its own pass).Tests
api-system.test.ts—/statsand/envreturn401for anonymous callers.public-forms.test.ts— deep-nesting, too-many-fields, and oversized-Content-Lengthsubmissions return413(and persist nothing); a normal submission still succeeds.tests/e2e/110-public-surface-hardening.spec.ts— end-to-end anon401on stats/env,/healthstays public, oversized submit413.