morok. Руководство пользователя
morok — утилита командной строки для подготовки обезличенных копий данных для разработки и тестирования. Она читает таблицы (CSV, XLSX, JSON, JSONL, PostgreSQL, дамп PostgreSQL), заменяет значения полей по правилам из конфигурации и записывает результат в отдельное место. Исходные данные не изменяются.
morok — инструмент подготовки тестовых данных. Он не является средством защиты информации: программа не шифрует данные, не ограничивает доступ к ним и не определяет, какие данные считать персональными. Состав правил определяет пользователь. Решение о том, достаточно ли выбранных способов для вашей задачи, принимает пользователь (оператор данных).
Документ описывает поведение версии, в которой есть команды run, validate, scan и version. Установка и эксплуатация описаны в руководстве администратора. Пошаговая проверка за 5–10 минут на демо-данных — в сценарии проверки.
Содержание
- Назначение и принципы работы
- Установка
- Быстрый старт
- Конфигурация
- Форматы источников
- Методы обезличивания
- Домены и согласованность связей
- Ключ
- Отчёт
- Режим CI
- Типовые ошибки
- Ограничения
1. Назначение и принципы работы
Типовая задача: из рабочей базы или выгрузки получить набор данных, который можно передать разработчикам и тестировщикам. Структура и форматы значений сохраняются (у СНИЛС и ИНН остаются верные контрольные разряды, у телефона — телефонный вид), а сами значения заменяются.
Принципы:
- Исходные файлы и исходная БД только читаются. Результат пишется во временный файл и публикуется целиком только при успехе всех таблиц.
- Замена детерминирована: один и тот же ключ, домен и исходное значение всегда дают один и тот же результат. Поэтому связи между таблицами (внешние ключи) сохраняются, а повторный прогон даёт тот же результат.
- Обратного преобразования нет. Таблицы соответствия «было — стало» программа не хранит и не создаёт.
- Программа не обращается к сети, кроме подключения к БД PostgreSQL, которую вы указали сами. Телеметрии и проверки обновлений нет.
- Данные не попадают в сообщения об ошибках и в отчёт: только имена таблиц и полей, методы, счётчики.
- Весь интерфейс (справка, сообщения, отчёт) — на русском языке.
Команды:
| Команда | Назначение |
|---|---|
morok scan |
проанализировать начало каждой таблицы и выдать черновик конфигурации |
morok validate |
проверить конфигурацию, не читая данные |
morok run |
выполнить обезличивание (с флагами --dry-run, --strict) |
morok version |
показать версию |
morok help <команда> |
справка по команде |
Справка по любой команде: morok --help, morok help run.
2. Установка
Пакеты .deb (Astra Linux) и .rpm (РЕД ОС) устанавливаются без доступа к сети и не имеют зависимостей. Кратко:
sudo apt install ./morok-*-astra-linux-amd64.deb # Astra Linux
sudo dnf install --disablerepo='*' ./morok-*-red-os-x86_64.rpm # РЕД ОС
morok version
macOS (дополнительная платформа) — один исполняемый файл: morok-<версия>-macos-arm64 (Apple Silicon) или morok-<версия>-macos-amd64 (Intel). Снимите атрибут карантина и скопируйте файл в PATH:
xattr -d com.apple.quarantine morok-<версия>-macos-arm64
sudo install -m 0755 morok-<версия>-macos-arm64 /usr/local/bin/morok
morok version
Подробно: проверка контрольных сумм, обновление, удаление — в руководстве администратора.
3. Быстрый старт
Допустим, в каталоге ./data лежат clients.csv и orders.csv. Последовательность:
Создайте ключ (один раз; подробности в разделе 8):
openssl rand -hex 32 > key.hex chmod 600 key.hexПолучите черновик конфигурации:
morok scan --source-type csv --source ./data --tables clients,orders -o morok.yamlscanчитает первые 1000 записей каждой таблицы (не случайную выборку), распознаёт виды персональных данных (ФИО, телефон, email, СНИЛС, ИНН, паспорт, полис ОМС, банковская карта, дата рождения, адрес) и предлагает для каждого найденного поля правило. Над каждым правилом в комментарии указано, что найдено и какова доля совпадений. Значения данных в черновик не попадают.Проверьте и отредактируйте черновик. Укажите ключ (
key.file: ./key.hex), раскомментируйте нужные правила связей между таблицами (раздел 7), удалите лишнее. Черновик — только предложение: распознавание ошибается в обе стороны.Проверьте конфигурацию:
morok validateПробный прогон: данные читаются и обрабатываются, но не записываются; пишутся отчёты
report.dry-run.mdиreport.dry-run.json.morok run --dry-runРабочий прогон:
morok runОткройте
report/report.mdи убедитесь, что для каждого нужного поля есть правило, а число изменённых значений соответствует ожиданиям.
Если файл конфигурации называется иначе, чем morok.yaml, укажите его флагом -c <путь> (или --config).
4. Конфигурация
Конфигурация — YAML-файл (по умолчанию ./morok.yaml). Неизвестные параметры считаются ошибкой, в сообщении указана строка файла.
Пример:
version: 1
key:
file: ./key.hex
source:
type: csv
path: ./data
csv: { delimiter: ",", header: true }
target:
type: csv
path: ./out
tables:
- name: clients
columns:
- { name: id, method: hmac, params: { unique: true }, domain: client_id }
- { name: fio, method: generate, params: { kind: fio } }
report:
dir: ./report4.1. Корневые параметры
| Параметр | Тип | Умолчание | Описание |
|---|---|---|---|
version |
целое | — (обязателен) | Версия формата конфигурации. Поддерживается 1. |
key |
раздел | — | Источник ключа. Нужен, если есть правила hmac, generate или shuffle. |
source |
раздел | — (обязателен) | Источник данных. |
target |
раздел | — (обязателен) | Приёмник данных. |
tables |
список | — (обязателен, не пуст) | Правила по таблицам. |
domains |
список | пусто | Группы полей с общим пространством замены. |
report |
раздел | — (обязателен) | Параметры отчёта. |
4.2. Раздел key
| Параметр | Тип | Умолчание | Описание |
|---|---|---|---|
key.env |
строка | — | Имя переменной окружения с ключом. |
key.file |
строка | — | Путь к файлу с ключом. |
Укажите ровно один из параметров. Сам ключ в конфигурации не хранится.
4.3. Раздел source
| Параметр | Тип | Умолчание | Описание |
|---|---|---|---|
source.type |
строка | — (обязателен) | csv, xlsx, json, jsonl, pg, pgdump, mysqldump. |
source.path |
строка | — | Каталог с файлами таблиц; для pgdump и mysqldump — файл дампа. Для pg не указывается. |
source.dsn_env |
строка | — | Только для pg: имя переменной окружения с DSN. |
source.csv.delimiter |
строка | , |
Разделитель CSV: один символ, не кавычка и не перевод строки. |
source.csv.header |
логический | true |
Есть ли в файле строка заголовка. |
source.xlsx.sheet |
строка | первый лист | Имя листа. |
source.xlsx.header |
логический | true |
Есть ли в листе строка заголовка. |
source.pg.schema |
строка | public |
Схема таблиц PostgreSQL. |
source.pgdump.schema |
строка | public |
Схема таблиц в дампе. |
source.pgdump.format |
строка | auto |
plain, custom или auto (custom определяется по сигнатуре файла). |
source.pgdump.pg_restore |
строка | поиск в PATH |
Путь к pg_restore (нужен для custom). |
source.mysqldump.database |
строка | любая | База дампа (команды USE), таблицы которой обезличиваются. Обязательна, если таблица из tables есть в нескольких базах дампа. |
Таблица name из tables соответствует файлу <path>/<name>.csv, <name>.xlsx, <name>.json, <name>.jsonl или таблице PostgreSQL <схема>.<name>.
4.4. Раздел target
| Параметр | Тип | Умолчание | Описание |
|---|---|---|---|
target.type |
строка | — (обязателен) | Должен совпадать с source.type. |
target.path |
строка | — | Каталог результата; для pgdump и mysqldump — файл результата. Каталог результата не может совпадать с каталогом источника или быть вложенным в него (и наоборот). |
target.dsn_env |
строка | — | Только для pg: имя переменной окружения с DSN целевой БД. Должна быть другой БД, чем источник. |
target.pg.schema |
строка | public |
Схема таблиц в целевой БД. |
target.pg.truncate |
логический | false |
Очистить целевые таблицы (в той же транзакции) перед записью. |
4.5. Раздел tables
tables:
- name: clients
columns:
- name: fio
method: generate
params: { kind: fio }
domain: fio| Параметр | Тип | Умолчание | Описание |
|---|---|---|---|
tables[].name |
строка | — | Имя таблицы без /, \ и .. Не повторяется. |
tables[].columns[].name |
строка | — | Поле. Для JSON/JSONL — путь a.b[].c. Для pg/pgdump/mysqldump — имя столбца без ., [, ]. Не повторяется в таблице. |
tables[].columns[].method |
строка | — | hmac, generate, shuffle, generalize, drop. |
tables[].columns[].params |
словарь | пусто | Параметры метода (раздел 6). |
tables[].columns[].domain |
строка | <таблица>.<поле> |
Домен поля (раздел 7). |
Поля, для которых правил нет, копируются без изменений. Поэтому важно, чтобы правила покрывали все поля с персональными данными; для проверки этого служит режим --strict (раздел 10).
4.6. Раздел domains
| Параметр | Тип | Описание |
|---|---|---|
domains[].name |
строка | Имя домена. Не повторяется. |
domains[].members |
список строк | Поля вида таблица.поле. Поле может входить только в один домен, и оно должно быть описано в tables. |
4.7. Раздел report
| Параметр | Тип | Умолчание | Описание |
|---|---|---|---|
report.dir |
строка | — (обязателен) | Каталог отчёта. Не должен находиться внутри каталога источника. |
4.8. Хеш конфигурации
morok validate и отчёт выводят хеш конфигурации (sha256:...). Он считается по разобранной конфигурации: комментарии, пробелы и явно указанные значения по умолчанию (например, csv.delimiter: ",") на него не влияют. Хеш позволяет убедиться, что два прогона выполнены по одной конфигурации.
5. Форматы источников
Формат результата всегда совпадает с форматом источника. Исходные файлы не изменяются; существующие файлы результата перезаписываются только с флагом --force.
5.1. CSV
- Кодировка UTF-8 (с BOM или без). Другие кодировки не поддерживаются. На выходе BOM не пишется, перевод строки —
\n, разделитель тот же, что у источника. - Пустые строки в файле пропускаются.
- Повтор имени столбца в заголовке — ошибка (код 3).
- Число полей в каждой строке должно совпадать с заголовком.
- Без заголовка (
header: false) столбцы именуются номерами1,2, … и в правилах указываются номерами.
5.2. XLSX
- Таблица — файл
<имя>.xlsx, обрабатывается один лист (source.xlsx.sheet, по умолчанию первый). Остальные листы копируются без изменений; об этом выводится предупреждение. - Стили и прочие части книги сохраняются. Обезличенные ячейки записываются как строки
inlineStrлибо числа. - Таблица общих строк (
sharedStrings.xml) очищается от обезличенных значений, чтобы исходные значения не остались в файле. - В
docProps/core.xmlиdocProps/app.xmlочищаются автор, последний редактор, организация и руководитель. - Книги, в которых могли сохраниться исходные значения вне листа, отвергаются с кодом 2: со сводными таблицами, внешними связями, диаграммами и комментариями. Удалите такие части или сохраните лист в CSV.
- Формулы в обезличиваемом столбце — ошибка; замените их значениями.
- Метод
dropв режимеcolumnдля XLSX не поддерживается (код 2). - Таблица общих строк больше 256 МБ — ошибка (код 3).
5.3. JSON и JSONL
json— файл<имя>.jsonс массивом объектов;jsonl— файл<имя>.jsonl, один объект на строку. Обработка потоковая.- Путь поля — точечная нотация:
client.name,client.contacts[].phone([]— все элементы массива). - Порядок ключей сохраняется. Повтор ключа в объекте — ошибка.
- Если путь не найден ни в одной записи, выводится предупреждение (не ошибка).
- Метод применяется к листовым значениям: строки и числа. Числа сохраняют исходную запись.
hmacдля числа с ненулевой первой цифрой целой части (42,-7.5) выбирает первую цифру результата из 1–9, поэтому число остаётся числом (то же для числовых ячеек XLSX); значения с ведущим нулём заменяются как раньше. true/falseиnullметодамиhmac,generate,shuffle,generalizeне меняются. Методdropсчитает логические значения данными:mode: null(кавычки не обязательны) заменяет значение наnull,mode: columnудаляет ключ.- Объект или массив на месте листового значения — ошибка (код 3).
- Строка JSONL длиннее 64 МБ — ошибка.
5.4. PostgreSQL (pg)
- DSN берётся только из переменных окружения (
source.dsn_env,target.dsn_env), в конфигурации и в сообщениях его нет. В сообщениях об ошибках подключения указан только класс причины, без адреса и пользователя. - Источник читается в одной транзакции
REPEATABLE READ READ ONLY: исходная БД не изменяется, а все таблицы читаются согласованным снимком. Таблицы читаются потоково, память не зависит от размера таблицы. - Приёмник — другая БД (та же БД в качестве приёмника — ошибка), схема и таблицы в ней должны существовать: DDL не копируется.
- Запись идёт одной транзакцией на все таблицы; при любой ошибке выполняется откат и в целевой БД остаётся прежнее состояние.
- Непустая целевая таблица — ошибка, пока не указан
target.pg.truncate: true. С этим параметром таблицы очищаются в той же транзакции перед записью. - Порядок таблиц и внешние ключи: таблицы записываются в порядке
tables. Внешние ключи сDEFERRABLEпроверяются при фиксации транзакции, и порядок не важен. Ключи безDEFERRABLEпроверяются сразу, поэтому родительские таблицы указывайте вtablesраньше дочерних. Для сохранения самих связей используйте общие домены (раздел 7). - Таблицы вне
tablesне копируются. - При
--dry-runк целевой БД программа не подключается. - Перечень таблиц для
scanзадаётся флагом--tables(обязателен дляpg). - Вычисляемые столбцы (
GENERATED ALWAYS AS (...) STORED) читаются (дляscanи--strict), но в запись не передаются: приёмник вычисляет их сам. Правило обезличивания на вычисляемом столбце — ошибка (код 2); удалите правило. - Столбцы-идентификаторы (
GENERATED ALWAYS | BY DEFAULT AS IDENTITY) записываются со значениями источника (они нужны для внешних ключей); перед фиксацией транзакции последовательность каждого такого столбца переставляется на максимальное значение. Нужны права на изменение последовательности. - Обе сессии (источник и приёмник) выставляют
DateStyle = 'ISO, YMD',IntervalStyle = 'postgres'иTimeZone = 'UTC': даты читаются и пишутся в одном формате независимо от настроек сервера;timestamptzпередаётся как момент времени в UTC (1990-05-17 13:45:10+00). - Тип столбца
date,timestamp,timestamptzучитываютgenerateсkind: birthdateиgeneralizeсkind: date: без явногоlayoutbirthdateвыдаёт2006-01-02(дляtimestamp—2006-01-02 00:00:00, дляtimestamptz— с+00);generalizeвыдаёт дату начала периода в том же формате (to: year→1990-01-01,month→1990-05-01,decade→1980-01-01). Явныйlayout, несовместимый с типом столбца, — ошибка плана (код 2): дляdateдопустим только2006-01-02, дляtimestamp/timestamptzlayoutне задаётся.
5.5. Дамп PostgreSQL (pgdump)
- Источник — один файл дампа (
source.path); результат — один файл plain-дампа (target.path). - Plain-формат (текстовый
pg_dump): блокиCOPYтаблиц изtablesобезличиваются, DDL и остальное копируется без изменений; результат восстанавливается черезpsql. БлокиCOPYтаблиц, которых нет вtables, также копируются без изменений. - Custom-формат читается через локальный
pg_restore(запускается как процесс, дамп преобразуется в plain-поток). Пакетpg_restoreиз репозитория ОС нужно установить отдельно; если его нет, программа завершится с кодом 3 и подсказкой.pg_restoreзапускается без подключения к серверу. - Не поддерживаются для обезличиваемых таблиц: дамп с
INSERT(создайте дамп без--inserts),COPY ... FROM '<файл>', large objects. Программа сообщает об этом и завершается с кодом 2. - Имена полей в правилах — имена столбцов.
5.6. Дамп MySQL и MariaDB (mysqldump)
- Источник — один файл дампа (
source.path), созданныйmysqldumpилиmariadb-dump; результат — один SQL-файл (target.path), который восстанавливается командойmysql < результат.sql. Результат публикуется атомарно (сначала<путь>.tmp, затем переименование). Внешние программы и подключение к серверу не нужны. - Обезличиваются записи команд
INSERT INTO,INSERT IGNORE INTOиREPLACE INTOтаблиц изtables: и с несколькими записями в команде (по умолчанию), и по одной записи (--skip-extended-insert), со списком столбцов (--complete-insert) и без него. Всё остальное (CREATE TABLE,SET,LOCK TABLES, комментарии, триггеры, команды других таблиц) копируется без изменений. Значения, которые правила не меняют, остаются теми же байтами, включая_binary '...',0x...(--hex-blob) и числа. - Если в
INSERTнет списка столбцов, столбцы берутся из предшествующегоCREATE TABLE; вычисляемые (generated) столбцы MySQL в дамп не выгружает, MariaDB выгружает вместе со значениями; вариант определяется автоматически. Методdropсmode: columnубирает столбец:INSERTперезаписывается с явным списком оставшихся столбцов. - Изменённое значение записывается как
NULL, число (если исходное значение было числом и новое выглядит как число) или строка в кавычках с экранированием MySQL; префикс_binaryсохраняется. - Контекст базы задают команды
USEдампа. Если таблица изtablesесть в нескольких базах (дамп--all-databases), укажитеsource.mysqldump.database, иначе программа завершится с кодом 2. - Таблицы дампа, которых нет в
tables, копируются без изменений; программа выводит предупреждение со списком таких таблиц. - Не поддерживаются для обезличиваемых таблиц (код 2): XML-дамп (
--xml), загрузка данных из файлов (--tab,LOAD DATA,INTO OUTFILE), неизвестный список столбцов (нетCREATE TABLEпередINSERTи нет--complete-insert). Код 3: оборванный дамп (нет;в конце командыINSERT), число значений в записи не равно числу столбцов. - Имена полей в правилах — имена столбцов (без
.,[,]).
6. Методы обезличивания
Пустая строка и NULL во всех методах остаются без изменений (кроме drop). Методы hmac, generate и shuffle требуют ключ.
Примеры ниже получены на учебном ключе из 64 символов a. С вашим ключом значения будут другими. Исходные данные в примерах вымышлены.
| Поле | Исходное | Метод | Результат (пример) |
|---|---|---|---|
| id | 1 |
hmac, unique: true |
1 (в примере из трёх строк 1, 2, 3 получилось 1, 3, 8) |
| fio | Тестов Тест Тестович |
generate, kind: fio |
Гусева Наталия Андреевна |
| phone | +7 900 000-12-34 |
generate, kind: phone |
+7 (996) 956-54-93 |
| snils | 112-233-445 95 |
hmac, format: snils |
936-546-873 60 |
| inn | 500100732259 |
hmac, format: inn |
785763473068 |
| birth | 15.03.1985 |
generalize, kind: date, to: year |
1985 |
| addr | г. Москва, ул. Тестовая, д. 1 |
generalize, kind: address, to: region |
город Москва |
| sum | 1234 |
generalize, kind: number, step: 100 |
1200–1299 |
6.1. hmac — детерминированная замена
Каждый символ значения заменяется псевдослучайным символом того же класса: цифра — цифрой, латинская буква — латинской того же регистра, кириллическая — кириллической того же регистра. Прочие символы (пробелы, дефисы, @, точки) сохраняются. Длина значения и разделители сохраняются. Результат вычисляется как HMAC-SHA256 от ключа, домена и значения.
Параметры:
| Параметр | Тип | Умолчание | Описание |
|---|---|---|---|
format |
строка | нет | snils, inn, card, oms: после замены пересчитываются контрольные разряды. |
on_mismatch |
строка | error |
Только с format или unique. Для значения не по формату: error — остановка с ошибкой (код 3), plain — обычная замена без пересчёта контрольных разрядов. |
keep_bin |
логический | true |
Только для format: card: первые 6 цифр (BIN эмитента) сохраняются. |
unique |
логический | false |
Биективная замена цифровых идентификаторов длиной 1–36 цифр: разные значения никогда не дают одинаковый результат. Несовместим с format. |
keep_leading_nonzero |
логический | true |
Только с unique: значение, не начинающееся с нуля, не получит ведущего нуля. |
Особенности:
format: snils— разделители исходной записи сохраняются; контрольное число СНИЛС пересчитывается. Номера не выше 001-001-998 контрольного числа не имеют.format: inn— 10 или 12 цифр определяется по длине; код региона заменяется допустимым; контрольные разряды пересчитываются.format: card— 13–19 цифр, последняя цифра по алгоритму Луна.format: oms— 16 цифр; алгоритм контрольного разряда полиса ОМС в программе не подтверждён, поэтому результат нужно считать только форматно правдоподобным.- Без
uniqueразные исходные значения теоретически могут дать одинаковый результат; для ключей, на которые ссылаются внешние ключи, используйтеunique: true. hmacдля email заменяет и локальную часть, и домен буквами, при этом получаются домены, не зарезервированные для примеров. Для email лучше использоватьgenerateсkind: email(доменыexample.ru,example.com,example.org).
Пример для ключа и связанного внешнего ключа:
- { name: id, method: hmac, params: { unique: true }, domain: client_id }6.2. generate — правдоподобное значение
Значение заменяется сгенерированным из встроенных словарей и алгоритмов. Результат детерминирован: зависит от ключа, домена и исходного значения. Исходное значение в результат не переносится. Параметр kind обязателен.
kind |
Результат | Дополнительные параметры |
|---|---|---|
fio |
Фамилия Имя Отчество, согласованные по полу | format: full (по умолчанию) или short (Иванов И. И.) |
surname, first_name, patronymic |
часть ФИО | — |
email |
адрес на example.ru, example.com или example.org |
— |
phone |
+7 (XXX) XXX-XX-XX |
format: +7 (XXX) XXX-XX-XX (по умолчанию) или plain (7XXXXXXXXXX) |
snils |
СНИЛС с верным контрольным числом | — |
inn10, inn12 |
ИНН юридического и физического лица с верными контрольными разрядами | — |
passport |
серия и номер вида 12 34 567890 |
— |
card |
номер карты с верной контрольной цифрой | prefix: mir (по умолчанию) или visa |
oms |
16 цифр полиса ОМС (контрольный разряд не подтверждён) | — |
birthdate |
дата рождения | layout: 02.01.2006 (по умолчанию), 2006-01-02, 02/01/2006, excel (серийная дата Excel, число); min_age (18), max_age (80) — целые от 0 до 120 |
address |
индекс, г. Город, ул. Улица, д. N, кв. M |
— |
city, region |
город, субъект РФ | — |
Особенности birthdate: возраст отсчитывается от 1 января года запуска, поэтому результаты в разные календарные годы различаются.
Пример:
- { name: fio, method: generate, params: { kind: fio } }
- { name: email, method: generate, params: { kind: email } }
- { name: born, method: generate, params: { kind: birthdate, min_age: 20, max_age: 60 } }6.3. shuffle — перемешивание значений столбца
Значения столбца переставляются между строками. Набор значений столбца сохраняется (мультимножество), меняется только принадлежность строкам. Пустые значения и NULL остаются на месте. Перестановка детерминирована ключом и доменом.
Метод требует два прохода по источнику; если источник изменился между проходами, прогон завершается с кодом 3.
| Параметр | Тип | Умолчание | Описание |
|---|---|---|---|
max_rows |
целое | 1000000 |
Сколько значений держать в памяти. При превышении используется внешняя сортировка во временных файлах (в системном каталоге временных файлов, права 0600, удаляются всегда). |
Для таблиц небольшого размера и столбцов с малым числом различных значений перемешивание мало меняет данные; не применяйте его к идентифицирующим полям отдельно от остальных.
6.4. generalize — огрубление
Значение заменяется менее точным. Ключ не нужен. Результат зависит только от значения.
Общие параметры: kind (обязателен: date, number, address), on_mismatch — error (по умолчанию, ошибка с кодом 3) или empty (заменить пустым значением). Режима «оставить исходное» нет намеренно.
kind: date:
| Параметр | Описание |
|---|---|
to (обязателен) |
year → 1985; month → 03.1985 (стиль входа: 03.1985, 03/1985, 1985-03); decade → 1980–1989 |
layout |
Формат Go с годом 2006, например 02.01.2006. Без него формат определяется автоматически среди 02.01.2006, 2006-01-02, 02/01/2006, RFC 3339. excel — значение это серийное число даты Excel (так даты хранятся в XLSX): результат — серийное число начала периода (to: year → 1 января года), ячейка остаётся датой со своим стилем. Система дат 1900 с учётом ошибки 29.02.1900; для книги с date1904 программа считает от 01.01.1904 сама. Для столбцов pg date/timestamp формат выбирается по типу столбца (раздел 5.4). |
kind: number:
| Параметр | Описание |
|---|---|
step (обязателен) |
Целое больше 0, ширина интервала. |
output |
range (по умолчанию) → 1200–1299; lower → 1200. Дробные числа в режиме range → [1200; 1300). |
kind: address:
| Параметр | Описание |
|---|---|
to (обязателен) |
region → название субъекта РФ; city → г. Город. |
Город и субъект ищутся в строке по встроенным словарям (без учёта регистра и «ё»). Не нашлось — значение не соответствует формату (см. on_mismatch).
6.5. drop — удаление
| Параметр | Значения | Умолчание | Описание |
|---|---|---|---|
mode |
null, empty, column |
empty |
null — заменить на NULL; empty — пустое значение; column — исключить столбец (ключ) из результата. |
В YAML слово null без кавычек — пустое значение; для mode оно принимается как режим null, поэтому mode: null и mode: "null" равнозначны (кавычки не обязательны).
Особенности:
- В CSV и XLSX значения NULL не бывает, поэтому
mode: "null"даёт пустую ячейку. mode: columnдля CSV убирает столбец с заголовком; для JSON/JSONL удаляет ключ; дляpgстолбец не передаётся в целевую таблицу; для XLSX не поддерживается.
7. Домены и согласованность связей
Домен — имя пространства замены. Одно и то же значение в одном домене всегда заменяется одинаково; в разных доменах — по-разному. По умолчанию домен поля — <таблица>.<поле>, поэтому clients.id и orders.client_id без дополнительных указаний заменялись бы по-разному и связь нарушилась бы.
Задайте общий домен одним из способов:
# 1) в правиле поля
- { name: client_id, method: hmac, params: { unique: true }, domain: client_id }
# 2) списком в разделе domains
domains:
- name: client_id
members: [clients.id, orders.client_id]Приоритет: членство в domains > domain правила > домен по умолчанию. Если domain правила противоречит списку domains, это ошибка конфигурации.
Требования к согласованным ключам:
- у всех полей домена один и тот же метод и одни и те же параметры;
- для ключей используйте
hmacсunique: true: биективная замена не создаёт коллизий и сохраняет уникальность первичного ключа; - домен можно разделять между таблицами разных конфигураций, если ключ тот же: так в демо-наборе
events.client.idсогласован сclients.id.
scan подсказывает возможные связи по именам id, *_id и совпадению значений: закомментированным правилом hmac с unique: true. Проверьте и включите его вручную.
8. Ключ
Ключ — hex-строка не короче 64 символов (32 байта). Ключ в конфигурацию не записывается; указывается только где его взять: переменная окружения (key.env) или файл (key.file).
Создание ключа любым из способов:
openssl rand -hex 32 > key.hex
head -c 32 /dev/urandom | xxd -p -c 64 > key.hex
chmod 600 key.hex
Правила:
- Файл ключа должен быть недоступен другим пользователям: права не шире
0600. Иначе программа не запустится (код 4) с подсказкойchmod 600. Файл ключа не должен быть больше 64 КиБ. - Не помещайте ключ в репозиторий кода и в каталог источника.
- Переменная окружения подходит для CI: храните значение в секретах системы CI.
- Ключ не выводится ни в сообщениях, ни в отчёте. В отчёте только идентификатор ключа (
key_id): несекретный отпечаток для проверки, что два прогона выполнены одним ключом. - Смена ключа меняет все результаты: те же исходные данные дадут другие значения, а прежние результаты перестанут совпадать с новыми (в том числе по внешним ключам). Для связанных наборов, которые собираются в разное время, используйте один ключ.
- Обезличивание по ключу необратимо в том смысле, что программа не хранит соответствий; но тот, у кого есть и ключ, и исходные данные, может воспроизвести результат. Храните ключ отдельно от обезличенных наборов.
9. Отчёт
После каждого прогона в report.dir пишутся два файла: report.md (для чтения) и report.json (для машин). Для --dry-run — report.dry-run.md и report.dry-run.json; рабочие отчёты они не затирают. Существующий отчёт перезаписывается без --force. Если отчёт записать не удалось после успешной публикации данных, программа завершается с кодом 3, но данные при этом уже записаны.
В отчёте нет значений данных, ключа, путей и DSN.
9.1. JSON
Схема — docs/report.schema.json (в пакете схема встроена в программу). Версия схемы — schema_version: 2.
| Поле | Описание |
|---|---|
schema_version |
Версия схемы отчёта (сейчас 2). |
tool, tool_version |
Имя и версия программы. |
config_hash |
Хеш конфигурации. |
key_id |
Идентификатор ключа; отсутствует, если ключ не использовался. |
dry_run |
Признак пробного прогона. |
started, finished |
Время начала и окончания (UTC). |
source_type |
Формат источника. |
tables[] |
По таблице: name, rows_read, rows_written, fields[]. |
tables[].fields[] |
По полю: name, method, changed, empty, technique, order140_status, params, domain. |
order140 |
Корневой раздел о сверке способов: document, status, note. |
Поля записи о поле: changed — число изменённых значений; empty — пустых; technique — способ обезличивания в формулировке программы; params — параметры метода без секретов; domain — домен, если поле входит в домен.
9.2. Markdown
Отчёт report.md на русском: заголовок с версией, хешем конфигурации, идентификатором ключа, режимом и временем; по таблице на раздел (поле, метод, способ, параметры, изменено, пустых); раздел «Сведения о способах обезличивания» со списком применённых способов и доменов.
9.3. Статус «не сверено» по приказу № 140
Приказ Роскомнадзора от 19.06.2025 № 140 устанавливает требования и методы обезличивания персональных данных. Формулировки способов приказа в программе не подтверждены юридической проверкой. Поэтому в отчёте для каждого поля стоит order140_status: «не сверено», а в Markdown есть пометка «Соответствие способу по приказу Роскомнадзора от 19.06.2025 № 140 не сверено».
Что это значит для пользователя:
- Поле
technique— описание способа, данное программой; это не формулировка приказа и не заключение о соответствии. - Программа не утверждает, что применённый набор правил удовлетворяет приказу или иному нормативному акту. Такое решение принимает оператор данных, при необходимости с юристом.
- После юридической сверки статус изменится на «сверено» в будущих версиях; отчёты прошлых прогонов остаются прежними.
10. Режим CI
Для автоматических конвейеров:
| Флаг | Действие |
|---|---|
--dry-run |
пробный прогон без записи результата |
--quiet |
не выводить прогресс в stderr (итоговая сводка в stdout остаётся) |
--force |
перезаписать существующие выходные файлы |
--strict |
перед обработкой проверить, что в полях без правил нет персональных данных |
--strict-sample N |
число записей на таблицу для проверки --strict (по умолчанию 1000) |
--strict классифицирует первые N записей каждой таблицы теми же алгоритмами, что и scan. Если поле без правила содержит персональные данные, выводится перечень полей (таблица, поле, вид, доля), программа завершается с кодом 5, и ни результат, ни отчёт не создаются. Таблицы источника, которых нет в конфигурации, — предупреждение, не ошибка. --strict работает и для pg (отдельное соединение, выборка LIMIT), и для pgdump и mysqldump (один проход по дампу с остановкой, когда выборка набрана). Правило drop с mode: column на путь объекта или массива покрывает этот путь и всех потомков (a, a.x, a[].x); другие методы к объекту неприменимы и потомков не покрывают. Пропуски обхода вложенных форматов (глубина больше 10, больше 500 путей, ключи с ., [, ] или пустые, записи не объекты) в --strict — нарушение «путь не проверен: {причина}» с кодом 5 (в scan — предупреждение). --strict проверяет только начало таблицы и только виды, которые умеет распознавать scan; это дополнительный контроль, а не полная проверка.
Коды возврата:
| Код | Значение |
|---|---|
| 0 | успешно |
| 1 | внутренняя ошибка или обработка прервана |
| 2 | неверные аргументы или конфигурация |
| 3 | ошибка чтения или записи данных |
| 4 | ключ отсутствует или некорректен |
| 5 | --strict: найдены персональные данные в полях без правил |
Фрагмент для скрипта CI (shell):
set -eu
export MOROK_KEY="$SECRET_MOROK_KEY" # значение берётся из секретов системы CI
morok validate -c morok.yaml
morok run -c morok.yaml --strict --quiet --force
Сообщения об ошибках выводятся в stderr, итоговая сводка — в stdout.
11. Типовые ошибки
Тексты ниже выводятся программой; в фигурных скобках — подставляемые значения. Все ошибки начинаются со слова «ошибка:».
| Сообщение | Код | Причина и действия |
|---|---|---|
| файл конфигурации «{path}» не найден | 2 | Конфигурация лежит не в текущем каталоге. Укажите -c <путь> или перейдите в каталог с morok.yaml. |
| {path}, строка {line}: неверный синтаксис YAML | 2 | Опечатка, отступы. Исправьте указанную строку. |
| {path}, строка {line}: неизвестный параметр «{name}» | 2 | Опечатка в имени параметра. Сверьтесь с разделом 4. |
| неизвестный метод «{method}»; доступны: {available} | 2 | Метод указан неверно. Допустимы drop, generalize, generate, hmac, shuffle. |
| у метода «{method}» не указан обязательный параметр «{param}» | 2 | Например, kind у generate или step у generalize. Добавьте параметр в params. |
| недопустимое значение параметра «{param}» метода «{method}»; допустимо: {allowed} | 2 | Исправьте значение. |
| неизвестный параметр «{param}» метода «{method}» | 2 | Параметр не относится к методу или виду (например, keep_bin не с format: card). |
| параметры «{a}» и «{b}» метода «{method}» несовместимы | 2 | unique и format у hmac задаются раздельно. |
| поле «{member}» не найдено в tables | 2 | В domains.members указано поле без правила. Добавьте правило или исправьте имя. |
| каталог результата совпадает с исходным или вложен в него | 2 | Задайте для target.path отдельный каталог. |
| формат результата должен совпадать с форматом источника | 2 | target.type должен быть равен source.type. |
| не задан источник ключа: укажите key.env или key.file в конфигурации | 4 | В конфигурации есть правила hmac, generate или shuffle, но нет раздела key. |
| переменная окружения «{name}» не задана | 4 | Экспортируйте переменную или проверьте имя в key.env. |
| файл ключа «{path}» доступен другим пользователям; выполните chmod 600 {path} | 4 | Права файла шире 0600. Выполните chmod 600. |
| ключ должен быть hex-строкой не короче 64 символов | 4 | Создайте ключ командой openssl rand -hex 32. |
| не найден файл «{file}» в каталоге «{dir}» | 3 | Для таблицы из tables нет файла в каталоге источника. Проверьте имя таблицы и source.path. |
| {file}, строка {line}: неверное число полей (ожидалось {want}, получено {got}) | 3 | Строка CSV повреждена или неверен csv.delimiter. |
| {file}, строка {line}: неверная кодировка (ожидается UTF-8) | 3 | Файл в другой кодировке (например, windows-1251). Перекодируйте в UTF-8. |
| в «{file}» нет поля «{field}» из конфигурации | 2 | Имя столбца в правиле не совпадает с заголовком файла. |
| в «{file}» повторяется столбец «{name}» | 3 | Дубликат имени в заголовке CSV. Переименуйте столбец. |
| файл «{path}» уже существует; используйте --force | 3 | Результат уже есть от прошлого прогона. Удалите его или добавьте --force. |
| временный файл «{path}» остался от прошлого запуска; используйте --force | 3 | Прошлый прогон прерван. Запустите с --force. |
| найдены ПДн без правил обезличивания: ... / обработка остановлена: поля с ПДн не покрыты правилами ({n}) | 5 | --strict: добавьте правила для перечисленных полей. |
| путь не проверен: {причина} / обработка остановлена: часть путей не проверена ({n}) | 5 | --strict: обход вложенной таблицы пропустил значения (глубина, число путей, ключ с ./[/], запись не объект). Проверьте их вручную или переименуйте ключи. |
| столбец «{column}» таблицы «{table}» вычисляемый; правило к нему не применяется — удалите его | 2 | Правило на вычисляемом столбце pg (GENERATED … STORED). |
| дамп оборван: нет конца блока данных таблицы «{schema}.{table}» | 3 | В дампе нет \. после данных таблицы: файл обрезан. |
| таблица «{schema}.{table}» в целевой БД не пуста; укажите target.pg.truncate: true | 3 | Целевая таблица содержит данные. Очистите её или включите truncate. |
| не удалось подключиться к исходной БД: {reason} | 3 | Проверьте DSN в переменной окружения, доступность сервера и права. |
| книга «{file}» содержит {parts}, где могут сохраниться исходные значения; удалите их или сохраните лист в CSV | 2 | В книге XLSX есть сводные таблицы, диаграммы, внешние связи или комментарии. |
| таблица «{table}», поле «{field}»: значение поля не соответствует формату «{format}» | 3 | Значение не удалось разобрать (например, дата не того вида). Исправьте данные, укажите layout или on_mismatch: plain / empty. |
| данные таблицы «{table}» изменились во время обработки | 3 | Источник менялся во время прогона. Остановите запись в источник или используйте его копию. |
Кроме того, ошибка «обработка прервана» (код 1) возникает при прерывании прогона пользователем.
12. Ограничения
- Программа не определяет за вас, какие данные персональные.
scanраспознаёт ограниченный набор видов по форме, контрольным суммам и встроенным словарям; свободный текст (комментарии, описания) не анализируется. Поля со свободным текстом удаляйте (drop) или проверяйте вручную. - Правила применяются к полям целиком; разбор вложенного содержимого строк (имена внутри текста) не выполняется.
- Кодировка входных файлов — только UTF-8.
- Выборка
scanи--strict— первые записи таблицы, а не случайные. - Косвенная идентификация (сочетание нетронутых полей, редкие значения, даты событий) остаётся на стороне пользователя. Метод
generalizeснижает точность значений, но не исключает возможности связать набор с конкретным человеком. hmacбезuniqueможет дать одинаковый результат для разных значений.- Контрольные разряды полиса ОМС не подтверждены.
- XLSX: обрабатывается один лист книги; книги с определёнными частями (раздел 5.2) отвергаются.
- Версия, формулировки способов по приказу Роскомнадзора № 140 и их соответствие — не подтверждены (раздел 9.3).
- Платформы: Linux x86_64 (Astra Linux, РЕД ОС; подробности — в руководстве администратора).