diff --git a/CHANGES.rst b/CHANGES.rst index 17c1a19..9b7c445 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,10 @@ Unreleased - Drop support for Python 3.8 and 3.9. - Remove previously deprecated code. +- ``Serializer.loads`` no longer accepts arbitrary keyword arguments. + Passing ``max_age`` (or other unexpected kwargs) to a non-timed + serializer now raises ``TypeError`` instead of being silently ignored. + :issue:`429` Version 2.2.0 diff --git a/src/itsdangerous/serializer.py b/src/itsdangerous/serializer.py index 5ed949c..0646add 100644 --- a/src/itsdangerous/serializer.py +++ b/src/itsdangerous/serializer.py @@ -325,11 +325,14 @@ def dump(self, obj: t.Any, f: t.IO[t.Any], salt: str | bytes | None = None) -> N """ f.write(self.dumps(obj, salt)) - def loads( - self, s: str | bytes, salt: str | bytes | None = None, **kwargs: t.Any - ) -> t.Any: + def loads(self, s: str | bytes, salt: str | bytes | None = None) -> t.Any: """Reverse of :meth:`dumps`. Raises :exc:`.BadSignature` if the signature validation fails. + + .. versionchanged:: 2.3 + Removed unused ``**kwargs``. Passing unexpected keyword + arguments such as ``max_age`` (only valid on timed serializers) + now raises :exc:`TypeError` instead of being silently ignored. """ s = want_bytes(s) last_exception = None diff --git a/tests/test_itsdangerous/test_serializer.py b/tests/test_itsdangerous/test_serializer.py index 737b504..f303bc8 100644 --- a/tests/test_itsdangerous/test_serializer.py +++ b/tests/test_itsdangerous/test_serializer.py @@ -51,6 +51,23 @@ def value(self): def test_serializer(self, serializer: Serializer, value: Any): assert serializer.loads(serializer.dumps(value)) == value + def test_loads_rejects_unexpected_kwargs( + self, serializer: Serializer, value: Any + ) -> None: + """Non-timed serializers must not silently ignore max_age. + + Accidentally swapping URLSafeTimedSerializer for URLSafeSerializer + while still passing max_age would otherwise accept expired tokens. + """ + import inspect + + if "max_age" in inspect.signature(type(serializer).loads).parameters: + pytest.skip("timed serializer accepts max_age") + + signed = serializer.dumps(value) + with pytest.raises(TypeError, match="unexpected keyword argument"): + serializer.loads(signed, max_age=-1) # type: ignore[call-arg] + @pytest.mark.parametrize( "transform", (