Skip to content

multipart: tolerate OWS after a quoted Content-Disposition parameter value#13056

Draft
HrachShah wants to merge 1 commit into
aio-libs:masterfrom
HrachShah:fix/parse-content-disposition-ows-after-quoted-value
Draft

multipart: tolerate OWS after a quoted Content-Disposition parameter value#13056
HrachShah wants to merge 1 commit into
aio-libs:masterfrom
HrachShah:fix/parse-content-disposition-ows-after-quoted-value

Conversation

@HrachShah

Copy link
Copy Markdown

What do these changes do?

parse_content_disposition now accepts optional whitespace (OWS) between a quoted parameter value and the ; that follows it. RFC 7230 § 3.2.3 / RFC 9110 § 5.6.6 permit OWS around the ; separator in HTTP header fields, so headers like

attachment; filename="test.txt" ; name="field"
attachment; filename="test.txt"\t; name="field"

were being misparsed: the trailing OWS made is_quoted return False, the parser fell through to the semicolon-repair heuristic, and the next parameter (name="field") was greedily consumed into the filename value.

The fix adds a second is_quoted check against value.rstrip() for the ordinary-parameter branch in aiohttp/multipart.py so OWS is stripped before the value is validated as a quoted string. The greedy-repair branch is unchanged — it is only ever reached when the value really is not a quoted string and a real follow-up token needs to be salvaged.

Are there changes in behavior for the user?

  • Content-Disposition headers with OWS after a quoted parameter value now parse the quoted value correctly and treat the next parameter as a separate entry, matching the behaviour of the browser-side Content-Disposition parser and the RFC.
  • Headers without OWS still parse identically. The repair heuristic that salvages filename=a;b is untouched.

Is it a substantial burden for the maintainers to support this?

No. The change is a one-line additional is_quoted check in the existing parameter-handling branch and a 5-test addition to tests/test_multipart_helpers.py. No new public API, no new failure modes — the additional branch is more permissive (it only matches values that already start and end with " after stripping trailing OWS).

Related issue number

Fixes #13002.

Checklist

  • Tests pass locally (PYTHONPATH=. pytest tests/test_multipart_helpers.py — 104 passed, 5 skipped, 1 unrelated pre-existing failure on test_attwithfn2231utf8comp).
  • New behaviour is covered by a test (5 new tests in TestParseContentDisposition).
  • Changelog fragment added at CHANGES/13002.bugfix.rst.
Test output
$ PYTHONPATH=. pytest tests/test_multipart_helpers.py -k "ows_after_quoted" -v
collected 110 items / 105 deselected / 5 selected

tests/test_multipart_helpers.py::TestParseContentDisposition::test_ows_after_quoted_value_single_space PASSED
tests/test_multipart_helpers.py::TestParseContentDisposition::test_ows_after_quoted_value_multiple_spaces PASSED
tests/test_multipart_helpers.py::TestParseContentDisposition::test_ows_after_quoted_value_tab PASSED
tests/test_multipart_helpers.py::TestParseContentDisposition::test_ows_after_quoted_value_no_following_param PASSED
tests/test_multipart_helpers.py::TestParseContentDisposition::test_ows_after_quoted_value_does_not_swallow_next_param PASSED
====================== 5 passed, 105 deselected in 0.17s ======================

Without the fix, all 5 new tests fail. The relevant pre-fix failure for the issue's exact reproducer:

>>> parse_content_disposition('attachment; filename="test.txt" ; name="field"')
('attachment', {'filename': 'test.txt" ; name="field'})
# post-fix:
('attachment', {'filename': 'test.txt', 'name': 'field'})

Drafted with Zo Computer; reviewed by HrachShah.

@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided There is a change note present in this PR label Jul 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

parse_content_disposition: OWS before semicolon causes greedy repair to consume the next parameter

1 participant