VYPR
High severity7.5NVD Advisory· Published Dec 2, 2024· Updated Apr 15, 2026

CVE-2024-53981

CVE-2024-53981

Description

python-multipart is a streaming multipart parser for Python. When parsing form data, python-multipart skips line breaks (CR \r or LF \n) in front of the first boundary and any tailing bytes after the last boundary. This happens one byte at a time and emits a log event each time, which may cause excessive logging for certain inputs. An attacker could abuse this by sending a malicious request with lots of data before the first or after the last boundary, causing high CPU load and stalling the processing thread for a significant amount of time. In case of ASGI application, this could stall the event loop and prevent other requests from being processed, resulting in a denial of service (DoS). This vulnerability is fixed in 0.0.18.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
python-multipartPyPI
< 0.0.180.0.18

Patches

2
c4fe4d3cebc0

Don't warn when CRLF is found after last boundary (#193)

https://github.com/Kludex/python-multipartMarcelo TrylesinskiDec 1, 2024via ghsa
5 files changed · +36 2
  • CHANGELOG.md+4 0 modified
    @@ -1,5 +1,9 @@
     # Changelog
     
    +## 0.0.19 (2024-11-30)
    +
    +* Don't warn when CRLF is found after last boundary on `MultipartParser` [#193](https://github.com/Kludex/python-multipart/pull/193).
    +
     ## 0.0.18 (2024-11-28)
     
     * Hard break if found data after last boundary on `MultipartParser` [#189](https://github.com/Kludex/python-multipart/pull/189).
    
  • python_multipart/__init__.py+1 1 modified
    @@ -2,7 +2,7 @@
     __author__ = "Andrew Dunham"
     __license__ = "Apache"
     __copyright__ = "Copyright (c) 2012-2013, Andrew Dunham"
    -__version__ = "0.0.18"
    +__version__ = "0.0.19"
     
     from .multipart import (
         BaseParser,
    
  • python_multipart/multipart.py+4 0 modified
    @@ -1397,6 +1397,10 @@ def data_callback(name: CallbackName, end_i: int, remaining: bool = False) -> No
                         i -= 1
     
                 elif state == MultipartState.END:
    +                # Don't do anything if chunk ends with CRLF.
    +                if c == CR and i + 1 < length and data[i + 1] == LF:
    +                    i += 2
    +                    continue
                     # Skip data after the last boundary.
                     self.logger.warning("Skipping data after last boundary")
                     i = length
    
  • scripts/check+1 1 modified
    @@ -6,5 +6,5 @@ SOURCE_FILES="python_multipart multipart tests"
     
     uvx ruff format --check --diff $SOURCE_FILES
     uvx ruff check $SOURCE_FILES
    -uvx --with types-PyYAML mypy $SOURCE_FILES
    +uv run mypy $SOURCE_FILES
     uvx check-sdist
    
  • tests/test_multipart.py+26 0 modified
    @@ -1,5 +1,6 @@
     from __future__ import annotations
     
    +import logging
     import os
     import random
     import sys
    @@ -9,6 +10,7 @@
     from typing import TYPE_CHECKING, cast
     from unittest.mock import Mock
     
    +import pytest
     import yaml
     
     from python_multipart.decoders import Base64Decoder, QuotedPrintableDecoder
    @@ -1248,6 +1250,30 @@ def on_file(f: FileProtocol) -> None:
             f = FormParser("multipart/form-data", on_field=Mock(), on_file=on_file, boundary="boundary")
             f.write(data.encode("latin-1"))
     
    +    @pytest.fixture(autouse=True)
    +    def inject_fixtures(self, caplog: pytest.LogCaptureFixture) -> None:
    +        self._caplog = caplog
    +
    +    def test_multipart_parser_data_end_with_crlf_without_warnings(self) -> None:
    +        """This test makes sure that the parser does not handle when the data ends with a CRLF."""
    +        data = (
    +            "--boundary\r\n"
    +            'Content-Disposition: form-data; name="file"; filename="filename.txt"\r\n'
    +            "Content-Type: text/plain\r\n\r\n"
    +            "hello\r\n"
    +            "--boundary--\r\n"
    +        )
    +
    +        files: list[File] = []
    +
    +        def on_file(f: FileProtocol) -> None:
    +            files.append(cast(File, f))
    +
    +        f = FormParser("multipart/form-data", on_field=Mock(), on_file=on_file, boundary="boundary")
    +        with self._caplog.at_level(logging.WARNING):
    +            f.write(data.encode("latin-1"))
    +            assert len(self._caplog.records) == 0
    +
         def test_max_size_multipart(self) -> None:
             # Load test data.
             test_file = "single_field_single_file.http"
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.