# ПДн 152-ФЗ — чек-лист аудита Лидерры

Основан на реальных артефактах проекта (db/schema.sql v8.26, 21.05.2026).

## Таблицы-носители ПДн (инвентарь)

| Таблица | ПДн-колонки | Тип субъекта |
|---|---|---|
| `deals` | `phone`, `phones` (JSONB), `contact_name`, `city` | лид (физлицо) |
| `supplier_leads` | `phone`, `raw_payload` (JSONB — весь payload поставщика) | лид (физлицо) |
| `users` | `email`, `first_name`, `last_name`, `phone`, `totp_secret` | пользователь-клиент |
| `tenants` | `contact_email`, `organization_name` | организация-клиент |
| `auth_log` | `email` (при login_failed для неизвестного пользователя) | пользователь |
| `pd_subject_requests` | `subject_email`, `subject_phone`, `subject_full_name` | субъект ПДн |
| `impersonation_tokens` | косвенно (связь user — admin) | пользователь |
| `import_log` | `filename`, `file_path` (может содержать имя файла с ПДн) | лид (косвенно) |

---

## Раздел 1 — Технический аудит ПДн

### Т1. RLS на таблицах-носителях ПДн

- [ ] `deals` — `ENABLE ROW LEVEL SECURITY` ✅ (подтверждено schema.sql:2780).
  Проверить: `FORCE ROW LEVEL SECURITY` не выставлен (только у `lead_charges`
  — там сильнее). Убедиться, что `crm_app_user` не BYPASSRLS.
- [ ] `users` — RLS включён (schema.sql:2778). Политика `tenant_isolation` по
  `tenant_id`. Проверить: нет прямого SELECT * без `SET LOCAL app.current_tenant_id`.
- [ ] `supplier_leads` — **RLS не включён** (таблица SaaS-уровня, schema.sql:1948).
  Это осознанное решение. Проверить: доступ только из воркера
  (`crm_supplier_worker` BYPASSRLS) с явным `WHERE tenant_id`.
- [ ] `pd_subject_requests` — **RLS не включён** намеренно (saas-уровневая,
  schema.sql:2483). Доступ только через `crm_admin_user` BYPASSRLS.
  Проверить: tenant-приложение к таблице не обращается.
- [ ] `auth_log` — RLS включён (schema.sql:2810). Политика `tenant_isolation`.
  Проверить: поле `email` в строке `login_failed` — не утекает ли email
  несуществующего пользователя в посторонний тенант.
- [ ] `import_log` — RLS включён (schema.sql:2790).

### Т2. Маскирование ПДн в дампах (pg_anonymizer #29)

- [ ] **Проверить вручную:** OPEN-И-24 (schema.sql:113) — «pg_anonymizer процедура,
  документация в Прил. И, без изменений схемы». Расширение ставится в фазе 3
  (db/CHANGELOG_schema.md:625). На момент аудита — **расширение может быть не
  установлено**. Выполнить: `psql -c "SELECT extname FROM pg_extension WHERE extname='anon';"`.
- [ ] Если pg_anonymizer установлен: проверить наличие `SECURITY LABEL` /
  `anon.mask_column` на колонках `deals.phone`, `deals.contact_name`,
  `users.email`, `users.first_name`, `users.last_name`.
- [ ] Если pg_anonymizer **не установлен**: дампы (`pg_dump`) содержат ПДн в открытом
  виде — критический риск перед продакшеном. Требуется: либо установить
  расширение и настроить маски, либо запретить дампы с ПДн вне зашифрованного
  хранилища.

### Т3. Утечки ПДн в логи и Sentry

- [ ] **Sentry PII-scrubbing** (OPEN-И-16, schema.sql:68): конфигурация в
  `app/config/sentry.php` (narrative §22 «Sentry PII-scrubbing»).
  Проверить: whitelist событий задан; regex-маска `phone`/`email`/`password`/
  `secret`/`token`/`api_key` включена. Тест: намеренно вызвать ошибку с
  телефоном в payload и проверить Sentry-событие.
- [ ] **Laravel-логи (`storage/logs/`)**: нет ли `Log::info`/`Log::debug` с
  `$deal->phone`, `$lead->phone`, `request()->all()` в необработанном виде.
  Grep: `Log::` + `phone\|email\|contact_name` в `app/app/`.
- [ ] **`activity_log.context`** (JSONB, schema.sql:1775): поле `context` журнала
  действий по сделкам. Проверить: не пишется ли туда `phone`/`contact_name`
  полностью (должны быть только ID и маскированные значения).
