Skip to content

fix(engines): the unpinned Scapy extra admits scapy 2.5.0, which misreads PCAP-NG timestamps and reshapes packet2dict output #1572

Closed

Description

@JarryShaw

Describe the bug
pyproject.toml:142 declares Scapy = [ "scapy" ], so scapy 2.5.0 satisfies it. On 2.5.0, main fails three ways. On 2.6.0, 2.6.1, 2.7.0 and 2.8.0 it passes. CI never sees this because every leg resolves the latest scapy.

Reproduction
Linux, CPython 3.14.8, fresh venv with scapy==2.5.0, main at a3da376dd:

  1. tests/toolkit/test_scapy_packet2dict_unit.py::ScapyPacket2DictTests::test_dns_records_are_converted fails with KeyError: 0 at :110.
  2. tests/toolkit/test_engine_pcap_trace_runtime.py::EnginePCAPTraceTests::test_dict_capable_trace_formats_are_unchanged (scapy): the IPv4 flow hashes to b2a5ea79…, not 7679cddf….
  3. tests/foundation/engines/test_engine_agreement_runtime.py, profile.pcapng/scapy/timestamps and /ipv4 give 1500000000.0005 != 1500000.0000005.

Root causes

  1. In 2.5.0, DNS.qd/an hold one record, with the rest chained on as payloads (scapy/layers/dns.py:377-398, :487-488). In 2.6.0 they are PacketListFields (dns.py:1292-1293). packet2dict (pcapkit/toolkit/scapy.py:137-152) converts either form faithfully, so on 2.5.0 qd comes out as a dict instead of a list. This is a scapy API change.
  2. 2.5.0 copies list defaults into Packet.fields before it dissects (scapy/packet.py:151, :282-289), which puts IP's options key first. 2.6.0 skips that copy when dissecting (packet.py:170, :303-305). packet2dict keeps the fields order (toolkit/scapy.py:148). The JSON content is identical; only the key order differs.
  3. This one is scapy's own bug. 2.5.0's _read_options stops at a comment option that has no newline (scapy/utils.py:1544-1549). It therefore never reads the if_tsresol option (9) that follows, and the nanosecond interface is read as microseconds. pcapkit reads that tsresol correctly (pcapkit/foundation/engines/scapy.py:124). 2.6.0 rewrote the parser (utils.py:1793-1836).

Expected behavior
pip install pypcapkit[Scapy] should never resolve a scapy that misreads captures. 2.6.0 is the first release that fixes all three (there is no 2.5.x after 2.5.0). It declares Requires-Python >=3.7 and installs and imports on 3.10-3.14. Expected: floor the extra at scapy>=2.6.0, with a python_version >= '3.7' marker because requires-python is >=3.6, as PCAP_CT does. Apply the same floor to the bare scapy in the dev extra (pyproject.toml:258). State it in the docs' engine-prerequisites note (docs/source/index.rst:436-441), which currently says scapy needs nothing further. Confirmed independently by the #1563 cross-review, which measured the same three failures on main under 2.5.0.

Activity

  1. added
    fixPull requests that fix a defect (fix: subject prefix)
    wipWork in flight - a covering PR is open or an agent is actively on it
    on Oct 10, 2026
  2. removed
    wipWork in flight - a covering PR is open or an agent is actively on it
    on Oct 10, 2026
  3. added this to the PyPCAPKit v1.5 milestone on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    fixPull requests that fix a defect (fix: subject prefix)

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions