From 8ceea7e007182166e3ec5ce3773384f3c810e3bc Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Mon, 26 Oct 2026 16:37:51 -0400 Subject: [PATCH] fix(transport): reject a bool port in TCP and UDP make (#2287) Transport._make_port handed any non-AppType value to AppType.get, which looked a bool up as port 0 or 1, so UDP(srcport=True) packed port 1 and TCP(dstport=False) port 0. _make_port becomes a classmethod and raises ProtocolError ": invalid port: True" for a bool, which covers SCTP as well. Test: test_make_refuses_a_bool_port pins the message for True and False on either port of TCP, UDP and SCTP. --- pcapkit/protocols/transport/transport.py | 13 ++++++++++--- .../transport/test_transport_unit.py | 19 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/pcapkit/protocols/transport/transport.py b/pcapkit/protocols/transport/transport.py index 5054d54649..e4b100874c 100644 --- a/pcapkit/protocols/transport/transport.py +++ b/pcapkit/protocols/transport/transport.py @@ -19,7 +19,7 @@ from pcapkit.corekit.module import ModuleDescriptor from pcapkit.protocols.protocol import _PT, _ST, ProtocolBase from pcapkit.protocols.schema.schema import keep_short_read, replay_short_read -from pcapkit.utilities.exceptions import StructError, UnsupportedCall, stacklevel +from pcapkit.utilities.exceptions import ProtocolError, StructError, UnsupportedCall, stacklevel from pcapkit.utilities.logging import DEVMODE, get_logger from pcapkit.utilities.warnings import RegistryWarning, warn @@ -206,8 +206,8 @@ def __post_init__(self, file: 'Optional[IO[bytes] | bytes]' = None, # Utilities. ########################################################################## - @staticmethod - def _make_port(port: 'Enum_AppType | int', + @classmethod + def _make_port(cls, port: 'Enum_AppType | int', proto: 'Enum_TransportProtocol') -> 'Enum_AppType': """Resolve a port number to its application type. @@ -219,6 +219,9 @@ def _make_port(port: 'Enum_AppType | int', Returns: The :class:`~pcapkit.const.reg.apptype.apptype.AppType` for ``port``. + Raises: + ProtocolError: If ``port`` is a :obj:`bool` (:issue:`2287`). + Important: :meth:`self.make ` accepts a bare :obj:`int` for a port, but the schema field converts it only @@ -233,6 +236,10 @@ def _make_port(port: 'Enum_AppType | int', """ if isinstance(port, Enum_AppType): return port + # NOTE: ``bool`` is an ``int`` subclass, so ``AppType.get`` would look + # ``True``/``False`` up as port 1/0 rather than refuse them (#2287). + if isinstance(port, bool): + raise ProtocolError(f'{cls.__name__}: invalid port: {port!r}') return Enum_AppType.get(port, proto=proto) def _decode_next_layer(self, dict_: '_PT', ports: 'tuple[int, int]', length: 'Optional[int]' = None, *, # type: ignore[override] diff --git a/tests/protocols/transport/test_transport_unit.py b/tests/protocols/transport/test_transport_unit.py index 8556180b43..9f97369ac4 100644 --- a/tests/protocols/transport/test_transport_unit.py +++ b/tests/protocols/transport/test_transport_unit.py @@ -441,6 +441,25 @@ def test_make_port_resolves_an_integer_and_passes_an_apptype_through(self) -> No # which would lose the protocol it was resolved for. self.assertIs(Transport._make_port(over_sctp, TransportProtocol.tcp), over_sctp) + def test_make_refuses_a_bool_port(self) -> None: + # #2287: ``bool`` is an ``int`` subclass, so ``AppType.get`` resolving + # ``True``/``False`` to port 1/0 is a defect this guards against. + from pcapkit.protocols.transport.sctp import SCTP + from pcapkit.protocols.transport.tcp import TCP + from pcapkit.protocols.transport.udp import UDP + from pcapkit.utilities.exceptions import ProtocolError + + for cls in (TCP, UDP, SCTP): + for value in (True, False): + for kwargs in ({'srcport': value, 'dstport': 53}, {'srcport': 53, 'dstport': value}): + with self.subTest(protocol=cls.__name__, **kwargs): + with self.assertRaises(ProtocolError) as ctx: + cls(**kwargs) + self.assertEqual(str(ctx.exception), f'{cls.__name__}: invalid port: {value!r}') + + # An ``int`` port resolves to its application type. + self.assertEqual(UDP(srcport=1, dstport=53).info.srcport.port, 1) + if __name__ == '__main__': unittest.main()