diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b953c59d..cabc3b53e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,7 @@ This is the resolution of #548, which reported `TransType.L2TP` (115) as registe - **Fixed** -- `httpv1`'s `_RE_METHOD` was unanchored and `re.match` anchors only at the start, so it prefix-matched, and the request-line reader then passed the whole `para1` to `Method.get` rather than the captured `method` group. Together those meant `b'Get'` matched on the single character `G`, satisfied the guard that decides a start-line is a request, and handed the entire mixed-case token to a lookup that raised on it. Fixing either half alone still gives a wrong answer -- normalising the lookup would parse `b'Get'` as `GET` off a one-character match, and passing the group would parse it as a method named `G`. The pattern is now anchored at both ends and the captured group is what is looked up, so a token that is not a method is a malformed request line rather than a mis-parsed one. Method tokens are case-sensitive per [RFC 9110 Section 9.1](https://datatracker.ietf.org/doc/html/rfc9110#section-9.1), so no `re.I` was added: `GET` parses, `Get` and `get` are rejected (#583). - **Fixed** -- `_RE_STATUS` in the same reader carried the same unanchored prefix defect, found by auditing `_RE_METHOD`'s siblings, and it escaped as the wrong exception type. That pattern is only a guard -- the value is taken from `int(para2)` on the raw token -- so a prefix match let a malformed status past the guard and then out of `int()` uncaught, where `_read_http_header` documents `ProtocolError`. Measured: a status of `200x` raised `ValueError: invalid literal for int() with base 10: b'200x'`, and one of `2000` raised `ValueError: 2000 is not a valid StatusCode`; both are now `ProtocolError`. [RFC 9112 Section 4](https://datatracker.ietf.org/doc/html/rfc9112#section-4) gives `status-code = 3DIGIT`, exactly three, so the anchor is what the grammar already said -- the production lives in HTTP/1.1 because `status-code` is part of its `status-line`, while [RFC 9110 Section 15](https://datatracker.ietf.org/doc/html/rfc9110#section-15) covers the code semantics and the IANA registry rather than the syntax. `_RE_VERSION` was audited at the same time and is safe as it stands, because both of its call sites read the captured group rather than the raw token (#583). - **Fixed** -- `get()`'s documented `default` was ignored on the integer path throughout the generated `pcapkit.const` tree, because `get` delegated the lookup to the enum call and `_missing_` has no access to the caller's `default` -- so `Hardware.get(99999, 0)` raised `ValueError: 99999 is not a valid Hardware` instead of returning the fallback it was handed. The integer path now consults `default` before letting the lookup error escape. `-1`, the placeholder the generated signature already carried, is what separates "no default was supplied" from "a default was supplied and should be used", so a caller that asked for no fallback still gets the error rather than a silent substitution. The sweep #584 asked for puts the scope at 110 of the 118 integer registries, not the three the issue named; the two carrying a bespoke integer fallback of their own, `pcapng` `OptionType` and `reg` `AppType`, are deliberately left alone, since neither drops a default by raising. Not reachable from wire data -- every value a wire field can carry already resolves -- so this is a contract fix rather than a parse fix. Applied to the nine vendor templates as well as the 113 generated modules, and a new test renders the shared template and compares it against the module generated from it, so a regeneration cannot quietly undo it (#584). +- **Added** -- `CITATION.cff`, citation metadata in Citation File Format 1.2.0, which GitHub renders as the repository's "Cite this repository" button and which citation managers and dependency inventories read directly. It is the machine-readable half of the attribution BSD-3-Clause already asks for, so credit carries into a paper or a bill of materials rather than depending on a reader opening `LICENSE`. Validated with `cffconvert --validate` and against the published 1.2.0 schema; `doi` and `orcid` are omitted rather than invented, since neither exists for this project today and both are checked formats, so a wrong value would still validate. The licence itself is deliberately unchanged -- still BSD-3-Clause, no `NOTICE` file, no change to its terms. Alongside it the copyright line moves from `2018-2023` to `2018-2026` -- `LICENSE` was its only occurrence in the tree, since `docs/source/conf.py` already derives its own from the current year -- and a stray `s` after the closing `DAMAGE.` of the licence text, present since the Mozilla-to-BSD relicence, is removed, so the wording now matches canonical BSD-3-Clause exactly (#615). - **Fixed** -- reading a big-endian classic PCAP byte-swapped every record header field, and then crashed. `Frame.unpack` seeded the file's declared byte order under the key `bytesorder` where the frame schema's `byteorder_callback` reads `byteorder`, so the lookup never found it and always fell back to `sys.byteorder` -- the reading host's order rather than the file's. On a little-endian host reading a little-endian capture that fallback gives the right answer by coincidence, and every capture in this repository was little-endian, so the wrong code path has always produced correct results. Against a big-endian capture, measured before the fix, frame 1 of `big_endian.pcap` read `ts_sec=3106905`, `ts_usec=1088553216` and `incl_len=1241513984` for a record whose real values are `1500000000`, `123456` and `74`, dating the frame to 1970-02-05 rather than to 2017-07-14. `incl_len` is the payload length, so that first record then consumed the whole file and the second was read with a negative payload length, raising `ValueError: read length must be non-negative or -1` out of the schema -- which is the reported crash, and it is the *second* symptom rather than the first. The sibling `Frame.pack` eleven lines earlier spelled the key correctly, which is what marks this as a slip rather than a second key, and the fallback is what made a misspelled key indistinguishable from an absent one; `byteorder_callback` now records that it is the definition of the key and why the fallback hides a typo (#605). - **Added** -- `examples/generators/endian.py`, and the byte-order tests that read what it writes. There was no big-endian `.pcap` in the repository at all, which is why #605 survived its own code review: the one-character fix leaves the corrected path exactly as untested as the broken one. The generator writes three captures -- `big_endian.pcap` (magic `a1 b2 c3 d4`), `big_endian_nanosecond.pcap` (`a1 b2 3c 4d`, the first fixture to take that branch of the magic-number table) and `little_endian.pcap` (`d4 c3 b2 a1`) -- carrying the *same three records* in each container, so the tests can assert that the byte order makes no difference to what is read out rather than only that the big-endian file matches numbers written down in a test. Frame 3 is captured short, 1200 octets on the wire cut to a 96-octet `snaplen`, so `incl_len` and `orig_len` differ and cannot both be satisfied by one byte-swapped value. `test_frame_endian_runtime.py` drives all three through `extract()` and walks each file's record chain with `struct` to derive its own expectations; a unit-tier case in `test_header_frame_unit.py` builds a two-record big-endian capture in memory instead, so the regression is also caught by the fixture-free selection CI runs on every push. All four fail on the unfixed tree -- the three fixture-backed ones by that `ValueError`, the in-memory one by `AssertionError: 3106905 != 1500000000` -- while the little-endian twin passes on both trees, which is what shows the records themselves are not the variable (#605). - **Changed** -- `tests/protocols/transport/test_tcp_udp_unit.py` now reaches the MP_JOIN dispatchers through `TCP()` itself, instead of assigning a Python `set` to `_flags` on a bare `TCP.__new__(TCP)`. A `set` answers the membership tests `_make_mptcp_join` and `_read_mptcp_join` use, so every flag branch ran and both TCP modules read 100% statement and branch coverage -- while the attribute had neither the `aenum.IntFlag` type production assigns nor the ordering that governs when it exists at all, which is how #587 stayed invisible behind that number and how the `cast('Enum_Flags', 0)` no-op behind it went unnoticed too. Measured on the rewrite, against the 17 tests of that file: revert #587's hoist and two of them fail with `AttributeError: 'TCP' object has no attribute '_flags'` where all 17 passed before; restore the `cast` and two fail with `TypeError: argument of type 'int' is not a container or iterable`, again where all 17 passed. The library is unchanged and the file's tests still pass, so the coverage numbers do not move -- the point is what the same numbers are now worth (#603). diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 000000000..8c655e45c --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,40 @@ +# Citation metadata for PyPCAPKit, in Citation File Format 1.2.0. +# c.f. https://citation-file-format.github.io +# +# This is the machine-readable half of the attribution that BSD-3-Clause asks for. +# GitHub renders it as the repository's "Cite this repository" button, and citation +# managers, Zenodo and dependency inventories read it directly -- so credit carries +# into a paper or a bill of materials instead of depending on a reader noticing +# ``LICENSE``. It grants nothing and restricts nothing; the licence is unchanged. +# +# ``version`` and ``date-released`` describe the newest *published* release, so they +# move with ``pcapkit.__version__`` in ``pcapkit/__init__.py`` and the matching entry +# under ``docs/source/changelog/`` when a release goes out. +# +# No ``doi`` and no ``orcid``: neither exists for this project or its author today, +# and both fields are checked formats rather than free text, so an invented value +# would validate and still be wrong. They are omitted rather than guessed. +cff-version: 1.2.0 +message: If you use this software, please cite it as below. +title: PyPCAPKit +abstract: >- + PyPCAPKit is a comprehensive, Python-native network packet analysis library: a + PCAP file extraction, construction and analysis toolkit that dissects protocols + in more detail than the common extractors do, over a choice of extraction + engines, and exposes them through a Pythonic interface. +type: software +authors: + - given-names: Jarry + family-names: Shaw + email: jarryshaw@icloud.com + alias: JarryShaw +repository-code: https://github.com/JarryShaw/PyPCAPKit +url: https://jarryshaw.github.io/PyPCAPKit/ +repository-artifact: https://pypi.org/project/pypcapkit/ +license: BSD-3-Clause +version: 1.5.0b4 +date-released: '2026-09-20' +keywords: + - network + - pcap + - packet diff --git a/LICENSE b/LICENSE index fd9e06d0d..5d8442d77 100755 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2018-2023, Jarry Shaw +Copyright (c) 2018-2026, Jarry Shaw All rights reserved. Redistribution and use in source and binary forms, with or without @@ -26,4 +26,4 @@ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.s +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/docs/source/changelog/1.5.0.rst b/docs/source/changelog/1.5.0.rst index 985d6578c..d2edfa714 100644 --- a/docs/source/changelog/1.5.0.rst +++ b/docs/source/changelog/1.5.0.rst @@ -814,6 +814,22 @@ pull requests between #326 and #509. well as the 113 generated modules, and a new test renders the shared template and compares it against the module generated from it, so a regeneration cannot quietly undo it (#584). +* **Added** -- ``CITATION.cff``, citation metadata in Citation File Format + 1.2.0, which GitHub renders as the repository's "Cite this repository" button + and which citation managers and dependency inventories read directly. It is + the machine-readable half of the attribution BSD-3-Clause already asks for, so + credit carries into a paper or a bill of materials rather than depending on a + reader opening ``LICENSE``. Validated with ``cffconvert --validate`` and + against the published 1.2.0 schema; ``doi`` and ``orcid`` are omitted rather + than invented, since neither exists for this project today and both are + checked formats, so a wrong value would still validate. The licence itself is + deliberately unchanged -- still BSD-3-Clause, no ``NOTICE`` file, no change to + its terms. Alongside it the copyright line moves from ``2018-2023`` to + ``2018-2026`` -- ``LICENSE`` was its only occurrence in the tree, since + ``docs/source/conf.py`` already derives its own from the current year -- and a + stray ``s`` after the closing ``DAMAGE.`` of the licence text, present since + the Mozilla-to-BSD relicence, is removed, so the wording now matches canonical + BSD-3-Clause exactly (#615). * **Fixed** -- reading a big-endian classic PCAP byte-swapped every record header field, and then crashed. ``Frame.unpack`` seeded the file's declared byte order under the key ``bytesorder`` where the frame schema's ``byteorder_callback``