Skip to content

About

Deterministic masking of personal data in Ukrainian military documents (names in all grammatical cases, ranks, tax IDs, passports, military IDs, units, order and BR numbers, dates) with exact restoration from a mapping file. CLI data-mask / data-unmask, Python 3.9+, Windows build.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

215 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Українська версія / Ukrainian version

Data Masking & Unmasking Scripts

A tool for consistent masking of personal data in Ukrainian military documents with exact restoration of the original. It recognises names in every grammatical case and word order (including forms of address without a surname), ranks, tax IDs, passports and military IDs, military units, order and BR numbers and dates; one person gets one mask throughout a document, and the mask keeps the grammatical form of the original. Restoration works from a mapping file (optionally encrypted) and does not depend on the algorithm version. Available as a CLI (data-mask / data-unmask), a Python module or a stand-alone Windows build; behaviour is configured in config.yaml and a private config_local.yaml.

Current version — see CHANGELOG.md or python data_masking.py -V.


📋 Project Structure

Since v3.0 all code lives in a single top-level package datamasking (pip-installable — see INSTALL.md).

Entry Points

  • data-mask / data-unmask / data-masking-diagnose — console scripts (after pip install)
  • python -m datamasking mask|unmask [args] — run as a module
  • data_masking.py — data masking (thin wrapper over datamasking.masking)
  • unmask_data.py — unmasking (thin wrapper over datamasking.unmasking)
  • diagnose_mapping.py — diagnostics, mapping comparison and recovery verification (wrapper over datamasking.diagnose)
  • __main__.py — run from the repository root: python . mask [args] / python . unmask [args]

Compatibility: the old flat import paths (import masking, import unmasking, from modules.tools import …, from rank_data import …) still work from a repository checkout via shims, but emit a DeprecationWarning; the shims will be removed in 4.0.0. They are not installed by pip install — use datamasking.* in new code.

datamasking.masking Package (masking core, v2.5.0+)

