Changes between Initial Version and Version 7 of Ticket #37198


Ignore:
Timestamp:
Jul 27, 2026, 7:22:22 AM (3 weeks ago)
Author:
Sarah Boyce
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #37198

    • Property Owner set to Vishy
    • Property Triage Stage UnreviewedReady for checkin
    • Property Status newassigned
    • Property Has patch set
  • Ticket #37198 – Description

    initial v7  
    1 content_disposition_header() is meant to emit the percent-encoded
    2 filename*=utf-8'' form for any filename that is not a valid RFC 9110
    3 quoted-string, and to only use the bare quoted form for filenames that
    4 are. Its check has a blind spot for a trailing newline:
     1`content_disposition_header()` is meant to emit the percent-encoded `filename*=utf-8''` form for any filename that is not a valid RFC 9110 quoted-string, and to only use the bare quoted form for filenames that are. Its check has a blind spot for a trailing newline:
    52
    63{{{
     
    1411
    1512
    16 The returned value contains a raw newline, so setting it as a header
    17 raises BadHeaderError (Django responses), and boto3/http.client raises
    18 ValueError("Invalid header value ...") — an uncaught 500 for anyone
    19 serving a user-supplied filename that ends in a newline. (A newline
    20 *elsewhere* in the filename is handled correctly.)
     13The returned value contains a raw newline, so setting it as a header raises `BadHeaderError (Django responses)`, and `boto3/http.client` raises `ValueError("Invalid header value ...")` — an uncaught 500 for anyone serving a user-supplied filename that ends in a newline. (A newline *elsewhere* in the filename is handled correctly.)
    2114
    2215Root cause
     
    3124
    3225
    33 In Python, "$" matches at the end of the string *or immediately before
    34 a trailing "\n"*. So a filename of quotable characters plus one
    35 trailing newline matches, takes the quoted-string branch, and is
    36 emitted verbatim. This is the same class of bug as CVE-2021-32052
    37 (URLValidator "$" accepting a trailing newline), after which validators
    38 were switched to "\Z".
     26In Python, `"$"` matches at the end of the string *or immediately before a trailing "\n"*. So a filename of quotable characters plus one trailing newline matches, takes the quoted-string branch, and is emitted verbatim. This is the same class of bug as CVE-2021-32052 (URLValidator "$" accepting a trailing newline), after which validators were switched to "\Z".
    3927
    4028This was introduced with the control-character handling in #36023.
     
    5139
    5240
    53 Verified this sends "report.pdf\n" and "\n" to the filename*=utf-8''
    54 branch while leaving all other filenames (e.g. "report.pdf",
    55 "my report.png") on the existing quoted-string branch. A regression
    56 case should be added to ContentDispositionHeaderTests in
    57 tests/utils_tests/test_http.py, e.g.:
     41Verified this sends "report.pdf\n" and "\n" to the `filename*=utf-8''` branch while leaving all other filenames (e.g. "report.pdf", "my report.png") on the existing quoted-string branch. A regression case should be added to ContentDispositionHeaderTests in tests/utils_tests/test_http.py, e.g.:
    5842
    5943{{{
Back to Top