---
name: zbr-public-report-check
description: "ЗБР, проверка отчёта по номеру — как устроена, что решил владелец и три грабли (X-Frame-Options на своём же фрейме, соль отпечатка, счётчик вместо лимита)"
metadata: 
  node_type: memory
  type: project
  originSessionId: d0c6c5aa-4d05-42e2-a1f9-034482b88603
  modified: 2026-08-03T06:43:50.038Z
---

**Проверка экспертного отчёта по номеру (ЗБР, доделано 03.08.2026, D-75).** Продавец кладёт номер/ссылку в объявление, покупатель открывает без регистрации и видит тот же документ.

**Решения владельца, не выводимые из кода:**
- публикация **только по кнопке**; ссылка приходит вместе с отчётом, но до нажатия помечена нерабочей;
- управлять доступом можно и позже — раздел «Доступ к моим отчётам» в кабинете бота и карточка на странице отчёта на сайте;
- **лимита на просмотры нет — вместо него счётчик**, видимый владельцу и в админке (`/admin/published`). Аномалия ловится глазами, живого продавца ничто не режет.

**Грабли, каждая стоила прогона:**
1. **Свой же iframe режется `X-Frame-Options: DENY`.** Публичный HTML-просмотр пришлось внести в то же исключение, что и `/ai-html`. Симптом всегда один: 200 в логе, белый экран у человека.
2. **Отпечаток ФИО+ИНН солится `ZW_INTERNAL_TOKEN`.** Стенд, созданный скриптом без `source .env`, считает хеш с фолбэк-солью — «пара не совпала» при полностью исправном коде. Любой тест на отпечатках гоняй с окружением бота.
3. **Лимит попыток жил в памяти бота** — перезапуск обнулял, то есть «5 в час» не гарантировали ничего. Теперь `seller_verify_attempts` в БД + второй ключ по отпечатку посетителя. При сбое БД считаем лимит исчерпанным: пропускать молча — дыра.

**Что я оценил неверно:** «каждый просмотр пересобирает PDF» — неправда, редакция кэшировалась на диск с самого начала. Добавлен только сброс кэша при перерендере payload. Полный вариант не кэшируется намеренно — в нём личные данные продавца.

**Не считать дырой:** раздача ссылки сотне людей = раздача PDF; проверка гипотезы «этот ли человек продавец» неустранима (это назначение функции и требует заранее знать пару); подбор кода нереален (32¹⁰).

**Как проверять:** редакцию PDF — только по **текстовому слою** (`/opt/homebrew/bin/pdftotext`, в PATH его нет), поиск по байтам ничего не доказывает. Публичные ветки — из браузера без сессии. Связано: [[security_header_broke_own_iframe]], [[inmemory_state_plus_gate_deadlock]], [[env_source_defeats_test_setdefault]].