Module Description
constants.py MASK_* flags, patterns, name dictionaries, metadata
helpers.py Seed, mapping, instance tracking, normalization
language.py Gender, grammatical case, name declension
context.py Recognition of lines containing PIB (Прізвище Ім'я По-батькові), context parsing
mask_personal.py IPN, passports, surnames, first names, patronymics
mask_military.py Ranks, units, orders, BR, dates
engine.py Main engine: context-aware masking of text/JSON, initials
cli.py CLI, config, reports, main()

datamasking.unmasking Package (unmasking core, v2.5.0+)

Module Description
helpers.py Instance map, file pair lookup, mapping versions
engine.py Text/JSON recovery, re-mask chains
io.py Loading mapping (.json/.enc), schema validation
cli.py CLI, main()

Data

  • datamasking/rank_data.py — UAF ranks (army, navy, medical/legal service) with declensions; datamasking/extras/rank_data.py — re-export
  • datamasking/_version.py — single source of the package version

datamasking.extras Package (optional features)

Module Description
config.py YAML + ENV + CLI configuration with priorities (CLI > ENV > config_local.yaml > config.yaml > config.py > Default)
security.py AES-256-GCM encryption/decryption of mapping files
masking_logger.py Structured logging (JSON + colored console output)
selective.py --only / --exclude filters for selective masking
re_mask.py Chain re-masking (multi-pass) with chain tracking
tools.py Atomic masking functions for programmatic use (API)
password_generator.py Password generator (ASCII, Cyrillic, custom symbols)

Supporting Files

  • config.yaml — the shared configuration in the repository root, read automatically: every key with its default and the full exclusion lists (dictionaries)
  • config_local.example.yaml — template for your private config_local.yaml (not committed)
  • config_example.yaml / config_example.py — full configuration example with all options and their defaults
  • docs/config-examples/ — ready-made sets for typical scenarios: share, strict, pii

🚀 Quick Start

Install (v3.0+)

cd data-masking              # repository checkout
pip install ".[full]"        # double quotes work in bash, PowerShell and cmd alike
data-mask -i input.txt       # console scripts appear on PATH
data-unmask masked_file.txt

Running from source without installation also works — see below.

Masking

python data_masking.py -i input.txt

Result (in the current directory, or next to -o when given):

  • output_YYYYMMDD_HHMMSS_NNN.txt — masked text
  • masking_map_YYYYMMDD_HHMMSS_NNN.json — mapping for unmask (.enc with --encrypt; mode 0600)
  • masking_report_YYYYMMDD_HHMMSS_NNN.txt — report (skip with --no-report)

Unmasking

Automatic mode:

python unmask_data.py masked_file.txt

Manual mode:

python unmask_data.py masked_file.txt -m mapping_file.json

With encrypted mapping file (v2.3.0+):

python unmask_data.py masked_file.txt --map mapping.enc --password mypassword

With configuration file (v2.3.0+):

python unmask_data.py -c config.yaml

Result:

  • input_recovery_YYYYMMDD_HHMMSS.txt next to the masked file (or the -o path) — recovered text

Diagnostics

python diagnose_mapping.py mapping1.json mapping2.json   # compare mappings
python diagnose_mapping.py              # in a folder with input.txt + input_recovery_*.txt: verifies the recovery, compares the two latest mappings

📊 What Gets Masked

Personal Data

  • PIB (Прізвище Ім'я По-батькові) — with grammatical case and gender awareness
  • PIB with initials (v2.5.0+) — Іванов П.А., П. Агранов, К.П. Іванов, Т. А. Сидоренко, КОВАЛЕНКО І.В.; initials are preserved in the mapping and restored during unmask
  • Patronymics — masked while preserving gender and letter case
  • IPN — 10 digits
  • ID passports — 9 digits (first 3 + last are fixed, middle 5 are random)
  • Passports — АА123456, АА №123456
  • Military IDs — МТ123456, мт-123456, мт №123456

Military Data

  • Ranks — all grammatical cases, with "retired"/"reserve" modifiers; ranks have the masculine form only, for women as well (сержанту Коваль Тетяні Сергіївні), and the mask keeps it. A feminine form that happens to be in the input (сержантці) is recognised and restored as it was
  • Brigades — "123 окрема механізована бригада"
  • Military units — "в/ч А1234"
  • Order numbers — "наказ №123 від 01.01.2025"

Documents

  • BR numbers — 75/25/3400/Р, 818/856, 86319/06/1689/Р, 566
  • Dates — DD.MM.YYYY and D.M.YYYY (±30 days, never 0; also when glued to letters: 31.12.2024р.); written-out dates («06» жовтня 2025 року) get another day, month and ±1 year

Exceptions

  • Abbreviations (never masked as a surname) — ЗСУ, МОУ, ВСУ, ДПСУ, НГУ, ДСНС, СБУ, ГУР, ТЦК, СП, КМУ, ОТЦКСП, ТВО, ТРО
  • Words that are never part of a name — legal and service terms (Згідно, Відповідно, Закону, Статуту …), positions (Командир, Начальник …), labels (ПІБ, ІПН, РНОКПП …), "Кабінет Міністрів" in every case
  • Position titles and unit names in capitals — three or more words in capitals without a patronymic and without a rank (МАСКУВАЛЬНИКОМ ІМІТАЦІЙНОГО ВІДДІЛЕННЯ), two words in capitals whose second word is not a known first name (ЗАХИСНИКУ ВІТЧИЗНИ), words whose endings do not fit a name (Імітаційного, Відділення); headings in capitals (НАКАЗ КОМАНДИРА ВІЙСЬКОВОЇ ЧАСТИНИ)
  • Dates of legal acts — a date after "Закону / Кодексу / Конституції / Указу Президента / постанови Кабінету Міністрів, Верховної Ради, КМУ … від" is not shifted

The full lists are in config.yaml (section dictionaries, editable; the program keeps a built-in copy for when the key is missing); the exclusions section of the configuration adds to them (abbreviations, words, phrases, legal acts, words that are always masked) or removes entries. data-mask --list-exclusions prints what is in effect.


🎯 Features

Instance Tracking

Each occurrence is tracked individually:

Вхід:  "Іванов Петро Іванович і Петров Олег Петрович. Потім Іванов пішов, а Петров лишився."
Маска: "Івенов Павло Леонідович і Пергов Омелян Степанович. Потім Івенов пішов, а Пергов лишився."

A surname is recognised as part of a name (Прізвище Ім'я По батькові, Ім'я По батькові Прізвище, or after a rank); once it is known in the document, every other occurrence of it — including other case forms — gets the same synthetic stem, and unmask restores each occurrence by its instance number. A first name with a patronymic and no surname is masked too (3.1.13): Шановний Олегу Петровичу! → Шановний Омеляну Степановичу!, за підписом Олега Петровича → за підписом Омеляна Степановича. A capitalised word in front of a full name (Заява Петренка Олега Петровича, Характеристика Коваль Тетяни Сергіївни) is left alone and the name after it is masked in full.

Position titles and unit names written in capitals are not names (3.1.17–3.1.18): three or more words in capitals in a row without a patronymic and without a rank in front (на посаду МАСКУВАЛЬНИКОМ ІМІТАЦІЙНОГО ВІДДІЛЕННЯ ІНЖЕНЕРНО-МАСКУВАЛЬНОГО ВЗВОДУ, з посади СТАРШОГО ОПЕРАТОРА РОЗРАХУНКУ) stay as they are, and the real name next to them (… солдата ПЕТРЕНКА ОЛЕГА ПЕТРОВИЧА) is still masked. Word endings must fit the roles: a first name never ends in -ого/-ому (Імітаційного, Старшого) and ends in -их/-ів only if it is a known name (Яків); a surname in the instrumental (Начальником) needs a first name in the instrumental; words in -ння/-ття/-ість (Відділення, Відповідальність) are neither names nor surnames. Two words in capitals without a rank and without a patronymic are a name only when the second word is a known first name (КОВАЛЬ ТЕТЯНА yes, ПОМІЧНИКА ГРАНАТОМЕТНИКА and a rare ПЕТРЕНКО АЙДЕР no; Петренко Айдер and рядового ПЕТРЕНКА АЙДЕРА are masked). Known limitation: in ВРУЧИТИ ГРАМОТУ ОЛЕГУ ПЕТРОВИЧУ ПЕТРЕНКУ the first name and patronymic are masked, the surname after them is not — a word in capitals before a name-first order cannot be told from a surname.

Preserving Grammatical Forms

Ranks are recognised next to a name (or in quotes as a standalone value); a rank word in running text (рядовий склад, Командир роти капітан) is left as it is — and masks never collide with it. Real output (3.1.x):

Rank declension (the surname keeps its case ending):

"молодшому сержанту Іванову …"   → "старшому солдату Івенову …"    (давальний)
"молодшого сержанта Іванова …"   → "старшого солдата Івенова …"    (родовий)
"молодшим сержантом Івановим …"  → "старшим солдатом Івеновим …"   (орудний)

First names and patronymics (3.1.12) follow the case of the original, and all case forms of one name share one mask — the mask is derived from the nominative:

"капітан Петренко Петро Іванович"          → "лейтенант Пебоженко Павло Леонідович"
"капітана Петренка Петра Івановича"        → "лейтенанта Пебоженка Павла Леонідовича"
"капітану Петренку Петру Івановичу"        → "лейтенанту Пебоженку Павлу Леонідовичу"
"капітаном Петренком Петром Івановичем"    → "лейтенантом Пебоженком Павлом Леонідовичем"
"сержанта Коваль Тетяни Сергіївни"         → "старшого сержанта Ковар Катерини Павлівни"
"сержантом Коваль Тетяною Сергіївною"      → "старшим сержантом Ковар Катериною Павлівною"

The gender and case of a first name are taken from the patronymic (Петра Івановича is a masculine genitive, not the feminine name Петра; Наталі Петрівни is genitive, Наталі Петрівні dative); without a patronymic — from the adjacent rank (рядового Петренка Богуслава → genitive of Богуслав). Forms in -ові (Петрові → Павлові), the vocative (Петре), the third declension (Любові → Лариси) and consonant alternations (Ользі → Ольга) are recognised. Unmasking does not depend on this: it restores by the mapping.

Women (the rank keeps its masculine form, as in documents; the first name and patronymic follow the gender):

"молодшим сержантом Коваленко Марією Петрівною"  → "старшим солдатом Котвиненко Марусею Гордіївною"
"капітану Коваленко Марії Петрівні"              → "лейтенанту Котвиненко Марусі Гордіївні"

Additional modifiers:

"Солдату у відставці Петренку …"   → "Рядовому у відставці Пебоженку …"
"Сержанту в запасі Петренку …"     → "Старшому сержанту в запасі Пебоженку …"
"Капітану на пенсії Петренку …"    → "Лейтенанту на пенсії Пебоженку …"

Surname masks (v3.0.8, rule refined in v3.0.16)

A surname mask keeps up to the first 3 characters of the original, the rest is synthetic, and the grammatical ending is preserved. The prefix and the preserved ending together stay within half of the surname, so long endings shorten the prefix, but at least the first 2 letters are always kept: Коваль → Ковар, Іванов → Івенов, Ґудзь → Ґубко, Петренку → Пебоженку (the -енко ending is already half of the word, so only the minimum is kept). All letter-case and grammatical-case forms of one surname share one synthetic stem (МАЗУРЕНКА / Мазуренко → МАЛІЖЕНКА / Маліженко). Configure with masking_rules.surname_prefix_length (maximum, 0 = fully synthetic) and surname_prefix_min (minimum, default 2; 0 = the half rule alone, as before 3.1.4) and the faker dictionaries with system.faker_locale (default uk_UA; grammar stays Ukrainian).

Case Preservation

"ІВАНОВ ІВАН ІВАНОВИЧ"  → "ІВЕНОВ ІГОР ЛЕОНІДОВИЧ"
"Іванов Іван Іванович"  → "Івенов Ігор Леонідович"

Names are recognised by their capital letter; an all-lowercase іванов іван is not a name for the parser and stays as it is.

Deterministic Generation

The system generates identical mapping files when processing identical input data:

# Запуск 1:
"Іванов" → blake2b hash → seed → "Петренко"

# Запуск 2 (той самий файл):
"Іванов" → blake2b hash → seed → "Петренко"  # Той самий результат!

Advantages:

  • One original = one mask (always)
  • Results are predictable and reproducible
  • You can re-run on the same file and get the same mapping
  • Unmask works consistently

Technical details:

  • Uses hashlib.blake2b() for seed generation
  • Faker and random are initialized with a deterministic seed

🔧 Modules v2.3.0

Mapping File Encryption (datamasking/extras/security.py)

Mapping files can be encrypted with AES-256-GCM:

# Маскування з шифруванням
python data_masking.py -i input.txt --encrypt --password-env DATA_MASKING_PASSWORD
# Результат: masking_map_*.enc — plaintext .json НЕ створюється (v3.0.1+)
# Той самий DATA_MASKING_PASSWORD читає і unmask_data.py

# Розмаскування з шифрованим mapping
python unmask_data.py masked.txt --map mapping.enc --password mypassword

Configuration (datamasking/extras/config.py)

Priority: CLI > ENV > config_local.yaml > config.yaml > config.py > Default. Keys left out of the file keep their defaults.

Shared configuration — config.yaml in the repository root (and next to the .exe in the Windows release) is read automatically when the program runs from that directory. It lists every key with its default and the full exclusion lists (dictionaries), so nothing has to be looked up in the code.

Private overrides — config_local.yaml. Keep shared settings in config.yaml and private ones (call signs, local abbreviations, your own paths) in config_local.yaml. It is looked up in the user config directory (~/.config/data-masking/, $XDG_CONFIG_HOME/data-masking/, %APPDATA%\data-masking\ on Windows) and next to config.yaml (or in the current directory); the one next to config.yaml is applied last. Every key set to a non-null value replaces the shared value (sections merge key by key, lists are replaced as a whole); null keeps the shared value. Start from config_local.example.yaml: cp config_local.example.yaml config_local.yaml. The file is in .gitignore; keep it chmod 600 (the program warns otherwise). --config-local FILE uses a specific file instead, --no-local-config ignores them; both work for data-mask and data-unmask.

data-mask --init-config                                  # write a short config.yaml (the repository config.yaml lists every key)
data-mask -i input.txt --config config.yaml              # ./config.yaml is also picked up automatically
data-mask -i input.txt --config docs/config-examples/share.yaml
data-unmask output.txt -c config.yaml
data-mask -i input.txt --config-local private.yaml       # private overrides from a specific file
data-mask --list-exclusions                              # exclusions in effect

All keys with explanations: config_example.yaml. Ready-made sets: docs/config-examples/ (share — keep dates and document numbers, strict — mask everything, fully synthetic surnames, pii — personal data only).

Selective Masking (datamasking/extras/selective.py)

# Маскувати тільки ІПН та паспорти
python data_masking.py -i input.txt --only ipn,passport   # кома або пробіл; невідомий тип — помилка (v3.0.1+)

# Маскувати все крім дат
python data_masking.py -i input.txt --exclude dates

Chain Re-masking (datamasking/extras/re_mask.py)

For re-masking already masked files while preserving the full mapping chain:

from datamasking.extras.re_mask import ReMasker, MappingChain

chain = MappingChain()
remasker = ReMasker(chain=chain, mask_function=my_mask_fn)

Programmatic API (datamasking/extras/tools.py)

Atomic functions for integration into external applications:

from datamasking.extras.tools import mask_ipn_direct, mask_rank_direct, mask_pib_force

result = mask_ipn_direct("1234567890", masking_dict, instance_counters)

Password Generator (datamasking/extras/password_generator.py)

from datamasking.extras.password_generator import generate_password

# Стандартний пароль (24 символи)
pwd = generate_password()

# З кирилицею
pwd = generate_password(length=32, include_cyrillic_lower=True)

# CLI
# python -m datamasking.extras.password_generator --length 32 --cyrillic

Logging (datamasking/extras/masking_logger.py)

Structured logging with JSON and colored console output:

from datamasking.extras.masking_logger import MaskingLogger, setup_logging

logger = MaskingLogger("masking")
setup_logging(level="DEBUG", format_type="json")

⚙️ Configuration

Categories are switched in the YAML configuration (or for one run with --only / --exclude); no code changes are needed:

masking_rules:
  enable_names: true        # ПІБ (прізвища, імена, по батькові, ініціали)
  enable_ipn: true          # ІПН (10 цифр)
  enable_passport: true     # паспорти та ID-картки
  enable_military_id: true  # військові квитки
  enable_ranks: true        # звання
  enable_units: true        # військові частини
  enable_brigades: true     # бригади
  enable_orders: true       # номери наказів
  enable_br_numbers: true   # БР номери
  enable_dates: true        # дати
  surname_prefix_length: 3  # літер оригінального прізвища в масці (0 = жодної)
  surname_prefix_min: 2     # мінімум, навіть для «-енко» (0 = як до 3.1.4)
system:
  hash_algorithm: "blake2b"
security:
  encrypt_output: false     # true = те саме, що --encrypt

See config_example.yaml for every key and docs/config-examples/ for ready-made scenarios.

Hash algorithm comparison:

Algorithm Speed Security Usage
blake2b ⚡⚡⚡⚡⚡ ✅ High Recommended (default)
md5 ⚡⚡⚡⚡⚡ ⚠️ Low for cryptography Fastest, sufficient for seed
sha1 ⚡⚡⚡⚡ ⚠️ Deprecated Fast, better than md5
sha256 ⚡⚡⚡ ✅ High Popular, Bitcoin
sha512 ⚡⚡ ✅ Maximum Slowest

📝 Masking Examples

Example 1: Declensions and additional modifiers

Input:

Молодшому сержанту у відставці СИЧУ Роману Сергійовичу (Житомирський ОТЦКСП),
звільненому 31.05.2025, який є особою з інвалідністю III групи.

Result:

Старшому солдату у відставці БОЙКО Павлу Сергійовичу (Житомирський ОТЦКСП),
звільненому 15.06.2025, який є особою з інвалідністю III групи.

Example 2: Feminine forms and documents

Input:

Капітанці медичної служби Коваленко Марії Іванівні, ІПН 1234567890,
ID-паспорт 123456789, видано наказом №75/25/3400/Р від 26.06.2025.

Result:

Майорці медичної служби Петренко Оксані Іванівні, ІПН 9876543210,
ID-паспорт 123947568, видано наказом №59/87/4249/Р від 10.07.2025.

Example 3: Signature format

Input:

Командир 72 окремої механізованої бригади
полковник                           Сергій ЗАПОРОЖЕЦЬ

Result:

Командир 85 окремої механізованої бригади
підполковник                        Павло ЗАВОДСЬКИЙ

Example 4: Abbreviations are not masked

Input:

Капітан ЗСУ Іванов Петро Миколайович, військовий квиток МТ123456,
проходив службу у МОУ, нагороджений СБУ за заслуги перед ГУР.

Result:

Майор ЗСУ Сидоров Андрій Миколайович, військовий квиток МТ654321,
проходив службу у МОУ, нагороджений СБУ за заслуги перед ГУР.

Example 5: Multiple PIB in one line

Input:

Сержанту БОНДАРЕНКО Олегу та молодшому сержанту КОВАЛЕНКО Андрію,
а також рядовому ШЕВЧЕНКО Петру видати по 100 грн.

Result:

Старшому сержанту БОСИЛЕНКО Омеляну та старшому солдату КОТВИНЕНКО Артему,
а також старшому солдату ШЕМАЧЕНКО Павлу видати по 100 грн.

📝 Mapping File Format

{
  "version": "3.1.20",
  "mappings": {
    "rank": {
      "молодший сержант": {
        "masked_as": "старший солдат",
        "instances": [1, 3]
      }
    },
    "surname": {
      "Іванов": {
        "masked_as": "Івенов",
        "instances": [1, 2]
      }
    },
    "passport_id": {
      "123456789": {
        "masked_as": "123947568",
        "instances": [1]
      }
    }
  },
  "instance_tracking": {
    "старший солдат": 3,
    "Івенов": 2,
    "123947568": 1
  }
}

📚 Supported Ranks

Army (27 ranks)

Enlisted: рядовий, солдат, старший солдат, ефрейтор

Sergeants: молодший сержант, сержант, старший сержант, головний сержант, штаб-сержант, майстер-сержант, старший майстер-сержант, головний майстер-сержант

Officers: молодший лейтенант, лейтенант, старший лейтенант, капітан, майор, підполковник, полковник

Generals: бригадний генерал, генерал-майор, генерал-лейтенант, генерал

Navy (22 ranks)

Sailors: матрос, старший матрос

Petty Officers: головний корабельний старшина, головний старшина, старшина 1/2 статті

Officers: молодший лейтенант, лейтенант, старший лейтенант, капітан-лейтенант

Captains: капітан 3/2/1 рангу

Admirals: контр-адмірал, віце-адмірал, адмірал

Special Services

Medical: капітан/майор/підполковник/полковник медичної служби

Legal: капітан/майор/підполковник/полковник юстиції

Supported Grammatical Cases

  • Називний (nominative): хто? що? — "солдат"
  • Родовий (genitive): кого? чого? — "солдата"
  • Давальний (dative): кому? чому? — "солдату"
  • Орудний (instrumental): ким? чим? — "солдатом"

🔐 Security

Recommendations:

  1. Store mapping files in a secure location
  2. DO NOT transmit mapping files together with masked data
  3. Delete mapping files after work is complete
  4. Use different mappings for different documents
  5. Use mapping file encryption (v2.3.0+): --encrypt --password
  6. Unmask is ONLY possible with the mapping file
  7. Pass the password via --password-env VAR_NAME instead of --password (command-line arguments are visible in shell history and process lists)

⚠️ Threat Model and Limitations

This is pseudonymization, not full anonymization. Consider the following:

  • Partial preservation of the original. IPN retains the first 3 and last digit (4 out of 10), passport retains 4 out of 9, surnames keep 2–3 leading letters and the grammatical ending (surname_prefix_length: 0 removes the prefix). This is intentional (preserves format and partial context) but allows re-identification by brute-forcing against a candidate dictionary.
  • Deterministic generation without a secret. Masks are derived from the blake2b hash of the original without salt/key. Anyone who knows the algorithm and has a list of possible originals can match masks without the mapping file.
  • Ranks are shifted by only ±1-2 positions in the hierarchy — the approximate rank of the person remains visible.
  • Dates are shifted by ±30 days — the time period of events remains recognizable.

Conclusion: masked files protect against accidental disclosure and are suitable for sharing with a limited audience, but are NOT designed for publication against a motivated adversary with knowledge of the context.


🛠️ Troubleshooting

Unmask does not restore correctly

  • Check the mapping version (2.1 or any 3.x; 1.x mappings are restored with the v1 logic)
  • Use the latest script versions

Ranks are not masked

  • Make sure masking_rules.enable_ranks: true and that the rank stands next to a name
  • Update to the latest version

Abbreviations are being masked

  • Update to v2.1.16+ with whitelist support

ID passports are not masked

  • Make sure masking_rules.enable_passport: true
  • ID passports = 9 digits (not 10 like IPN)

📦 Usage in Projects

Git Submodule (recommended)

# Add as submodule
git submodule add https://github.com/click0/data-masking.git

# Update submodule
git submodule update --remote data-masking

# Use in project
cd data-masking
python data_masking.py -i ../documents/input.txt

Standalone

# Clone separately
git clone https://github.com/click0/data-masking.git
cd data-masking
python data_masking.py -i input.txt

📜 License

BSD 3-Clause "New" or "Revised" License


👨‍💻 Author

Vladyslav V. Prodan

  • GitHub: @click0
  • Phone: +38(099)6053340

📅 Version

See CHANGELOG.md for version history and release notes.

About

Deterministic masking of personal data in Ukrainian military documents (names in all grammatical cases, ranks, tax IDs, passports, military IDs, units, order and BR numbers, dates) with exact restoration from a mapping file. CLI data-mask / data-unmask, Python 3.9+, Windows build.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages