Opened 3 weeks ago

Last modified 4 hours ago

#37445 assigned Bug

Invalid Content-Type header returns HTTP 500 instead of 400

Reported by: Oliver Haas Owned by: Oliver Haas
Component: HTTP handling Version: 6.1
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description

#36991 made HttpRequest._set_content_type_params() raise BadRequest for an invalid Content-Type header; before it was LookupError.

But according to the ​docs, the behavior and intention of the change was to my understanding to get a 400 response, but current main still responds with a 500, e.g. just with

curl -i -X POST http://127.0.0.1:8000/ -H "Content-Type: text/plain; charset*=BOGUS''%20"

This isn't really a regression, more like an incomplete fix.

Basically the exceptions are raised when the request is created and not inside the get_response(), so it slips by before the aptly named convert_exception_to_response() is used.

I'm intending to open a pull request shortly after this.

'Disclaimer'
I used Claude Code to investigate this bug. I saw this (super rare) error in our production env, probably coming from a security scanner. Mostly harmless, but also thought I could contribute here with a quick fix.

Change History (4)

comment:1 by Sarah Boyce, 3 weeks ago

Summary: Invalid Content-Type header (still) returns HTTP 500 instead of 400 → Invalid Content-Type header returns HTTP 500 instead of 400
Triage Stage: Unreviewed → Accepted

Thank you for the report, replicated
Feel free to submit a PR!

Possible test below

  • tests/handlers/tests.py

    a b class HandlerTests(SimpleTestCase):  
    9393        # Expect "bad request" response
    9494        self.assertEqual(response.status_code, 400)
    9595
     96    def test_invalid_content_type_returns_bad_request(self):
     97        environ = self.request_factory.post("/malformed_post/").environ
     98        environ["CONTENT_TYPE"] = "text/plain; charset*=BOGUS''%20"
     99        handler = WSGIHandler()
     100        response = handler(environ, lambda *a, **k: None)
     101        self.addCleanup(response.close)
     102        self.assertEqual(response.status_code, 400)
     103
    96104
    97105@override_settings(ROOT_URLCONF="handlers.urls", MIDDLEWARE=[])

comment:2 by Chiranjibee18, 12 days ago

I have submitted a pull request with the fix and regression test:https://github.com/django/django/pull/22224

comment:3 by Chiranjibee18, 12 days ago

Has patch: set

in reply to:  2 comment:4 by Oliver Haas, 4 hours ago

Replying to Chiranjibee18:

I have submitted a pull request with the fix and regression test:https://github.com/django/django/pull/22224

I think this is a correct minimal fix.

Ideally the code surrounding it would be refactored though in the future. asgi and wsgi approach do not really agree here, and sometimes request parsing errors are not raised when parsing the request and just handled later, while other request parsing errors raise.

Last edited 4 hours ago by Oliver Haas (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top