morok. Руководство пользователя

morok — утилита командной строки для подготовки обезличенных копий данных для разработки и тестирования. Она читает таблицы (CSV, XLSX, JSON, JSONL, PostgreSQL, дамп PostgreSQL), заменяет значения полей по правилам из конфигурации и записывает результат в отдельное место. Исходные данные не изменяются.

morok — инструмент подготовки тестовых данных. Он не является средством защиты информации: программа не шифрует данные, не ограничивает доступ к ним и не определяет, какие данные считать персональными. Состав правил определяет пользователь. Решение о том, достаточно ли выбранных способов для вашей задачи, принимает пользователь (оператор данных).

Документ описывает поведение версии, в которой есть команды run, validate, scan и version. Установка и эксплуатация описаны в руководстве администратора. Пошаговая проверка за 5–10 минут на демо-данных — в сценарии проверки.

Содержание

  1. Назначение и принципы работы
  2. Установка
  3. Быстрый старт
  4. Конфигурация
  5. Форматы источников
  6. Методы обезличивания
  7. Домены и согласованность связей
  8. Ключ
  9. Отчёт
  10. Режим CI
  11. Типовые ошибки
  12. Ограничения

1. Назначение и принципы работы

Типовая задача: из рабочей базы или выгрузки получить набор данных, который можно передать разработчикам и тестировщикам. Структура и форматы значений сохраняются (у СНИЛС и ИНН остаются верные контрольные разряды, у телефона — телефонный вид), а сами значения заменяются.

Принципы:

Команды:

Команда Назначение
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. Последовательность:

  1. Создайте ключ (один раз; подробности в разделе 8):

    openssl rand -hex 32 > key.hex
    chmod 600 key.hex
  2. Получите черновик конфигурации:

    morok scan --source-type csv --source ./data --tables clients,orders -o morok.yaml

    scan читает первые 1000 записей каждой таблицы (не случайную выборку), распознаёт виды персональных данных (ФИО, телефон, email, СНИЛС, ИНН, паспорт, полис ОМС, банковская карта, дата рождения, адрес) и предлагает для каждого найденного поля правило. Над каждым правилом в комментарии указано, что найдено и какова доля совпадений. Значения данных в черновик не попадают.

  3. Проверьте и отредактируйте черновик. Укажите ключ (key.file: ./key.hex), раскомментируйте нужные правила связей между таблицами (раздел 7), удалите лишнее. Черновик — только предложение: распознавание ошибается в обе стороны.

  4. Проверьте конфигурацию:

    morok validate
  5. Пробный прогон: данные читаются и обрабатываются, но не записываются; пишутся отчёты report.dry-run.md и report.dry-run.json.

    morok run --dry-run
  6. Рабочий прогон:

    morok run
  7. Откройте 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: ./report

4.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

5.2. XLSX

5.3. JSON и JSONL

5.4. PostgreSQL (pg)

5.5. Дамп PostgreSQL (pgdump)

5.6. Дамп MySQL и MariaDB (mysqldump)

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: значение, не начинающееся с нуля, не получит ведущего нуля.

Особенности:

Пример для ключа и связанного внешнего ключа:

- { 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" равнозначны (кавычки не обязательны).

Особенности:

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, это ошибка конфигурации.

Требования к согласованным ключам:

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

Правила:

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 не сверено».

Что это значит для пользователя:

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. Ограничения