Repository navigation
Fix bugs from the fourth bug hunt: invisible characters, dates, numbers, CSV, CLI - #93
Merged
Merged
Conversation
Words with invisible characters or apostrophe variants: - Soft hyphens, zero-width spaces/joiners, word joiners and a BOM in the middle of the text, and the apostrophe typed as ‘ or `, are now part of a word in every phase. Before, the surname-plus-initials phase masked only the tail of such a surname and the main parser masked the whole word again, so unmasking could not restore it. - Surname masks are made from the word without these characters, and the mapping keeps the document spelling. Distinct spellings of one double surname get distinct masks. - Unmasking applies letter case past a leading invisible character. Surname stems: - Soft adjective surnames (Задорожній/Задорожнього/Задорожнім) share one stem, and soft endings become hard after a stem not ending in н. - Feminine dative adjective forms (Головатій) share the stem of the other forms. Dates and numbers: - A legal act date is kept only right after the act's name. The act's number, «України», a quoted title and a line break may come between; «законодавства», «законних» and a comma no longer count. - The spaced unit number form needs a «в/ч»/«частина» context, so years («У 2025 році») are no longer masked as units. NBSP and hyphen forms are recognised. - Old passports are masked when only passports are enabled. - Text dates in single quotes and without a space before the month are handled. The day always changes. Text dates no longer take quadratic time on long whitespace. - «№ 15.01.2025» is masked as a date. «БР566» and «БР: 566» are now recognised. Every number of a list of order numbers is masked. - A one-digit order number widens to two digits when every one-digit mask is taken, and the last generated attempt is checked too. Other: - CSV cells separated by a glued comma or quoted are read as separate words. - Lone CR and U+2028/U+2029 split lines, so such files are no longer processed as one long line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XT6iUWaQgahXDB9TWX9Bq7 Generated-With: Claude Code 2.1.42
- --log-file naming the input, the output or the mapping is a usage error (exit 2). Before, log lines were appended into the original document or the mapping. - Mapping loading: - a JSON object without 'mappings' is rejected; - a mask with no letter or digit is rejected; - a malformed legacy 're_mask_passes' is rejected; - a UTF-8 BOM is accepted; - the size limit is 30x the configured input limit, not a fixed 100 MB. - In automatic mode, data-unmask now honours -o and --map. - Non-text codecs (base64, hex, rot13) are a configuration error, and too deeply nested JSON is exit 1. Both were tracebacks before. - data-mask now rejects -o naming a directory or a missing directory before masking. - scrypt parameters are now limited by total work (n*r*p), so a forged .enc header can no longer stall decryption. - tests/test_fuzz_findings4.py: 79 tests for this round. - CHANGELOG entries under [Unreleased]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XT6iUWaQgahXDB9TWX9Bq7 Generated-With: Claude Code 2.1.42
The word-boundary classes now also cover invisible characters and apostrophe variants. That made each per-phrase pattern about twice as costly to compile, and large documents spent most of their extra time recompiling patterns evicted from the re cache. locate_words and the related helpers, and surname.document_contains, now share an LRU cache of compiled patterns. A 2000-line document is back to its previous time. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XT6iUWaQgahXDB9TWX9Bq7 Generated-With: Claude Code 2.1.42
The nesting depth at which Python raises RecursionError differs between versions and platforms: 700 levels failed on Python 3.11 here but not in CI. Test the main() wrapper directly instead, for both CLIs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XT6iUWaQgahXDB9TWX9Bq7 Generated-With: Claude Code 2.1.42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fourth bug hunt. It had three parts:
All entries are in
CHANGELOG.mdunder[Unreleased].Data left unmasked
‘or`typed instead of the apostrophe (ДЕМ‘ЯНЧУК) are common in text copied from Word, PDF or the web.ЗІНЧЕНКО→ЗІНЧЕНАНЕЦЬ), and the main parser then masked the whole word again, so unmasking could not restore it.Петренко,Іван,Петрович) and quoted cells ("Петренко";"Іван","Петренко","Іван") left the name open; they now separate words.законодавства,законнихand anyвідafter a comma (на підставі закону, рапорт від 01.02.2025), so those dates stayed open.України, a quoted title and a line break may come in between.відon the next line, is now kept as well (before, it was shifted).БР566andБР: 566are now masked.накази № 12, 13 та 14is masked, not only the first.наказ № 15.01.2025is masked as a date.в/ч А 1234with a non-breaking space andв/ч А-1234are now masked.--only passport, old passports (АА123456) are masked.Unmasking did not restore the original
№ 5) in a document with open numbered items 1–9 got a mask equal to an item number, and unmasking swapped them. It now gets a two-digit mask when every one-digit mask is taken.«28»вересня 2025) was masked but not restored.Masks
У 2025 році,В 2024 р.,А 2000 грнwere masked as military unit numbers (У 9309 році). A spaced letter-and-digits form is now a unit number only afterв/чorчастина.Задорожній/Задорожнього/Задорожнім) and feminine datives (Головатій) now share the stem of the other case forms.Command line
--log-filenaming the input, the output or the mapping is now an error (exit code 2). Before, log lines were written into the original document or the mapping.data-unmaskused to accept any JSON object as a mapping and report success. It now rejects:mappings;re_mask_passes.-oand--mapare no longer ignored.base64,hex,rot13);-onaming a directory or a missing directory..encfile are now limited by total work.Performance
Tests
tests/test_fuzz_findings4.pywith 79 tests. It does not import onmain(it needs the newclean_word).--re-maskchains: 0 failures in 320;Known limitations, not fixed
Кравець/Кравця,Лебідь/Лебедя,Соловей/Солов'я) still get a different stem per case.з 01.02 по 03.02.2025) stays open.Generated by Claude Code