Date: 2026-06-17 Target: python-multipart 0.0.31 (fresh cluster, disclosed ~2026-06-16) Verdict: WALK CLEAN.
; -> CPU DoS (fix@0.0.30); treated as querystring field separator -> param smuggling (fix@0.0.30); is NO LONGER a separator.a=1;b=2&c=3 -> [(a, '1;b=2'),(c,'3')]. Both the quadratic and the smuggling die from the same change.parse_options_header does if "*" in key: continue - a BROAD check catching every RFC 2231/5987 formfilename*=, filename*0*=, continuations). Keeps the plain key authoritative. No smuggle.int(content_length); if content_length < 0: raise ValueError. (non-numeric -> ValueError, acceptable 400.)The querystring fix wouldn't touch _parseparam (the SEPARATE header-options ;-splitter, vendored from stdlib
email._parseparam) which has an inner quote-counting loop s.count('"', ind, end) over a growing string -> I
hypothesized an O(n^2) sibling on an unclosed-quote + many-semicolons Content-Type/Disposition header
(attacker-controlled in uploads). EMPIRICAL timing: LINEAR (2k->16k semicolons = 2.5ms->19.7ms, ratio ~2x per
2x; N quoted params likewise exactly 2x). The windowed counts stay small per iteration -> no quadratic. Walk.
that ALSO splits on the same delimiter." Here that sibling (_parseparam) existed and was a plausible O(n^2) by
inspection - but EMPIRICAL doubling-test disproved it (windowed counts != growing-range counts). Don't claim a
complexity bug from code-shape alone; the constant-vs-quadratic distinction needs the timing test (mirrors the
Django Truncator cycle where the timing CONFIRMED, here it REFUTED). Same method, opposite outcome = honest.
coordinated thorough disclosure -> swept. Fresh != automatically low-scrutiny. Cluster #1 of today's fresh hunt.