Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions python_multipart/multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -714,7 +714,7 @@ def __init__(self, callbacks: OctetStreamCallbacks = {}, max_size: float = float
self.callbacks = callbacks
self._started = False

if not isinstance(max_size, Number) or max_size < 1:
if isinstance(max_size, bool) or not isinstance(max_size, Number) or max_size < 1:
raise ValueError("max_size must be a positive number, not %r" % max_size)
self.max_size: int | float = max_size
self._current_size = 0
Expand Down Expand Up @@ -796,7 +796,7 @@ def __init__(
self.callbacks = callbacks

# Max-size stuff
if not isinstance(max_size, Number) or max_size < 1:
if isinstance(max_size, bool) or not isinstance(max_size, Number) or max_size < 1:
raise ValueError("max_size must be a positive number, not %r" % max_size)
self.max_size: int | float = max_size
self._current_size = 0
Expand Down Expand Up @@ -1049,11 +1049,20 @@ def __init__(

self.callbacks = callbacks

if not isinstance(max_size, Number) or max_size < 1:
if isinstance(max_size, bool) or not isinstance(max_size, Number) or max_size < 1:
raise ValueError("max_size must be a positive number, not %r" % max_size)
self.max_size = max_size
self._current_size = 0

# bool subclasses int; True would silently become count/size 1
if isinstance(max_header_count, bool) or not isinstance(max_header_count, int):
raise ValueError(
"max_header_count must be an int, not %r" % max_header_count
)
if isinstance(max_header_size, bool) or not isinstance(max_header_size, int):
raise ValueError(
"max_header_size must be an int, not %r" % max_header_size
)
self.max_header_count = max_header_count
self._current_header_count = 0

Expand Down
27 changes: 27 additions & 0 deletions tests/test_multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,12 @@ def test_invalid_max_size(self) -> None:
with self.assertRaises(ValueError):
p = QuerystringParser(max_size=-100)

def test_rejects_bool_max_size(self) -> None:
"""bool is a Number; max_size=True must not silently become 1."""
for value in (True, False):
with self.assertRaises(ValueError):
QuerystringParser(max_size=value)

def test_strict_parsing_pass(self) -> None:
data = b"foo=bar&another=asdf"
for first, last in split_all(data):
Expand Down Expand Up @@ -639,6 +645,12 @@ def test_invalid_max_size(self) -> None:
with self.assertRaises(ValueError):
q = OctetStreamParser(max_size="foo") # type: ignore[arg-type]

def test_rejects_bool_max_size_octet(self) -> None:
"""bool is a Number; max_size=True must not silently become 1."""
for value in (True, False):
with self.assertRaises(ValueError):
OctetStreamParser(max_size=value)


class TestBase64Decoder(unittest.TestCase):
# Note: base64('foobar') == 'Zm9vYmFy'
Expand Down Expand Up @@ -1592,6 +1604,21 @@ def test_invalid_max_size_multipart(self) -> None:
with self.assertRaises(ValueError):
MultipartParser(b"bound", max_size="foo") # type: ignore[arg-type]

def test_rejects_bool_max_size_and_header_limits(self) -> None:
"""bool subclasses Number/int; True must not silently become size/count 1."""
for value in (True, False):
with self.assertRaises(ValueError):
MultipartParser(b"bound", max_size=value)
with self.assertRaises(ValueError):
MultipartParser(b"bound", max_header_count=value)
with self.assertRaises(ValueError):
MultipartParser(b"bound", max_header_size=value)
# valid ints still accepted
p = MultipartParser(b"bound", max_size=100, max_header_count=4, max_header_size=256)
assert p.max_size == 100
assert p.max_header_count == 4
assert p.max_header_size == 256

def test_boundary_too_long(self) -> None:
with self.assertRaisesRegex(FormParserError, "Boundary length 257 exceeds maximum of 256"):
MultipartParser(b"x" * 257)
Expand Down
Loading