- [ ] **`supplier_leads.raw_payload`** (JSONB, schema.sql:1966): хранит весь
  webhook-payload от поставщика, включая телефон. Это осознанное хранение
  (нужно для дебага/реконсайла). Проверить: доступ ограничен только
  `crm_supplier_worker` + `crm_admin_user`; не отдаётся в tenant API.
- [ ] **`auth_log.email`** (schema.sql:1458): email попадает в лог при `login_failed`
  для неизвестного адреса. Проверить: колонка не индексируется publicly,
  доступна только под RLS tenant-политикой.

### Т4. Шифрование чувствительных полей в покое

- [ ] **`users.totp_secret`** (schema.sql:723): комментарий «ШИФРУЕТСЯ `Crypt::encrypt`».
  Проверить: в коде Laravel используется `Crypt::encrypt`/`decrypt`, не plain TEXT.
  Grep: `totp_secret` в моделях/сервисах — нет ли прямого assignment без encrypt.
- [ ] **`tenants.webhook_token`** (schema.sql:628): хранится в открытом виде как
  уникальный токен. Допустимо (по дизайну — это API-ключ, не пароль), но
  проверить: не логируется ли при ротации (`webhook_token_rotated_at`).
- [ ] **Encryption at rest (диск/облако)**: Yandex Cloud `ru-central1` — проверить,
  включено ли шифрование диска/объектного хранилища на уровне YC-консоли.
  Это вне кода, но обязательно для 152-ФЗ.

### Т5. CSV-экспорт лидов и signed URL

- [ ] **`report_jobs`** (schema.sql:2313): `file_path` = `s3://bucket/path/file.xlsx`.
  Триггер `trg_report_jobs_export_log` (schema.sql:3096) автоматически пишет
  запись в `pd_processing_log` при INSERT. Проверить: триггер активен в prod.
  SQL: `SELECT tgname, tgenabled FROM pg_trigger WHERE tgname = 'trg_report_jobs_export_log';`
- [ ] **Signed URL TTL**: schema.sql:3182 — «доступ через signed URL TTL 1 ч».
  Проверить в коде: `Storage::temporaryUrl(...)` с `now()->addHour()`.
  Файлы экспорта не доступны без аутентификации.
- [ ] **`report_jobs.expires_at`**: автоудаление файла. Проверить: есть ли
  scheduled command / cleanup job, удаляющий S3-файл и обнуляющий `file_path`
  после `expires_at`.

### Т6. CSV-импорт исторических лидов

- [ ] **`import_log.file_path`** (schema.sql:1544): путь к загруженному CSV-файлу с
  ПДн. Проверить: файл хранится во временном/приватном location, не в
  публично доступном URL; удаляется после обработки.
- [ ] **Проверить вручную:** содержит ли исторический CSV телефоны лидов в открытом
  виде в `storage/`? Если да — нужен cleanup после импорта.

---

## Раздел 2 — Соответствие 152-ФЗ

### З1. Хранение ПДн на территории РФ (ст.18.1 152-ФЗ)

- [ ] Облако: Yandex Cloud, регион `ru-central1` (Москва) — **✅ РФ**.
  Подтверждено в CLAUDE.md §2.
- [ ] S3-хранилище файлов экспорта (`report_jobs.file_path`): убедиться, что
  Yandex Object Storage используется (не AWS S3 / GCS). Проверить
  `app/config/filesystems.php`.
- [ ] Self-hosted Sentry: Yandex Cloud `ru-central1` — ✅ РФ (CLAUDE.md §2).
  Проверить: Sentry не проксирует события в eu.sentry.io / sentry.io (US).
- [ ] Unisender Go (email): **Проверить вручную** — уточнить у Unisender
  расположение серверов; письма с ПДн (email адреса) передаются провайдеру.

### З2. Согласия субъектов ПДн (ст.6, ст.9 152-ФЗ)

- [ ] **`tenant_consents`** (schema.sql:2430): таблица согласий. Проверить:
  при регистрации тенанта записывается `consent_type='pd_processing'` с
  `document_version`, `ip_address`, `user_agent`, `given_at`.
- [ ] Проверить: согласие на обработку ПДн лидов (телефоны физлиц) — не пользователя-
  клиента, а лидов. Лиды приходят от поставщика (crm.bp-gr.ru) — проверить
  договор с поставщиком (правовое основание обработки ст.6 ч.1 п.5 или п.4).
  **Проверить вручную** — вне schema (юридический документ).
- [ ] `consent_type` значения: `pd_processing`, `marketing`, `oferta_v1` — убедиться,
  что consent_type='pd_processing' обязателен при регистрации (нет bypass).

### З3. Сроки хранения и удаление (ст.21 152-ФЗ)

