Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-python-multipart for 
openSUSE:Factory checked in at 2026-05-16 19:24:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-python-multipart (Old)
 and      /work/SRC/openSUSE:Factory/.python-python-multipart.new.1966 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-python-multipart"

Sat May 16 19:24:41 2026 rev:15 rq:1353179 version:0.0.28

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-python-multipart/python-python-multipart.changes
  2026-04-21 12:42:16.648087570 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-python-multipart.new.1966/python-python-multipart.changes
        2026-05-16 19:25:46.605112193 +0200
@@ -1,0 +2,10 @@
+Thu May 14 12:49:47 UTC 2026 - Daniel Garcia <[email protected]>
+
+- Update to 0.0.28 (CVE-2026-42561, bsc#1265250):
+  * Speed up partial-boundary tail scan via bytes.find by @Kludex in #281
+  * Cap multipart boundary length at 256 bytes by @Kludex in #282
+- 0.0.27:
+  * Pass parse offsets via constructors by @Kludex in #268
+  * Add multipart header limits by @Kludex in #267
+
+-------------------------------------------------------------------

Old:
----
  python_multipart-0.0.26.tar.gz

New:
----
  python_multipart-0.0.28.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-python-multipart.spec ++++++
--- /var/tmp/diff_new_pack.vTIDfU/_old  2026-05-16 19:25:47.141134172 +0200
+++ /var/tmp/diff_new_pack.vTIDfU/_new  2026-05-16 19:25:47.145134335 +0200
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-python-multipart
-Version:        0.0.26
+Version:        0.0.28
 Release:        0
 License:        Apache-2.0
 Summary:        Python streaming multipart parser

++++++ python_multipart-0.0.26.tar.gz -> python_multipart-0.0.28.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python_multipart-0.0.26/CHANGELOG.md 
new/python_multipart-0.0.28/CHANGELOG.md
--- old/python_multipart-0.0.26/CHANGELOG.md    2020-02-02 01:00:00.000000000 
+0100
+++ new/python_multipart-0.0.28/CHANGELOG.md    2020-02-02 01:00:00.000000000 
+0100
@@ -1,5 +1,15 @@
 # Changelog
 
+## 0.0.28 (2026-05-10)
+
+* Speed up partial-boundary tail scan via `bytes.find` 
[#281](https://github.com/Kludex/python-multipart/pull/281).
+* Cap multipart boundary length at 256 bytes 
[#282](https://github.com/Kludex/python-multipart/pull/282).
+
+## 0.0.27 (2026-04-27)
+
+* Add multipart header limits 
[#267](https://github.com/Kludex/python-multipart/pull/267).
+* Pass parse offsets via constructors 
[#268](https://github.com/Kludex/python-multipart/pull/268).
+
 ## 0.0.26 (2026-04-10)
 
 * Skip preamble before the first multipart boundary more efficiently 
[#262](https://github.com/Kludex/python-multipart/pull/262).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python_multipart-0.0.26/PKG-INFO 
new/python_multipart-0.0.28/PKG-INFO
--- old/python_multipart-0.0.26/PKG-INFO        2020-02-02 01:00:00.000000000 
+0100
+++ new/python_multipart-0.0.28/PKG-INFO        2020-02-02 01:00:00.000000000 
+0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: python-multipart
-Version: 0.0.26
+Version: 0.0.28
 Summary: A streaming multipart parser for Python
 Project-URL: Homepage, https://github.com/Kludex/python-multipart
 Project-URL: Documentation, https://kludex.github.io/python-multipart/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python_multipart-0.0.26/pyproject.toml 
new/python_multipart-0.0.28/pyproject.toml
--- old/python_multipart-0.0.26/pyproject.toml  2020-02-02 01:00:00.000000000 
+0100
+++ new/python_multipart-0.0.28/pyproject.toml  2020-02-02 01:00:00.000000000 
+0100
@@ -37,16 +37,17 @@
     "atomicwrites==1.4.1",
     "attrs==26.1.0",
     "coverage==7.13.5",
-    "more-itertools==11.0.1",
+    "more-itertools==11.0.2",
     "pbr==7.0.3",
     "pluggy==1.6.0",
     "py==1.11.0",
-    "pytest==9.0.2",
+    "pytest==9.0.3",
     "pytest-cov==7.1.0",
+    "pytest-codspeed>=4.1.1",
     "PyYAML==6.0.3",
-    "invoke==2.2.1",
+    "invoke==3.0.3",
     "pytest-timeout==2.4.0",
-    "ruff==0.15.9",
+    "ruff==0.15.11",
     "mypy",
     "types-PyYAML",
     "atheris==2.3.0; python_version <= '3.11'",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python_multipart-0.0.26/python_multipart/__init__.py 
new/python_multipart-0.0.28/python_multipart/__init__.py
--- old/python_multipart-0.0.26/python_multipart/__init__.py    2020-02-02 
01:00:00.000000000 +0100
+++ new/python_multipart-0.0.28/python_multipart/__init__.py    2020-02-02 
01:00:00.000000000 +0100
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-__version__ = "0.0.26"
+__version__ = "0.0.28"
 
 from .multipart import (
     BaseParser,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_multipart-0.0.26/python_multipart/exceptions.py 
new/python_multipart-0.0.28/python_multipart/exceptions.py
--- old/python_multipart-0.0.26/python_multipart/exceptions.py  2020-02-02 
01:00:00.000000000 +0100
+++ new/python_multipart-0.0.28/python_multipart/exceptions.py  2020-02-02 
01:00:00.000000000 +0100
@@ -7,9 +7,9 @@
     parsing something.
     """
 
-    #: This is the offset in the input data chunk (*NOT* the overall stream) in
-    #: which the parse error occurred.  It will be -1 if not specified.
-    offset = -1
+    def __init__(self, message: str, *, offset: int = -1) -> None:
+        super().__init__(message)
+        self.offset = offset
 
 
 class MultipartParseError(ParseError):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/python_multipart-0.0.26/python_multipart/multipart.py 
new/python_multipart-0.0.28/python_multipart/multipart.py
--- old/python_multipart-0.0.26/python_multipart/multipart.py   2020-02-02 
01:00:00.000000000 +0100
+++ new/python_multipart-0.0.28/python_multipart/multipart.py   2020-02-02 
01:00:00.000000000 +0100
@@ -54,6 +54,8 @@
     class FormParserConfig(FileConfig):
         UPLOAD_ERROR_ON_BAD_CTE: bool
         MAX_BODY_SIZE: float
+        MAX_HEADER_COUNT: int
+        MAX_HEADER_SIZE: int
 
     CallbackName: TypeAlias = Literal[
         "start",
@@ -140,6 +142,20 @@
     b"!#$%&'*+-.^_`|~")
 # fmt: on
 
+DEFAULT_MAX_HEADER_COUNT = 8
+"""Default maximum number of headers allowed per multipart part."""
+
+DEFAULT_MAX_HEADER_SIZE = 4096 + 128
+"""Default maximum size of a single multipart header line, including syntax 
overhead."""
+
+MAX_BOUNDARY_LENGTH = 256
+"""Maximum allowed length of a multipart boundary.
+
+[RFC 2046 ยง5.1.1](https://datatracker.ietf.org/doc/html/rfc2046#section-5.1.1)
+recommends boundaries be at most 70 bytes. 256 bytes is generous headroom over
+every HTTP client.
+"""
+
 
 def parse_options_header(value: str | bytes | None) -> tuple[bytes, 
dict[bytes, bytes]]:
     """Parses a Content-Type header into a value in the following format: 
(content_type, {parameters})."""
@@ -820,9 +836,7 @@
                     if found_sep:
                         # If we're parsing strictly, we disallow blank chunks.
                         if strict_parsing:
-                            e = QuerystringParseError("Skipping duplicate 
ampersand/semicolon at %d" % i)
-                            e.offset = i
-                            raise e
+                            raise QuerystringParseError("Skipping duplicate 
ampersand/semicolon at %d" % i, offset=i)
                         else:
                             self.logger.debug("Skipping duplicate 
ampersand/semicolon at %d", i)
                     else:
@@ -886,13 +900,12 @@
                         # We're parsing strictly.  If we find a separator,
                         # this is an error - we require an equals sign.
                         if sep_pos != -1:
-                            e = QuerystringParseError(
+                            raise QuerystringParseError(
                                 "When strict_parsing is True, we require an "
                                 "equals sign in all field chunks. Did not "
-                                "find one in the chunk that starts at %d" % 
(i,)
+                                "find one in the chunk that starts at %d" % 
(i,),
+                                offset=i,
                             )
-                            e.offset = i
-                            raise e
 
                         # No separator in the rest of this chunk, so it's just
                         # a field name.
@@ -927,9 +940,7 @@
             else:  # pragma: no cover (error case)
                 msg = "Reached an unknown state %d at %d" % (state, i)
                 self.logger.warning(msg)
-                e = QuerystringParseError(msg)
-                e.offset = i
-                raise e
+                raise QuerystringParseError(msg, offset=i)
 
             i += 1
 
@@ -972,10 +983,18 @@
         boundary: The multipart boundary.  This is required, and must match 
what is given in the HTTP request - usually in the Content-Type header.
         callbacks: A dictionary of callbacks.  See the documentation for 
[`BaseParser`][python_multipart.BaseParser].
         max_size: The maximum size of body to parse.  Defaults to infinity - 
i.e. unbounded.
+        max_header_count: The maximum number of headers allowed per part.
+        max_header_size: The maximum size of a single header line (excluding 
the trailing CRLF).
     """  # noqa: E501
 
     def __init__(
-        self, boundary: bytes | str, callbacks: MultipartCallbacks = {}, 
max_size: float = float("inf")
+        self,
+        boundary: bytes | str,
+        callbacks: MultipartCallbacks = {},
+        max_size: float = float("inf"),
+        *,
+        max_header_count: int = DEFAULT_MAX_HEADER_COUNT,
+        max_header_size: int = DEFAULT_MAX_HEADER_SIZE,
     ) -> None:
         # Initialize parser state.
         super().__init__()
@@ -989,12 +1008,20 @@
         self.max_size = max_size
         self._current_size = 0
 
+        self.max_header_count = max_header_count
+        self._current_header_count = 0
+
+        self.max_header_size = max_header_size
+        self._current_header_size = 0
+
         # Setup marks.  These are used to track the state of data received.
         self.marks: dict[str, int] = {}
 
         # Save our boundary.
         if isinstance(boundary, str):  # pragma: no cover
             boundary = boundary.encode("latin-1")
+        if len(boundary) > MAX_BOUNDARY_LENGTH:
+            raise FormParserError(f"Boundary length {len(boundary)} exceeds 
maximum of {MAX_BOUNDARY_LENGTH}")
         self.boundary = b"\r\n--" + boundary
 
     def write(self, data: bytes) -> int:
@@ -1042,10 +1069,18 @@
         state = self.state
         index = self.index
         flags = self.flags
+        current_header_count = self._current_header_count
+        current_header_size = self._current_header_size
 
         # Our index defaults to 0.
         i = 0
 
+        def advance_header_size(amount: int = 1) -> None:
+            nonlocal current_header_size
+            current_header_size += amount
+            if current_header_size > self.max_header_size:
+                raise MultipartParseError("Maximum header size exceeded", 
offset=i)
+
         # Set a mark.
         def set_mark(name: str) -> None:
             self.marks[name] = i
@@ -1131,9 +1166,7 @@
                         # Error!
                         msg = "Did not find CR at end of boundary (%d)" % (i,)
                         self.logger.warning(msg)
-                        e = MultipartParseError(msg)
-                        e.offset = i
-                        raise e
+                        raise MultipartParseError(msg, offset=i)
 
                     index += 1
 
@@ -1141,15 +1174,15 @@
                     if c != LF:
                         msg = "Did not find LF at end of boundary (%d)" % (i,)
                         self.logger.warning(msg)
-                        e = MultipartParseError(msg)
-                        e.offset = i
-                        raise e
+                        raise MultipartParseError(msg, offset=i)
 
                     # The index is now used for indexing into our boundary.
                     index = 0
 
                     # Callback for the start of a part.
                     self.callback("part_begin")
+                    current_header_count = 0
+                    current_header_size = 0
 
                     # Move to the next character and state.
                     state = MultipartState.HEADER_FIELD_START
@@ -1159,9 +1192,7 @@
                     if c != boundary[index + 2]:
                         msg = "Expected boundary character %r, got %r at index 
%d" % (boundary[index + 2], c, index + 2)
                         self.logger.warning(msg)
-                        e = MultipartParseError(msg)
-                        e.offset = i
-                        raise e
+                        raise MultipartParseError(msg, offset=i)
 
                     # Increment index into boundary and continue.
                     index += 1
@@ -1171,6 +1202,12 @@
                 # continue parsing our header field.
                 index = 0
 
+                if c != CR:
+                    current_header_count += 1
+                    if current_header_count > self.max_header_count:
+                        raise MultipartParseError("Maximum header count 
exceeded", offset=i)
+                    current_header_size = 0
+
                 # Set a mark of our header field.
                 set_mark("header_field")
 
@@ -1200,13 +1237,12 @@
 
                 # If we've reached a colon, we're done with this header.
                 if c == COLON:
+                    advance_header_size()
                     # A 0-length header is an error.
                     if index == 1:
                         msg = "Found 0-length header at %d" % (i,)
                         self.logger.warning(msg)
-                        e = MultipartParseError(msg)
-                        e.offset = i
-                        raise e
+                        raise MultipartParseError(msg, offset=i)
 
                     # Call our callback with the header field.
                     data_callback("header_field", i)
@@ -1217,13 +1253,14 @@
                 elif c not in TOKEN_CHARS_SET:
                     msg = "Found invalid character %r in header at %d" % (c, i)
                     self.logger.warning(msg)
-                    e = MultipartParseError(msg)
-                    e.offset = i
-                    raise e
+                    raise MultipartParseError(msg, offset=i)
+                else:
+                    advance_header_size()
 
             elif state == MultipartState.HEADER_VALUE_START:
                 # Skip leading spaces.
                 if c == SPACE:
+                    advance_header_size()
                     i += 1
                     continue
 
@@ -1240,16 +1277,17 @@
                 if c == CR:
                     data_callback("header_value", i)
                     self.callback("header_end")
+                    current_header_size = 0
                     state = MultipartState.HEADER_VALUE_ALMOST_DONE
+                else:
+                    advance_header_size()
 
             elif state == MultipartState.HEADER_VALUE_ALMOST_DONE:
                 # The last character should be a LF.  If not, it's an error.
                 if c != LF:
                     msg = f"Did not find LF character at end of header (found 
{c!r})"
                     self.logger.warning(msg)
-                    e = MultipartParseError(msg)
-                    e.offset = i
-                    raise e
+                    raise MultipartParseError(msg, offset=i)
 
                 # Move back to the start of another header.  Note that if that
                 # state detects ANOTHER newline, it'll trigger the end of our
@@ -1263,9 +1301,7 @@
                 if c != LF:
                     msg = f"Did not find LF at end of headers (found {c!r})"
                     self.logger.warning(msg)
-                    e = MultipartParseError(msg)
-                    e.offset = i
-                    raise e
+                    raise MultipartParseError(msg, offset=i)
 
                 self.callback("headers_finished")
                 state = MultipartState.PART_DATA_START
@@ -1306,14 +1342,12 @@
                         # No match found for whole string.
                         # There may be a partial boundary at the end of the
                         # data, which the find will not match.
-                        # Since the length should to be searched is limited to
-                        # the boundary length, just perform a naive search.
+                        # Since the length to be searched is limited to the
+                        # boundary length, scan the tail for boundary[0] via
+                        # bytes.find (C-level) to keep cost off the Python 
loop.
                         i = max(i, data_length - boundary_length)
-
-                        # Search forward until we either hit the end of our 
buffer,
-                        # or reach a potential start of the boundary.
-                        while i < data_length - 1 and data[i] != boundary[0]:
-                            i += 1
+                        j = data.find(boundary[:1], i, data_length - 1)
+                        i = j if j >= 0 else data_length - 1
 
                     c = data[i]
 
@@ -1364,6 +1398,8 @@
                             # a part, and are starting a new one.
                             self.callback("part_end")
                             self.callback("part_begin")
+                            current_header_count = 0
+                            current_header_size = 0
 
                             # Move to parsing new headers.
                             index = 0
@@ -1409,9 +1445,7 @@
                     if c != HYPHEN:
                         msg = "Did not find - at end of boundary (%d)" % (i,)
                         self.logger.warning(msg)
-                        e = MultipartParseError(msg)
-                        e.offset = i
-                        raise e
+                        raise MultipartParseError(msg, offset=i)
                     index += 1
                     self.callback("end")
                     state = MultipartState.END
@@ -1426,9 +1460,7 @@
                 # We got into a strange state somehow!  Just stop processing.
                 msg = "Reached an unknown state %d at %d" % (state, i)
                 self.logger.warning(msg)
-                e = MultipartParseError(msg)
-                e.offset = i
-                raise e
+                raise MultipartParseError(msg, offset=i)
 
             # Move to the next byte.
             i += 1
@@ -1449,6 +1481,8 @@
         self.state = state
         self.index = index
         self.flags = flags
+        self._current_header_count = current_header_count
+        self._current_header_size = current_header_size
 
         # Return our data length to indicate no errors, and that we processed
         # all of it.
@@ -1494,6 +1528,8 @@
     #: Note: all file sizes should be in bytes.
     DEFAULT_CONFIG: FormParserConfig = {
         "MAX_BODY_SIZE": float("inf"),
+        "MAX_HEADER_COUNT": DEFAULT_MAX_HEADER_COUNT,
+        "MAX_HEADER_SIZE": DEFAULT_MAX_HEADER_SIZE,
         "MAX_MEMORY_FILE_SIZE": 1 * 1024 * 1024,
         "UPLOAD_DIR": None,
         "UPLOAD_DELETE_TMP": True,
@@ -1733,6 +1769,8 @@
                     "on_end": _on_end,
                 },
                 max_size=self.config["MAX_BODY_SIZE"],
+                max_header_count=self.config["MAX_HEADER_COUNT"],
+                max_header_size=self.config["MAX_HEADER_SIZE"],
             )
 
         else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python_multipart-0.0.26/tests/test_benchmarks.py 
new/python_multipart-0.0.28/tests/test_benchmarks.py
--- old/python_multipart-0.0.26/tests/test_benchmarks.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/python_multipart-0.0.28/tests/test_benchmarks.py        2020-02-02 
01:00:00.000000000 +0100
@@ -0,0 +1,117 @@
+from __future__ import annotations
+
+import string
+from collections.abc import Iterator
+from typing import TYPE_CHECKING
+
+import pytest
+
+from python_multipart import MultipartParser, QuerystringParser
+
+if TYPE_CHECKING:
+    from python_multipart.multipart import MultipartCallbacks, 
QuerystringCallbacks
+
+pytestmark = pytest.mark.benchmark
+
+BOUNDARY = b"------------------------WqclBHaXe8KIsoSum4zfZ6"
+CHUNK_SIZE = 64 * 1024
+
+
+def on_event() -> None:
+    pass
+
+
+def on_data(_data: bytes, _start: int, _end: int) -> None:
+    pass
+
+
+MULTIPART_CALLBACKS: MultipartCallbacks = {
+    "on_part_begin": on_event,
+    "on_part_data": on_data,
+    "on_part_end": on_event,
+    "on_header_field": on_data,
+    "on_header_value": on_data,
+    "on_header_end": on_event,
+    "on_headers_finished": on_event,
+    "on_end": on_event,
+}
+
+QUERYSTRING_CALLBACKS: QuerystringCallbacks = {
+    "on_field_start": on_event,
+    "on_field_name": on_data,
+    "on_field_data": on_data,
+    "on_field_end": on_event,
+    "on_end": on_event,
+}
+
+
+def pattern(pat: bytes, size: int) -> bytes:
+    return (pat * (size // len(pat) + 1))[:size]
+
+
+def build_part(name: bytes, body: bytes, *, filename: bytes | None = None) -> 
bytes:
+    disposition = b'form-data; name="' + name + b'"'
+    if filename is not None:
+        disposition += b'; filename="' + filename + b'"'
+    headers = b"Content-Disposition: " + disposition + b"\r\n"
+    if filename is not None:
+        headers += b"Content-Type: application/octet-stream\r\n"
+    return b"--" + BOUNDARY + b"\r\n" + headers + b"\r\n" + body
+
+
+def build_form(parts: list[bytes]) -> bytes:
+    return b"\r\n".join(parts) + b"\r\n--" + BOUNDARY + b"--\r\n"
+
+
+def split(data: bytes, chunk_size: int = CHUNK_SIZE) -> list[bytes]:
+    return [data[i : i + chunk_size] for i in range(0, len(data), chunk_size)]
+
+
+PRINTABLE = string.printable.encode("ascii")
+
+SIMPLE_FORM = build_form(
+    [build_part(b"email", pattern(PRINTABLE, 24)), build_part(b"password", 
pattern(PRINTABLE, 16))]
+)
+LARGE_FORM = build_form([build_part(f"field{i}".encode(), pattern(PRINTABLE, 
i)) for i in range(100)])
+FILE_UPLOAD = build_form([build_part(b"file", pattern(PRINTABLE, 8 * 1024 * 
1024), filename=b"file.bin")])
+FILE_UPLOAD_CHUNKS = split(FILE_UPLOAD)
+WORSTCASE_BCHAR = build_form([build_part(b"file", pattern(b"\r\n", 1024 * 
1024), filename=b"file.bin")])
+WORSTCASE_BCHAR_CHUNKS = split(WORSTCASE_BCHAR)
+
+URLENCODED_LARGE = b"&".join(f"field{i}={'v' * 64}".encode() for i in 
range(100))
+
+
[email protected]
+def multipart_parser() -> Iterator[MultipartParser]:
+    parser = MultipartParser(BOUNDARY, MULTIPART_CALLBACKS)
+    yield parser
+    parser.finalize()
+
+
[email protected]
+def querystring_parser() -> Iterator[QuerystringParser]:
+    parser = QuerystringParser(QUERYSTRING_CALLBACKS)
+    yield parser
+    parser.finalize()
+
+
+def test_multipart_simple_form(multipart_parser: MultipartParser) -> None:
+    multipart_parser.write(SIMPLE_FORM)
+
+
+def test_multipart_large_form(multipart_parser: MultipartParser) -> None:
+    multipart_parser.write(LARGE_FORM)
+
+
+def test_multipart_file_upload(multipart_parser: MultipartParser) -> None:
+    for chunk in FILE_UPLOAD_CHUNKS:
+        multipart_parser.write(chunk)
+
+
+def test_multipart_worstcase_boundary_chars(multipart_parser: MultipartParser) 
-> None:
+    for chunk in WORSTCASE_BCHAR_CHUNKS:
+        multipart_parser.write(chunk)
+
+
+def test_querystring_large_form(querystring_parser: QuerystringParser) -> None:
+    querystring_parser.write(URLENCODED_LARGE)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/python_multipart-0.0.26/tests/test_multipart.py 
new/python_multipart-0.0.28/tests/test_multipart.py
--- old/python_multipart-0.0.26/tests/test_multipart.py 2020-02-02 
01:00:00.000000000 +0100
+++ new/python_multipart-0.0.28/tests/test_multipart.py 2020-02-02 
01:00:00.000000000 +0100
@@ -1182,6 +1182,18 @@
         ):
             self.f.write(data)
 
+    def test_multipart_header_count_limit(self) -> None:
+        self.make("poc")
+        payload = b'--poc\r\nContent-Disposition: form-data; name="x"\r\n' + 
(b"X-A: 1\r\n" * 8)
+        with self.assertRaisesRegex(MultipartParseError, "Maximum header count 
exceeded"):
+            self.f.write(payload)
+
+    def test_multipart_header_size_limit(self) -> None:
+        self.make("poc")
+        payload = b'--poc\r\nContent-Disposition: form-data; name="x"\r\n' + 
b"X-A: " + (b"a" * (4096 + 124))
+        with self.assertRaisesRegex(MultipartParseError, "Maximum header size 
exceeded"):
+            self.f.write(payload)
+
     def test_octet_stream(self) -> None:
         files: list[File] = []
 
@@ -1502,6 +1514,12 @@
         with self.assertRaises(ValueError):
             MultipartParser(b"bound", max_size="foo")  # type: ignore[arg-type]
 
+    def test_boundary_too_long(self) -> None:
+        with self.assertRaisesRegex(FormParserError, "Boundary length 257 
exceeds maximum of 256"):
+            MultipartParser(b"x" * 257)
+        # 256 should be accepted.
+        MultipartParser(b"x" * 256)
+
     def test_header_begin_callback(self) -> None:
         """
         This test verifies we call the `on_header_begin` callback.

Reply via email to