Skip to main content

python-multipart CVE-2026-53540

| EUVDEUVD-2026-38325 LOW
Improper Validation of Specified Quantity in Input (CWE-1284)
2026-06-15 https://github.com/Kludex/python-multipart GHSA-v9pg-7xvm-68hf
3.7
CVSS 3.1 · Vendor: https://github.com/Kludex/python-multipart

Severity by source

Vendor (https://github.com/Kludex/python-multipart) PRIMARY
3.7 LOW
AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
vuln.today AI
3.7 LOW

AC:H reflects the non-default deployment pattern required; A:L captures memory pressure rather than full service loss; C:N and I:N because no data is disclosed or modified.

3.1 AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:L
4.0 AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/Kludex/python-multipart).

CVSS VectorVendor: https://github.com/Kludex/python-multipart

Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
Low

Lifecycle Timeline

2
Source Code Evidence Fetched
Jun 15, 2026 - 20:55 vuln.today
Analysis Generated
Jun 15, 2026 - 20:55 vuln.today

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 17 pypi packages depend on python-multipart (13 direct, 4 indirect)

Ecosystem-wide dependent count for version 0.0.31.

DescriptionCVE.org

Summary

parse_form() did not validate the Content-Length header before using it to bound its chunked read of the request body. A negative Content-Length turned the bounded read into a read-until-EOF, so the entire body was loaded into memory in a single read instead of in fixed-size chunks.

Details

parse_form() reads the input stream in chunks, never reading more than the remaining Content-Length at a time. The per-chunk size is computed as min(content_length - bytes_read, chunk_size). The header value was parsed to an integer without checking its sign, so a Content-Length of -1 made this expression negative, and input_stream.read(-1) reads until end of stream. The intended bounded, chunked read therefore collapsed into a single unbounded read of the whole stream. The amount read is still bounded by what the client actually sends.

Impact

This only affects code that calls parse_form() directly with a Content-Length header taken from attacker-controlled input and without normalizing a negative value first. No known package is affected:

  • Starlette and FastAPI drive MultipartParser directly from the ASGI receive() stream and do not call parse_form().
  • Known parse_form() consumers either do not forward Content-Length to it, recompute it from the already-read body, or run behind a layer (such as Werkzeug) that normalizes a negative Content-Length to 0.

The realistic exposure is limited to bespoke WSGI or http.server handlers that forward raw client headers into parse_form(). In that case a crafted request buffers the body in memory at once, degrading availability under concurrent requests rather than causing a complete denial of service.

Mitigation

Upgrade to version 0.0.31 or later, which rejects a negative Content-Length with a ValueError before reading the stream.

AnalysisAI

Memory exhaustion in python-multipart's parse_form() function allows a remote attacker to force unbounded body buffering by supplying a negative Content-Length header, degrading server availability under concurrent load. Affected deployments are narrowly scoped: only bespoke WSGI or http.server handlers that pass raw, unvalidated client-supplied Content-Length values directly into parse_form(). Mainstream consumers such as Starlette, FastAPI, and Werkzeug are not affected. No public exploit code exists and this vulnerability is not listed in the CISA KEV catalog, consistent with the low CVSS base score of 3.7.

Technical ContextAI

python-multipart (pkg:pip/python-multipart) provides multipart form-data and URL-encoded body parsing for Python web applications. The parse_form() function implements a chunked read loop bounded by the Content-Length header: each chunk size is computed as min(content_length - bytes_read, chunk_size). The integer parsed from the Content-Length header was never validated for sign, so a value of -1 makes the expression negative on the first iteration. Python's io.RawIOBase.read() interprets a negative argument as 'read until EOF', collapsing the intended bounded chunked loop into a single unbounded read of the entire request body into memory. The root cause maps to CWE-1284 (Improper Validation of Specified Quantity in Input): the quantity used to bound I/O was accepted without sign checking. The total bytes read is still limited by what the client actually transmits, so this is a memory pressure issue rather than an infinite loop.

RemediationAI

Upgrade python-multipart to version 0.0.31 or later, which rejects a negative Content-Length with a ValueError before any stream read occurs; this is the definitive fix per the vendor advisory at https://github.com/Kludex/python-multipart/security/advisories/GHSA-v9pg-7xvm-68hf. If an immediate upgrade is not feasible, the specific compensating control is to validate and normalize the Content-Length header before passing it to parse_form(): reject or clamp any value less than zero to 0 at the application or middleware layer. This can be implemented as a one-line guard (e.g., content_length = max(0, int(request.headers.get('Content-Length', 0)))) in the handler that calls parse_form(). A secondary control is to enforce server-side request body size limits at the reverse proxy or WSGI server level (e.g., LimitRequestBody in Apache, client_max_body_size in nginx), which caps the memory consumed per request regardless of the header value. The trade-off of the proxy limit approach is that it does not fix the root cause and may reject legitimate large uploads.

Share

CVE-2026-53540 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy