From 3144adc16ab73502f8e8a06ae52c2f48c9292f40 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Mon, 26 Oct 2026 16:42:42 -0400 Subject: [PATCH] fix(loopback): reject an address family that is not an int (#2289) Loopback.make range-checked family and then called family.to_bytes with no type check, so True packed as family 1, '2' and None raised TypeError and 2.0 raised AttributeError. A bool or any non-int family now raises the documented ProtocolError "Loopback: invalid address family: ...", from the same check as the range test. Test: test_make_rejects_a_family_that_is_not_an_int pins the message for True, False, '2', 2.0 and None. --- pcapkit/protocols/link/loopback.py | 8 ++++++-- tests/protocols/link/test_loopback_unit.py | 13 +++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pcapkit/protocols/link/loopback.py b/pcapkit/protocols/link/loopback.py index bb025a7f4c..73664d89b7 100644 --- a/pcapkit/protocols/link/loopback.py +++ b/pcapkit/protocols/link/loopback.py @@ -233,12 +233,16 @@ def make(self, Raises: ProtocolError: If ``byteorder`` is neither ``'big'`` nor - ``'little'``, or ``family`` does not fit in 4 octets. + ``'little'``, or ``family`` is not an :obj:`int` (a + :obj:`bool` included, :issue:`2289`) or does not fit in 4 + octets. """ if byteorder not in ('big', 'little'): raise ProtocolError(f'Loopback: invalid byte order: {byteorder!r}') - if not 0 <= family <= 0xFFFF_FFFF: + # NOTE: ``bool`` is an ``int`` subclass, so without the explicit check + # ``True`` would pack as address family 1 (#2289). + if isinstance(family, bool) or not isinstance(family, int) or not 0 <= family <= 0xFFFF_FFFF: raise ProtocolError(f'Loopback: invalid address family: {family!r}') return Schema_Loopback( diff --git a/tests/protocols/link/test_loopback_unit.py b/tests/protocols/link/test_loopback_unit.py index c90fc58a31..ccae463acf 100644 --- a/tests/protocols/link/test_loopback_unit.py +++ b/tests/protocols/link/test_loopback_unit.py @@ -127,6 +127,19 @@ def test_make_rejects_what_does_not_fit(self) -> None: with self.subTest(**kwargs), self.assertRaises(ProtocolError): Loopback(**kwargs) + def test_make_rejects_a_family_that_is_not_an_int(self) -> None: + # #2289: a bool is an int and packs as family 1; a non-int fails the + # range comparison or ``to_bytes`` with a bare ``TypeError`` or + # ``AttributeError``. Each must raise ``ProtocolError``. + from pcapkit.protocols.link.loopback import Loopback + from pcapkit.utilities.exceptions import ProtocolError + + for family in (True, False, '2', 2.0, None): + with self.subTest(family=family): + with self.assertRaises(ProtocolError) as ctx: + Loopback(family=family) + self.assertEqual(str(ctx.exception), f'Loopback: invalid address family: {family!r}') + def test_registered_for_null_and_loop(self) -> None: from pcapkit.const.reg.linktype import LinkType from pcapkit.corekit.module import ModuleDescriptor