Repository navigation
Enforce max_fields and max_part_size in FormParser - #3329
Conversation
The limits accepted by `request.form()` were only applied to `multipart/form-data`. Forward them to `FormParser` and enforce them while parsing `application/x-www-form-urlencoded` bodies, matching the `MultiPartParser` defaults (`max_fields=1000`, `max_part_size=1MB`).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ffeda64b56
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| field_count += 1 | ||
| if field_count > self.max_fields: | ||
| raise MultiPartException(f"Too many fields. Maximum number of fields is {self.max_fields}.") |
There was a problem hiding this comment.
Enforce max_fields during parser callbacks
When the URL-encoded body arrives as one ASGI receive chunk (for example via TestClient, some servers, or after request.body() cached it), QuerystringParser.write() invokes all callbacks before this loop drains self.messages. A request with far more than max_fields tiny fields is therefore fully tokenized and buffered in self.messages before this field_count check can raise, so the new security limit does not bound CPU/memory in that common chunking scenario. Count and reject in the callback path (or otherwise process messages while write() is running) so parsing stops as soon as the limit is crossed.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
I should really enforce this in python-multipart.
starlette 1.3.1 enforces max_fields/max_part_size for urlencoded form bodies (Kludex/starlette#3329, #3331), closing the memory-pressure DoS on Form() endpoints (dependabot alert #223). Crosses starlette's 1.0 boundary: none of the removed pre-1.0 APIs (on_event, route decorators, Jinja2Templates signatures) are used here. fastapi 0.137.0 turned router.routes into a tree — nothing iterates it. Co-Authored-By: Claude Fable 5 <[email protected]>
…tipart 0.0.27->0.0.31, pytest 9.0.2->9.0.3 CVE-2026-48710 (BadHost) pin was already in place at 1.0.1; starlette 1.3.1 additionally fixes CVE-2026-54283 (request.form() limits silently ignored for application/x-www-form-urlencoded, DoS, CVSS 7.5, fixed upstream in Kludex/starlette#3329). Bump applies to every extra that pins starlette directly (mcp, computer-use, web, dev) plus the matching lazy_deps.py entries for tool.dashboard and tool.computer_use, and the test asserting that lazy-install pin. python-multipart bumped in lockstep (web extra) to the version paired with starlette 1.3.1 in the regenerated lock. pytest bumped to the latest 9.0.x patch (dev extra). This diff had been sitting uncommitted in the working tree since it was first applied, surviving multiple hermes update autostash/restore cycles without ever landing on origin/main.
request.form()acceptsmax_fieldsandmax_part_size, but these were only forwarded toMultiPartParser. Forapplication/x-www-form-urlencodedbodies,FormParserwas constructed without them and had no field-count or field-size checks, so the limits had no effect.This forwards both limits to
FormParserand enforces them while parsing, raisingMultiPartException(returned as400inside an app) when a body exceeds them. Defaults matchMultiPartParser:max_fields=1000,max_part_size=1MB.AI Disclaimer
This PR was developed with the assistance of either Claude or Codex. I've reviewed and verified the changes.