Repository navigation
fix(corekit): StringField is not lossless: guessed charset, U+FFFD substitution, and a character-count length - #1336
Conversation
|
Cross-review verdict on 1. 2. The field can now raise on decode. With an explicit Verified as described: all three #1293 repros fail on Nits: |
…bstitution, and a character-count length - post_process returns a DecodedString -- a str subclass carrying the octets it decoded -- whenever re-encoding the text would not reproduce them: a detected charset other than UTF-8, an octet the charset cannot decode, a lossy errors scheme, or a URL quoting that differs from the original. pre_process packs those octets verbatim, so the field round-trips. - The decoded text is left exactly as it was, U+FFFD and all, which is what keeps the dumpers working: a lone surrogate there raises UnicodeEncodeError inside dictdumper for plist and tree. The decode fallback therefore stays 'replace', and the docstring describes that rather than claiming 'strict' raises. - length=-1 resolves to the encoded octet count, not the character count. Closes #1293
e28a639 to
b1f9072
Compare
|
Round-2 verdict on
Two PR-body figures to correct, neither blocking:
|
Searched for similar pull requests
Followed the coding style —
pylint,mypyandisortrun on both files: pylint reports the same six findings asmain's copy ofstrings.pyand no new one, mypy none, isort cleanmake testpasses, and a test case covers the change — legs belowAdded a changelog entry — N/A, added centrally after the wave
fix— corrects a defectStringFieldnow packs back exactly the octets it unpacked, while the decoded text is left exactly as it was. Where re-encoding the text would not reproduce its octets — a detected charset other than UTF-8, an octet the charset cannot decode, a lossyerrorsscheme, a URL quoting that differs —post_processreturns aDecodedString, astrsubclass carrying those octets, andpre_processpacks them verbatim.length=-1counts encoded octets. I kept the octets rather than recording the detected charset because a field is copied per packet, so a charset stored on unpack never reaches pack.Decode gained no new way to fail. The fallback stays
main's'replace', so an undecodable octet still reads as U+FFFD, and the docstring says that instead of claiming'strict'raises — namingidna, measured, as the codec that raises anyway because it supports no error handler. The'surrogateescape'fallback of my first revision was wrong twice: lone surrogates in the text raiseUnicodeEncodeErrorinsidedictdumperforplistandtree, and it is unsupported forutf-16{,-le,-be}/utf-32. Both are now pinned by tests, andplist/tree/jsondumps of a capture whose comments areff feandcaf\xe9are byte-identical tomain's.DecodedStringcompares and hashes as its text, deliberately: mixing.rawinto__eq__would makeinfo.comment == 'café'false against a plain literal, which is the comparison callers write. It is exported frompcapkit.corekit.fields.strings; adding it topcapkit.corekit.fields,pcapkit.corekitandpcapkit.allwould touch files this PR does not own, so I am reporting it rather than doing it.Repro,
e974e9undetected /caf\xe9as UTF-8 /-1with'héllo': beforec3a974/636166efbfbd/68c3a96c6c, aftere974e9/636166e9/68c3a96c6c6f. A 24,000-case fuzz over 8 field configurations gives 0 mismatches, 0 exceptions and 0 surrogate-bearing values. Per-frameinfoover all 23 fixture captures is identical before and after. Tests: 11 of 13 fail onmainand 5 fail on my first revision — including the dumper test — and all 13 pass now. Legs:corekit606 tests / 5 failures, all five pre-existing (test_sentinel_exports_unit, identical on an unpatched tree);protocols/internet503/0;protocols(rest) 850/0;dumpkit209/0;tests/project389 passed, 1 skipped.Callers (encoding): pcapng
CommentOption,IF_Name,IF_Description,IF_OS,IF_Hardware(utf-8);IPv4Record/IPv6Record.resol,NS_DNSNameOption.name(none, so chardet);TLSKeyLog,WireGuardKeyLog(ascii); MHmn_id_selectorNAI andServiceSelectionOption.identifier(none). The five without an encoding should declareencoding='utf-8'— caller work, in files this PR does not own.Refs #1277. Its repro is repaired by that
encoding='utf-8'alone: with it, the NRB record parses to('a.example', 'b.example')and rebuilds byte-exactly — onmain'sstrings.pytoo, so this PR is not what fixes it. A name that is not valid UTF-8 stays lossy either way, becauseIPv4Record.post_processsplitsresolwithstr.split, which returns plainstrand drops the octets.Refs #1276. The makers pass
length=len(str)explicitly, so this PR does not fix it.#1241 is unaffected: it goes through
Protocol.decode, notStringField.Closes #1293