Package: release.debian.org
Severity: normal
Tags: trixie
X-Debbugs-Cc: [email protected], [email protected]
Control: affects -1 + src:python-marshmallow
User: [email protected]
Usertags: pu
* New upstream release.
- CVE-2025-68480: DoS with Schema.load(many) (Closes: #1123888)
The CVE fix is the only non-trivial change in the new upstream version.
diffstat for python-marshmallow-3.26.1 python-marshmallow-3.26.2
CHANGELOG.rst | 8 ++++++++
debian/changelog | 8 ++++++++
pyproject.toml | 4 ++--
src/marshmallow/error_store.py | 35 +++++++++++++++++++++++------------
src/marshmallow/schema.py | 4 ++--
tests/test_error_store.py | 18 +++++++++++++++++-
6 files changed, 60 insertions(+), 17 deletions(-)
diff -Nru python-marshmallow-3.26.1/CHANGELOG.rst
python-marshmallow-3.26.2/CHANGELOG.rst
--- python-marshmallow-3.26.1/CHANGELOG.rst 2025-02-03 17:31:14.000000000
+0200
+++ python-marshmallow-3.26.2/CHANGELOG.rst 2025-12-22 08:51:52.000000000
+0200
@@ -1,6 +1,14 @@
Changelog
---------
+3.26.2 (2025-12-19)
+++++++++++++++++++
+
+Bug fixes:
+
+- :cve:`CVE-2025-68480`: Merge error store messages without rebuilding
collections.
+ Thanks 카푸치노 for reporting and :user:`deckar01` for the fix.
+
3.26.1 (2025-02-03)
*******************
diff -Nru python-marshmallow-3.26.1/debian/changelog
python-marshmallow-3.26.2/debian/changelog
--- python-marshmallow-3.26.1/debian/changelog 2025-03-26 13:58:32.000000000
+0200
+++ python-marshmallow-3.26.2/debian/changelog 2026-06-28 11:20:03.000000000
+0300
@@ -1,3 +1,11 @@
+python-marshmallow (3.26.2-0+deb13u1) trixie; urgency=medium
+
+ * Non-maintainer upload.
+ * New upstream release.
+ - CVE-2025-68480: DoS with Schema.load(many) (Closes: #1123888)
+
+ -- Adrian Bunk <[email protected]> Sun, 28 Jun 2026 11:20:03 +0300
+
python-marshmallow (3.26.1-0.2) unstable; urgency=medium
* Non-maintainer upload.
diff -Nru python-marshmallow-3.26.1/pyproject.toml
python-marshmallow-3.26.2/pyproject.toml
--- python-marshmallow-3.26.1/pyproject.toml 2025-02-03 17:31:14.000000000
+0200
+++ python-marshmallow-3.26.2/pyproject.toml 2025-12-22 08:51:52.000000000
+0200
@@ -1,6 +1,6 @@
[project]
name = "marshmallow"
-version = "3.26.1"
+version = "3.26.2"
description = "A lightweight library for converting complex datatypes to and
from native Python datatypes."
readme = "README.rst"
license = { file = "LICENSE" }
@@ -121,7 +121,7 @@
[tool.mypy]
files = ["src", "tests", "examples"]
ignore_missing_imports = true
-warn_unreachable = true
+warn_unreachable = false
warn_unused_ignores = true
warn_redundant_casts = true
no_implicit_optional = true
diff -Nru python-marshmallow-3.26.1/src/marshmallow/error_store.py
python-marshmallow-3.26.2/src/marshmallow/error_store.py
--- python-marshmallow-3.26.1/src/marshmallow/error_store.py 2025-02-03
17:31:14.000000000 +0200
+++ python-marshmallow-3.26.2/src/marshmallow/error_store.py 2025-12-22
08:51:52.000000000 +0200
@@ -18,6 +18,7 @@
# field error -> store/merge error messages under field name key
# schema error -> if string or list, store/merge under _schema key
# -> if dict, store/merge with other top-level keys
+ messages = copy_containers(messages)
if field_name != SCHEMA or not isinstance(messages, dict):
messages = {field_name: messages}
if index is not None:
@@ -25,6 +26,14 @@
self.errors = merge_errors(self.errors, messages)
+def copy_containers(errors):
+ if isinstance(errors, list):
+ return [copy_containers(val) for val in errors]
+ if isinstance(errors, dict):
+ return {key: copy_containers(val) for key, val in errors.items()}
+ return errors
+
+
def merge_errors(errors1, errors2): # noqa: PLR0911
"""Deeply merge two error messages.
@@ -37,24 +46,26 @@
return errors1
if isinstance(errors1, list):
if isinstance(errors2, list):
- return errors1 + errors2
+ errors1.extend(errors2)
+ return errors1
if isinstance(errors2, dict):
- return dict(errors2, **{SCHEMA: merge_errors(errors1,
errors2.get(SCHEMA))})
- return [*errors1, errors2]
+ errors2[SCHEMA] = merge_errors(errors1, errors2.get(SCHEMA))
+ return errors2
+ errors1.append(errors2)
+ return errors1
if isinstance(errors1, dict):
- if isinstance(errors2, list):
- return dict(errors1, **{SCHEMA: merge_errors(errors1.get(SCHEMA),
errors2)})
if isinstance(errors2, dict):
- errors = dict(errors1)
for key, val in errors2.items():
- if key in errors:
- errors[key] = merge_errors(errors[key], val)
+ if key in errors1:
+ errors1[key] = merge_errors(errors1[key], val)
else:
- errors[key] = val
- return errors
- return dict(errors1, **{SCHEMA: merge_errors(errors1.get(SCHEMA),
errors2)})
+ errors1[key] = val
+ return errors1
+ errors1[SCHEMA] = merge_errors(errors1.get(SCHEMA), errors2)
+ return errors1
if isinstance(errors2, list):
return [errors1, *errors2]
if isinstance(errors2, dict):
- return dict(errors2, **{SCHEMA: merge_errors(errors1,
errors2.get(SCHEMA))})
+ errors2[SCHEMA] = merge_errors(errors1, errors2.get(SCHEMA))
+ return errors2
return [errors1, errors2]
diff -Nru python-marshmallow-3.26.1/src/marshmallow/schema.py
python-marshmallow-3.26.2/src/marshmallow/schema.py
--- python-marshmallow-3.26.1/src/marshmallow/schema.py 2025-02-03
17:31:14.000000000 +0200
+++ python-marshmallow-3.26.2/src/marshmallow/schema.py 2025-12-22
08:51:52.000000000 +0200
@@ -374,7 +374,7 @@
class MySchema2(Schema):
# Type checkers will check attributes
- class Meta(Schema.Opts):
+ class Meta(Schema.Meta):
additional = True # Incompatible types in assignment
.. versionremoved:: 3.0.0b7 Remove ``strict``.
@@ -1139,7 +1139,7 @@
msg = (
f'Field for "{field_name}" must be declared as a '
"Field instance, not a class. "
- f'Did you mean "fields.{field_obj.__name__}()"?' # type:
ignore[attr-defined]
+ f'Did you mean "fields.{field_obj.__name__}()"?'
)
raise TypeError(msg) from error
raise
diff -Nru python-marshmallow-3.26.1/tests/test_error_store.py
python-marshmallow-3.26.2/tests/test_error_store.py
--- python-marshmallow-3.26.1/tests/test_error_store.py 2025-02-03
17:31:14.000000000 +0200
+++ python-marshmallow-3.26.2/tests/test_error_store.py 2025-12-22
08:51:52.000000000 +0200
@@ -1,7 +1,7 @@
from typing import NamedTuple
from marshmallow import missing
-from marshmallow.error_store import merge_errors
+from marshmallow.error_store import ErrorStore, merge_errors
def test_missing_is_falsy():
@@ -149,3 +149,19 @@
assert merge_errors(
{"field1": {"field2": "error1"}}, {"field1": {"field2": "error2"}}
) == {"field1": {"field2": ["error1", "error2"]}}
+
+ def test_list_not_changed(self):
+ store = ErrorStore()
+ message = ["foo"]
+ store.store_error(message)
+ store.store_error(message)
+ assert message == ["foo"]
+ assert store.errors == {"_schema": ["foo", "foo"]}
+
+ def test_dict_not_changed(self):
+ store = ErrorStore()
+ message = {"foo": ["bar"]}
+ store.store_error(message)
+ store.store_error(message)
+ assert message == {"foo": ["bar"]}
+ assert store.errors == {"foo": ["bar", "bar"]}