diff --git a/docs/source/pcapkit/protocols/application/ospf.rst b/docs/source/pcapkit/protocols/application/ospf.rst index b5ab314a76..22071ec532 100644 --- a/docs/source/pcapkit/protocols/application/ospf.rst +++ b/docs/source/pcapkit/protocols/application/ospf.rst @@ -63,7 +63,7 @@ Header Schemas :members: :show-inheritance: -.. autoclass:: pcapkit.protocols.schema.application.ospf.CrytographicAuthentication +.. autoclass:: pcapkit.protocols.schema.application.ospf.CryptographicAuthentication :members: :show-inheritance: @@ -81,7 +81,7 @@ Data Models :members: :show-inheritance: -.. autoclass:: pcapkit.protocols.data.application.ospf.CrytographicAuthentication +.. autoclass:: pcapkit.protocols.data.application.ospf.CryptographicAuthentication :members: :show-inheritance: diff --git a/pcapkit/protocols/application/ospf.py b/pcapkit/protocols/application/ospf.py index bd33a8645c..290d347de7 100644 --- a/pcapkit/protocols/application/ospf.py +++ b/pcapkit/protocols/application/ospf.py @@ -46,11 +46,11 @@ from pcapkit.protocols.application.application import Application from pcapkit.protocols.data.application.ospf import OSPF as Data_OSPF from pcapkit.protocols.data.application.ospf import \ - CrytographicAuthentication as Data_CrytographicAuthentication + CryptographicAuthentication as Data_CryptographicAuthentication from pcapkit.protocols.protocol import ProtocolBase from pcapkit.protocols.schema.application.ospf import OSPF as Schema_OSPF from pcapkit.protocols.schema.application.ospf import \ - CrytographicAuthentication as Schema_CrytographicAuthentication + CryptographicAuthentication as Schema_CryptographicAuthentication from pcapkit.utilities.exceptions import ProtocolError if TYPE_CHECKING: @@ -178,7 +178,7 @@ def read(self, length: 'Optional[int]' = None, **kwargs: 'Any') -> 'Data_OSPF': if ospf.autype == Enum_Authentication.Cryptographic_authentication: ospf.__update__([ ('auth', self._read_encrypt_auth( - cast('Schema_CrytographicAuthentication', schema.auth_data), + cast('Schema_CryptographicAuthentication', schema.auth_data), )), ]) else: @@ -204,7 +204,7 @@ def make(self, auth_type_default: 'Optional[int]' = None, auth_type_namespace: 'Optional[dict[str, int] | dict[int, str] | Type[StdlibEnum] | Type[AenumEnum]]' = None, # pylint: disable=line-too-long auth_type_reversed: 'bool' = False, - auth_data: 'bytes | Schema_CrytographicAuthentication | Data_CrytographicAuthentication' = b'\x00\x00\x00\x00\x00\x00\x00\x00', + auth_data: 'bytes | Schema_CryptographicAuthentication | Data_CryptographicAuthentication' = b'\x00\x00\x00\x00\x00\x00\x00\x00', payload: 'bytes | ProtocolBase | Schema' = b'', **kwargs: 'Any') -> 'Schema_OSPF': """Make (construct) packet data. @@ -346,7 +346,7 @@ def _make_id_numbers(self, id: 'IPv4Address | str | bytes | bytearray') -> 'byte return cast('IPv4Address', parse_ip_address( id, f'{self.__class__.__name__}: invalid ID', version=4)).packed - def _read_encrypt_auth(self, schema: 'Schema_CrytographicAuthentication') -> 'Data_CrytographicAuthentication': + def _read_encrypt_auth(self, schema: 'Schema_CryptographicAuthentication') -> 'Data_CryptographicAuthentication': """Read Authentication field when Cryptographic Authentication is employed, i.e. :attr:`~OSPF.autype` is ``2``. @@ -369,7 +369,7 @@ def _read_encrypt_auth(self, schema: 'Schema_CrytographicAuthentication') -> 'Da Parsed packet data. """ - auth = Data_CrytographicAuthentication( + auth = Data_CryptographicAuthentication( key_id=schema.key_id, len=schema.len, seq=schema.seq, @@ -377,8 +377,8 @@ def _read_encrypt_auth(self, schema: 'Schema_CrytographicAuthentication') -> 'Da return auth def _make_encrypt_auth(self, - auth_data: 'bytes | Schema_CrytographicAuthentication | Data_CrytographicAuthentication' # pylint: disable=line-too-long - ) -> 'bytes | Schema_CrytographicAuthentication': + auth_data: 'bytes | Schema_CryptographicAuthentication | Data_CryptographicAuthentication' # pylint: disable=line-too-long + ) -> 'bytes | Schema_CryptographicAuthentication': """Make Authentication field when Cryptographic Authentication is employed. Args: @@ -388,10 +388,10 @@ def _make_encrypt_auth(self, Authentication bytes. """ - if isinstance(auth_data, (Schema_CrytographicAuthentication, bytes)): + if isinstance(auth_data, (Schema_CryptographicAuthentication, bytes)): return auth_data - if isinstance(auth_data, Data_CrytographicAuthentication): - return Schema_CrytographicAuthentication( + if isinstance(auth_data, Data_CryptographicAuthentication): + return Schema_CryptographicAuthentication( key_id=auth_data.key_id, len=auth_data.len, seq=auth_data.seq, diff --git a/pcapkit/protocols/data/__init__.py b/pcapkit/protocols/data/__init__.py index 8313b29db0..b3f9c7eac8 100644 --- a/pcapkit/protocols/data/__init__.py +++ b/pcapkit/protocols/data/__init__.py @@ -61,9 +61,6 @@ # Ethernet Protocol 'Ethernet', - # Open Shortest Path First - 'OSPF', 'OSPF_CrytographicAuthentication', - # 802.1Q/802.1ad VLAN Tag Types 'VLAN', 'VLAN_TCI', @@ -205,6 +202,9 @@ 'NGAP', 'NGAP_IE', 'NGAP_Choice', 'NGAP_BitString', 'NGAP_Sequence', + # Open Shortest Path First + 'OSPF', 'OSPF_CryptographicAuthentication', + # Hypertext Transfer Protocol (HTTP/1.*) 'HTTPv1', 'HTTPv1_Header', diff --git a/pcapkit/protocols/data/application/__init__.py b/pcapkit/protocols/data/application/__init__.py index 955c5d1926..5f8087a98b 100644 --- a/pcapkit/protocols/data/application/__init__.py +++ b/pcapkit/protocols/data/application/__init__.py @@ -46,7 +46,7 @@ # Open Shortest Path First from pcapkit.protocols.data.application.ospf import OSPF from pcapkit.protocols.data.application.ospf import \ - CrytographicAuthentication as OSPF_CrytographicAuthentication + CryptographicAuthentication as OSPF_CryptographicAuthentication __all__ = [ # File Transfer Protocol @@ -72,5 +72,5 @@ 'NGAP_IE', 'NGAP_Choice', 'NGAP_BitString', 'NGAP_Sequence', # Open Shortest Path First - 'OSPF', 'OSPF_CrytographicAuthentication', + 'OSPF', 'OSPF_CryptographicAuthentication', ] diff --git a/pcapkit/protocols/data/application/ospf.py b/pcapkit/protocols/data/application/ospf.py index d5d8754e04..1801b3a7b2 100644 --- a/pcapkit/protocols/data/application/ospf.py +++ b/pcapkit/protocols/data/application/ospf.py @@ -13,11 +13,11 @@ from pcapkit.const.ospf.authentication import Authentication from pcapkit.const.ospf.packet import Packet -__all__ = ['OSPF', 'CrytographicAuthentication'] +__all__ = ['OSPF', 'CryptographicAuthentication'] @info_final -class CrytographicAuthentication(Data): +class CryptographicAuthentication(Data): """Data model for OSPF cryptographic authentication.""" #: Key ID. @@ -52,7 +52,7 @@ class OSPF(Protocol): if TYPE_CHECKING: #: Authentication. - auth: 'bytes | CrytographicAuthentication' + auth: 'bytes | CryptographicAuthentication' def __init__(self, version: 'int', type: 'Packet', len: 'int', router_id: 'IPv4Address', area_id: 'IPv4Address', chksum: 'bytes', autype: 'Authentication') -> 'None': ... # pylint: disable=super-init-not-called,unused-argument,line-too-long,multiple-statements,redefined-builtin diff --git a/pcapkit/protocols/schema/__init__.py b/pcapkit/protocols/schema/__init__.py index 9f573639bf..b3c4d58651 100644 --- a/pcapkit/protocols/schema/__init__.py +++ b/pcapkit/protocols/schema/__init__.py @@ -28,7 +28,6 @@ 'ARP', 'Ethernet', 'L2TP', - 'OSPF', 'OSPF_CrytographicAuthentication', # application layer 'VLAN', 'VLAN_TCI', # Internet Layer Protocols @@ -136,6 +135,7 @@ # Application Layer Protocols 'FTP', 'NGAP', + 'OSPF', 'OSPF_CryptographicAuthentication', 'HTTPv1', 'HTTPv2', 'HTTPv2_FrameType', diff --git a/pcapkit/protocols/schema/application/__init__.py b/pcapkit/protocols/schema/application/__init__.py index 72a397d466..b786af1ab5 100644 --- a/pcapkit/protocols/schema/application/__init__.py +++ b/pcapkit/protocols/schema/application/__init__.py @@ -30,7 +30,7 @@ # Open Shortest Path First from pcapkit.protocols.schema.application.ospf import OSPF from pcapkit.protocols.schema.application.ospf import \ - CrytographicAuthentication as OSPF_CrytographicAuthentication + CryptographicAuthentication as OSPF_CryptographicAuthentication __all__ = [ # File Transfer Protocol @@ -50,5 +50,5 @@ 'NGAP', # Open Shortest Path First - 'OSPF', 'OSPF_CrytographicAuthentication', + 'OSPF', 'OSPF_CryptographicAuthentication', ] diff --git a/pcapkit/protocols/schema/application/ospf.py b/pcapkit/protocols/schema/application/ospf.py index 1ba65d15c7..fd6f0ff03e 100644 --- a/pcapkit/protocols/schema/application/ospf.py +++ b/pcapkit/protocols/schema/application/ospf.py @@ -12,7 +12,7 @@ from pcapkit.corekit.fields.strings import BytesField, PaddingField from pcapkit.protocols.schema.schema import Schema, schema_final -__all__ = ['OSPF', 'CrytographicAuthentication'] +__all__ = ['OSPF', 'CryptographicAuthentication'] if TYPE_CHECKING: from ipaddress import IPv4Address @@ -30,17 +30,17 @@ def ospf_auth_data_selector(pkt: 'dict[str, Any]') -> 'Field': Returns: * If :attr:`OSPF.auth_type` is 2, a :class:`~pcapkit.corekit.fields.misc.SchemaField` - wrapped :class:`~pcapkit.protocols.schema.application.ospf.CrytographicAuthentication` instance. + wrapped :class:`~pcapkit.protocols.schema.application.ospf.CryptographicAuthentication` instance. * Otherwise, a :class:`~pcapkit.corekit.fields.strings.BytesField` instance. """ if pkt['auth_type'] == Enum_Authentication.Cryptographic_authentication: - return SchemaField(length=8, schema=CrytographicAuthentication) + return SchemaField(length=8, schema=CryptographicAuthentication) return BytesField(length=8) @schema_final -class CrytographicAuthentication(Schema): +class CryptographicAuthentication(Schema): """Header schema for OSPF cryptographic authentication.""" #: Reserved bytes. @@ -75,7 +75,7 @@ class OSPF(Schema): #: Authentication type. auth_type: 'Enum_Authentication' = EnumField(length=2, namespace=Enum_Authentication) #: Authentication data. - auth_data: 'bytes | CrytographicAuthentication' = SwitchField( + auth_data: 'bytes | CryptographicAuthentication' = SwitchField( selector=ospf_auth_data_selector, ) #: Payload. @@ -86,5 +86,5 @@ def __init__(self, version: 'int', type: 'Enum_Packet', length: 'int', router_id: 'IPv4Address | bytes | str | int', area_id: 'IPv4Address | bytes | str | int', checksum: 'bytes', auth_type: 'Enum_Authentication', - auth_data: 'bytes | CrytographicAuthentication', + auth_data: 'bytes | CryptographicAuthentication', payload: 'bytes | ProtocolBase | Schema') -> 'None': ... diff --git a/tests/protocols/application/test_ospf_cryptographic_authentication_unit.py b/tests/protocols/application/test_ospf_cryptographic_authentication_unit.py new file mode 100644 index 0000000000..61e9baca26 --- /dev/null +++ b/tests/protocols/application/test_ospf_cryptographic_authentication_unit.py @@ -0,0 +1,93 @@ +# -*- coding: utf-8 -*- +"""Unit tests for the OSPF cryptographic-authentication names, :issue:`1090`. + +``OSPF_CryptographicAuthentication`` is exported under that spelling, with no +alias for the misspelled ``OSPF_CrytographicAuthentication``, and both OSPF names +sit in the application-layer group of the :mod:`pcapkit.protocols.schema` and +:mod:`pcapkit.protocols.data` :attr:`__all__`. +""" +from __future__ import annotations + +import importlib.util +import unittest + +from tests._support import purge_modules, reimport_once_per_class + +RUNTIME_DEPS = ('tbtrim', 'aenum', 'chardet', 'dictdumper') +HAS_RUNTIME = all(importlib.util.find_spec(name) is not None for name in RUNTIME_DEPS) + + +@unittest.skipUnless(HAS_RUNTIME, 'runtime dependencies not installed') +class OSPFCryptographicAuthenticationUnitTests(unittest.TestCase): + def setUp(self) -> None: + reimport_once_per_class(self) + + def tearDown(self) -> None: + purge_modules(['pcapkit']) + + def _packages(self) -> 'list': + import pcapkit.protocols.data as data + import pcapkit.protocols.schema as schema + return [schema, data] + + def test_correctly_spelled_name_is_exported(self) -> None: + from pcapkit.protocols.data.application import ospf as data_ospf + from pcapkit.protocols.schema.application import ospf as schema_ospf + + for package, module in zip(self._packages(), (schema_ospf, data_ospf)): + with self.subTest(package=package.__name__): + self.assertIn('OSPF_CryptographicAuthentication', package.__all__) + self.assertIs(package.OSPF_CryptographicAuthentication, + module.CryptographicAuthentication) + self.assertEqual(module.CryptographicAuthentication.__name__, + 'CryptographicAuthentication') + + def test_misspelled_name_is_gone(self) -> None: + import pcapkit.protocols.data.application as data_application + import pcapkit.protocols.schema.application as schema_application + from pcapkit.protocols.data.application import ospf as data_ospf + from pcapkit.protocols.schema.application import ospf as schema_ospf + + for package in self._packages() + [schema_application, data_application]: + with self.subTest(package=package.__name__): + self.assertNotIn('OSPF_CrytographicAuthentication', package.__all__) + self.assertFalse(hasattr(package, 'OSPF_CrytographicAuthentication')) + for module in (schema_ospf, data_ospf): + with self.subTest(module=module.__name__): + self.assertNotIn('CrytographicAuthentication', module.__all__) + self.assertFalse(hasattr(module, 'CrytographicAuthentication')) + + def test_ospf_is_grouped_with_the_application_layer(self) -> None: + # The groups in these __all__ literals are runs of names: OSPF has to sit + # inside the application run, which FTP opens and HTTPv2 closes, and not + # between the link-layer names it used to share a run with. + for package in self._packages(): + names = package.__all__ + application = names[names.index('FTP'):names.index('HTTPv2_ContinuationFrame') + 1] + with self.subTest(package=package.__name__): + for name in ('OSPF', 'OSPF_CryptographicAuthentication'): + self.assertIn(name, application) + for name in ('Ethernet', 'VLAN', 'ARP', 'L2TP'): + self.assertNotIn(name, application) + + def test_auth_data_selector_picks_the_cryptographic_schema(self) -> None: + # Moved from tests/protocols/link/test_link_unit.py. + from pcapkit.const.ospf.authentication import Authentication + from pcapkit.corekit.fields.misc import SchemaField + from pcapkit.corekit.fields.strings import BytesField + from pcapkit.protocols.schema.application.ospf import (CryptographicAuthentication, + ospf_auth_data_selector) + + crypto_field = ospf_auth_data_selector({ + 'auth_type': Authentication.Cryptographic_authentication, + }) + self.assertIsInstance(crypto_field, SchemaField) + self.assertIs(crypto_field.schema, CryptographicAuthentication) + + plain_field = ospf_auth_data_selector({'auth_type': Authentication.No_Authentication}) + self.assertIsInstance(plain_field, BytesField) + self.assertEqual(plain_field.length, 8) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/protocols/application/test_ospf_unit.py b/tests/protocols/application/test_ospf_unit.py index 48831004ec..7ba668809e 100644 --- a/tests/protocols/application/test_ospf_unit.py +++ b/tests/protocols/application/test_ospf_unit.py @@ -63,10 +63,10 @@ def test_ospf_properties_read_make_and_auth_helpers(self) -> None: from pcapkit.const.ospf.authentication import Authentication from pcapkit.const.ospf.packet import Packet from pcapkit.protocols.data.application.ospf import \ - CrytographicAuthentication as DataCryptoAuth + CryptographicAuthentication as DataCryptoAuth from pcapkit.protocols.application.ospf import OSPF from pcapkit.protocols.schema.application.ospf import \ - CrytographicAuthentication as SchemaCryptoAuth + CryptographicAuthentication as SchemaCryptoAuth from pcapkit.protocols.schema.application.ospf import OSPF as SchemaOSPF from pcapkit.utilities.exceptions import ProtocolError diff --git a/tests/protocols/link/test_link_unit.py b/tests/protocols/link/test_link_unit.py index f28215edf3..a64b49cfab 100644 --- a/tests/protocols/link/test_link_unit.py +++ b/tests/protocols/link/test_link_unit.py @@ -376,19 +376,13 @@ def test_l2tp_index_is_unsupported_and_make_data_preserves_flags(self) -> None: self.assertEqual(values['session_id'], 4) self.assertIn('payload', values) - def test_link_schema_callbacks_resolve_payload_and_auth_fields(self) -> None: - from pcapkit.const.ospf.authentication import Authentication + def test_link_schema_callbacks_resolve_payload_fields(self) -> None: from pcapkit.const.reg.ethertype import EtherType - from pcapkit.corekit.fields.misc import PayloadField, SchemaField - from pcapkit.corekit.fields.strings import BytesField + from pcapkit.corekit.fields.misc import PayloadField from pcapkit.corekit.module import ModuleDescriptor from pcapkit.protocols.link.ethernet import Ethernet from pcapkit.protocols.misc.raw import Raw from pcapkit.protocols.schema.link.ethernet import callback_payload - from pcapkit.protocols.schema.application.ospf import ( - CrytographicAuthentication, - ospf_auth_data_selector, - ) custom_type = EtherType.get(0x88B5) registry = Ethernet.__proto__ @@ -427,16 +421,6 @@ def test_link_schema_callbacks_resolve_payload_and_auth_fields(self) -> None: else: registry.pop(custom_type, None) - crypto_field = ospf_auth_data_selector({ - 'auth_type': Authentication.Cryptographic_authentication, - }) - self.assertIsInstance(crypto_field, SchemaField) - self.assertIs(crypto_field.schema, CrytographicAuthentication) - - plain_field = ospf_auth_data_selector({'auth_type': Authentication.No_Authentication}) - self.assertIsInstance(plain_field, BytesField) - self.assertEqual(plain_field.length, 8) - def test_link_base_layer_registry_and_protocol_reader(self) -> None: from pcapkit.const.reg.ethertype import EtherType from pcapkit.corekit.module import ModuleDescriptor