Summary
MAX_ALERT_BODY_BYTES (1 MiB) is applied only on /alerts. Sync /investigate and Clerk /api/investigations accept unbounded JSON into memory via FastAPI/Pydantic. Additionally, /alerts skips the early reject when Content-Length is missing or 0, then await request.body() buffers the full payload before the post-read check — peak memory is not bounded during read.
Expected vs actual behavior
Expected: one shared max-body limit on all mutating routes, enforced before unbounded buffering, with consistent 413 responses.
Actual: the cap lives only in gateway/http/webapp.py for /alerts (Content-Length early reject L149–159, then await request.body() + post-read check). Code search confirms only webapp.py + its test reference the constant, so /investigate and /api/investigations are uncapped; and a missing/0 Content-Length bypasses the early reject (default 0, not > MAX), buffering the full body first.
Steps to reproduce
- POST an oversized JSON body (>1 MiB) to
/investigate or /api/investigations → accepted.
- POST to
/alerts with no Content-Length header → full body buffered before the post-read size check.
Can you reproduce it consistently?
Yes
How often does it occur?
Under specific conditions (oversized / authenticated payloads)
Operating system
Other (server/gateway)
Additional context
- Apply the same (or a shared) max body size to
/investigate and /api/investigations create paths.
- Prefer a streaming / middleware size limit so a missing
Content-Length cannot allocate unbounded memory before reject.
- Keep 413 responses consistent (
payload too large / shared helper).
- Tests: oversized body → 413 on each mutating route; missing CL still capped.
Done when: authenticated oversized payloads cannot unbounded-buffer gateway request memory on alert, investigate, or investigations-create.
Summary
MAX_ALERT_BODY_BYTES(1 MiB) is applied only on/alerts. Sync/investigateand Clerk/api/investigationsaccept unbounded JSON into memory via FastAPI/Pydantic. Additionally,/alertsskips the early reject whenContent-Lengthis missing or0, thenawait request.body()buffers the full payload before the post-read check — peak memory is not bounded during read.Expected vs actual behavior
Expected: one shared max-body limit on all mutating routes, enforced before unbounded buffering, with consistent
413responses.Actual: the cap lives only in
gateway/http/webapp.pyfor/alerts(Content-Length early reject L149–159, thenawait request.body()+ post-read check). Code search confirms onlywebapp.py+ its test reference the constant, so/investigateand/api/investigationsare uncapped; and a missing/0Content-Lengthbypasses the early reject (default0, not> MAX), buffering the full body first.Steps to reproduce
/investigateor/api/investigations→ accepted./alertswith noContent-Lengthheader → full body buffered before the post-read size check.Can you reproduce it consistently?
Yes
How often does it occur?
Under specific conditions (oversized / authenticated payloads)
Operating system
Other (server/gateway)
Additional context
/investigateand/api/investigationscreate paths.Content-Lengthcannot allocate unbounded memory before reject.payload too large/ shared helper).Done when: authenticated oversized payloads cannot unbounded-buffer gateway request memory on alert, investigate, or investigations-create.