- [ ] **Soft-delete в `deals`** (schema.sql:1648 `deleted_at`): после soft-delete
  данные остаются. Проверить: есть ли политика retention (hard-delete или
  анонимизация `phone`/`contact_name` через N дней после `deleted_at`).
  **Проверить вручную:** scheduled command для hard-delete сделок.
- [ ] **`users.deleted_at`** (schema.sql:751): комментарий «soft delete + анонимизация».
  Проверить в коде: при soft-delete пользователя анонимизируются ли
  `email`/`first_name`/`last_name`/`phone`? Grep: `UserObserver` / `UserService`
  метод delete/anonymize.
- [ ] **Право на удаление** (ст.21): обращение типа `request_type='deletion'` в
  `pd_subject_requests`. Проверить: есть ли процедура исполнения (скрипт/ручной
  процесс) удаления ПДн конкретного субъекта по `subject_phone`/`subject_email`
  из `deals`, `supplier_leads`, `activity_log`.

### З4. Журнал обработки ПДн (ст.18.1 152-ФЗ)

- [ ] **`pd_processing_log`** (schema.sql:2449): таблица журнала. RLS включён
  (schema.sql:2806), политика `tenant_isolation` (schema.sql:2846).
  Проверить: `subject_type`, `action`, `purpose` заполняются при
  ключевых операциях (просмотр сделки, экспорт, удаление).
- [ ] **Триггер экспорта** `trg_report_jobs_export_log` (schema.sql:3096): AFTER
  INSERT на `report_jobs` → INSERT `pd_processing_log` с `action='exported'`.
  Закрывает требование ст.18 (учёт трансграничной передачи / выгрузки).
- [ ] **Append-only hash chain** (schema.sql:63): `log_hash BYTEA` + триггеры
  `BEFORE UPDATE/DELETE` с `RAISE EXCEPTION`. Проверить: цепочка целостна.
  SQL: `SELECT id, log_hash IS NULL AS broken FROM pd_processing_log ORDER BY id DESC LIMIT 10;`

### З5. Обращения субъектов ПДн (ст.14 152-ФЗ)

- [ ] **`pd_subject_requests`** (schema.sql:2491): таблица обращений. Поля:
  `subject_email`, `subject_phone`, `subject_full_name`, `request_type`
  (`access`/`rectification`/`deletion`/`objection`), `deadline_at` (30 дней),
  `processing_restricted`.
- [ ] **Триггер дедлайна** `trg_pd_subject_requests_deadline` (schema.sql:3165):
  функция `set_pd_subject_request_deadline()` заполняет `deadline_at =
  received_at + INTERVAL '30 days'` при INSERT/UPDATE.
  Проверить: `SELECT COUNT(*) FROM pd_subject_requests WHERE deadline_at IS NULL;`
  — должно быть 0.
- [ ] **`processing_restricted`** (schema.sql:2514, ст.21 ч.5): при `TRUE`
  `ProcessingRestrictedException` блокирует операции с ПДн субъекта.
  Проверить в коде: `ProcessingRestrictionGuard` вызывается в сервисах
  перед mutable-операциями с `deals`/`users`.
- [ ] Индекс (schema.sql:2519): `idx_pd_requests_restricted` — эффективный поиск
  активных ограничений. Проверить: он используется в `ProcessingRestrictionGuard`.

### З6. Уведомление РКН и реестр обработки (ст.22 152-ФЗ)

- [ ] **Проверить вручную:** подана ли заявка оператора в реестр Роскомнадзора
  на сайте pd.rkn.gov.ru? Это организационная мера, вне кода.
- [ ] **Проверить вручную:** составлен ли внутренний реестр обработки ПДн
  (перечень категорий субъектов, целей, сроков, мер защиты)?
  Требование ст.22.1 ФЗ-152.
- [ ] **`incidents_log`** (schema.sql:2535): при утечке ПДн — поле
  `related_pd_subject_request_ids BIGINT[]`. Проверить: есть ли внутренняя
  процедура уведомления РКН в течение 24 ч (ст.21.1, с 01.03.2023)?

### З7. Передача ПДн третьим лицам

- [ ] **Поставщик crm.bp-gr.ru**: получает запросы с телефонами лидов обратно
  при синхронизации статусов (`supplier_sync_log`). Проверить наличие договора
  на обработку ПДн по поручению (ст.6 ч.3 152-ФЗ).
  **Проверить вручную** — юридический документ.
- [ ] **Unisender Go** (email-рассылки с именами пользователей):
  **Проверить вручную** — договор поручения на обработку ПДн.
- [ ] **JivoSite** (helpdesk): передаются ли туда email/ФИО клиентов?
  **Проверить вручную**.
