Medium severity5.3OSV Advisory· Published Dec 22, 2025· Updated Apr 15, 2026
CVE-2025-68480
CVE-2025-68480
Description
Marshmallow is a lightweight library for converting complex objects to and from simple Python datatypes. In versions from 3.0.0rc1 to before 3.26.2 and from 4.0.0 to before 4.1.2, Schema.load(data, many=True) is vulnerable to denial of service attacks. A moderately sized request can consume a disproportionate amount of CPU time. This issue has been patched in version 3.26.2 and 4.1.2.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
marshmallowPyPI | >= 3.0.0rc1, < 3.26.2 | 3.26.2 |
marshmallowPyPI | >= 4.0.0, < 4.1.2 | 4.1.2 |
Affected products
1- Range: 3.0.0, 3.0.0rc1, 3.0.0rc2, …
Patches
1d24a0c9df061Merge commit from fork
4 files changed · +47 −14
CHANGELOG.rst+8 −0 modified@@ -1,6 +1,14 @@ Changelog --------- +4.1.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. + 4.1.1 (2025-11-05) ++++++++++++++++++
pyproject.toml+1 −1 modified@@ -1,6 +1,6 @@ [project] name = "marshmallow" -version = "4.1.1" +version = "4.1.2" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." readme = "README.rst" license = { file = "LICENSE" }
src/marshmallow/error_store.py+21 −12 modified@@ -18,12 +18,19 @@ def store_error(self, messages, field_name=SCHEMA, index=None): # 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: messages = {index: messages} 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 +44,26 @@ def merge_errors(errors1, errors2): # noqa: PLR0911 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]
tests/test_error_store.py+17 −1 modified@@ -1,7 +1,7 @@ from typing import NamedTuple from marshmallow import missing -from marshmallow.error_store import merge_errors +from marshmallow.error_store import merge_errors, ErrorStore def test_missing_is_falsy(): @@ -149,3 +149,19 @@ def test_deep_merging_dicts(self): 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"]}
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
4News mentions
0No linked articles in our index yet.