No length limits on any request body #64
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Severity: HIGH
The bug
No request body field has a length limit. Grepped every route module for
max_length,constr,Field(,min_length— zero matches.CreateCommentBody.bodyis a barestr; same for notes,rationale, and evidence notes. The DB columns are
Text(unbounded in SQLite) and the migrationadds no
CheckConstraint. FastAPI/Starlette impose no default body cap either.Impact
An authenticated user POSTs a 500 MB comment; it is buffered in memory and written to SQLite.
Repeat to exhaust disk — the same disk holding the photo archive and the database. Under WAL, a
full disk risks write failures across the app.
More insidiously, a few multi-megabyte comments make
GET /api/photos/{id}/commentsenormous,and the frontend renders every comment unconditionally with no pagination
(
ReviewWorkspacePage.tsx:311-316) — hanging every reviewer's browser on that photo. Commentdeletion is own-or-admin, so an admin must intervene.
Fix
body: str = Field(..., min_length=1, max_length=10_000)and equivalents on notes, rationale,and evidence notes.
Done when
References
backend/app/api/routes/photos.py:198-200,225-228,289-294,388-394,467-468frontend/src/pages/ReviewWorkspacePage.tsx:311-316Fixed in
791d117. CI green.Two layers, because they address different failure modes.
Transport cap (
BodySizeLimitMiddleware): 1 MiB for JSON, a separate larger allowance for/api/ingest. Written as pure ASGI rather thanBaseHTTPMiddlewarespecifically so it can stopreading mid-stream — checking after the fact would still force the memory allocation the limit
exists to prevent. Declared
Content-Lengthis rejected up front; a chunked body with no declaredlength is counted during receive.
Honest limitation, documented in the code: for a chunked body the overrun is only detectable
mid-stream, when the app may already be producing a response, so a clean 413 is not always
injectable. The middleware disconnects instead. The request fails either way, and the property that
matters — the oversized payload is never fully buffered — holds.
Field ceilings: notes 20,000; rationale 10,000; comments 10,000 with
min_length=1so emptycomments are refused too.
Comment pagination is not done here — it belongs with the API work in #76 and the UI in v0.3.0.
The field ceiling removes the "one comment hangs every browser" case; a large number of comments
is still unpaginated.