fix(tradein/admin): развести исходы проверки кук — бан не равен «плохим кукам» #2829

Merged
lekss361 merged 1 commit from fix/tradein-cookerr into main 2026-08-11 06:08:17 +00:00
Owner

Проблема

cian_session.verify_session возвращает четыре разных исхода — это сделано намеренно, с sentinel-объектами и подробными докстрингами:

  • VERIFY_BAN_SENTINEL — HTTP 403, бан по IP/TLS, куки могут быть валидны
  • VERIFY_SOURCE_UNAVAILABLE_SENTINEL — 5xx/сеть, источник недоступен
  • VERIFY_MARKUP_CHANGED_SENTINEL — 200, но auth-state не распарсился
  • None — 401 / isAuthenticated=false, куки реально протухли

А эндпоинты в admin.py проверяли только if state is None и сразу лезли в state.get("user", {}).get("userId"). Все три sentinel'а проваливались в одну ветку и отдавали 400 "Authenticated state missing userId".

На проде 2026-08-10 это стоило часа диагностики на живом инциденте: реальный бан по IP показывался как «плохие куки», и заведомо валидные куки перезаливались несколько раз. Настоящей причиной был забаненный egress.

Summary

Исход Код Что говорит текст
бан 403 503 Заблокирован наш IP; чинить egress — SCRAPER_PROXY_URL и баны в scrape_proxy_source_bans; перезаливать куки бесполезно
источник недоступен 503 Временно недоступен, повторить позже, куки не трогать
смена вёрстки 500 Нужен фикс парсера (extract_state/_MFE_AUTH), перезалив не поможет
None 401 Куки протухли — перезалить
успех без userId 400 State распарсился, но идентификатор не найден — куки ни при чём
  • Sentinel'ы сравниваются через is, как прямо оговорено в их докстрингах.
  • Единая точка маппинга _cian_verify_state_error(), используется в upload_cian_cookies, cian_auto_login и test_cian_auth.
  • Текст бана называет живую переменную SCRAPER_PROXY_URL и явно помечает CIAN_PROXY_URL как мёртвую (снята в #2616) — именно эта мёртвая переменная увела диагностику в сторону. На это есть отдельный assert в тесте.
  • Тексты по-русски: их читает человек в момент инцидента, поэтому каждый говорит, что делать.

Не вошло

upload_domclick_cookies не вызывает verify_session вообще — это задокументированный предел MVP («DomClick-верификация требует реального browser-фетча»), а не тот же баг. Фабриковать несуществующий verify-путь не стали.

Test plan

  • Полный прогон: 4210 passed, 18 skipped (+13 новых, база 4197)
  • ruff check / ruff format — чисто, pre-commit зелёный
  • Тесты на все пять исходов для upload, бан + успех для auto-login, четыре reason + успех для test-auth
## Проблема `cian_session.verify_session` возвращает **четыре** разных исхода — это сделано намеренно, с sentinel-объектами и подробными докстрингами: - `VERIFY_BAN_SENTINEL` — HTTP 403, бан по IP/TLS, **куки могут быть валидны** - `VERIFY_SOURCE_UNAVAILABLE_SENTINEL` — 5xx/сеть, источник недоступен - `VERIFY_MARKUP_CHANGED_SENTINEL` — 200, но auth-state не распарсился - `None` — 401 / `isAuthenticated=false`, куки реально протухли А эндпоинты в `admin.py` проверяли только `if state is None` и сразу лезли в `state.get("user", {}).get("userId")`. Все три sentinel'а проваливались в одну ветку и отдавали `400 "Authenticated state missing userId"`. На проде 2026-08-10 это стоило часа диагностики на живом инциденте: реальный бан по IP показывался как «плохие куки», и заведомо валидные куки перезаливались несколько раз. Настоящей причиной был забаненный egress. ## Summary | Исход | Код | Что говорит текст | |---|---|---| | бан 403 | **503** | Заблокирован наш IP; чинить egress — `SCRAPER_PROXY_URL` и баны в `scrape_proxy_source_bans`; перезаливать куки бесполезно | | источник недоступен | **503** | Временно недоступен, повторить позже, куки не трогать | | смена вёрстки | **500** | Нужен фикс парсера (`extract_state`/`_MFE_AUTH`), перезалив не поможет | | `None` | **401** | Куки протухли — перезалить | | успех без `userId` | **400** | State распарсился, но идентификатор не найден — куки ни при чём | - Sentinel'ы сравниваются через `is`, как прямо оговорено в их докстрингах. - Единая точка маппинга `_cian_verify_state_error()`, используется в `upload_cian_cookies`, `cian_auto_login` и `test_cian_auth`. - Текст бана называет **живую** переменную `SCRAPER_PROXY_URL` и явно помечает `CIAN_PROXY_URL` как мёртвую (снята в #2616) — именно эта мёртвая переменная увела диагностику в сторону. На это есть отдельный assert в тесте. - Тексты по-русски: их читает человек в момент инцидента, поэтому каждый говорит, что делать. ## Не вошло `upload_domclick_cookies` **не вызывает** `verify_session` вообще — это задокументированный предел MVP («DomClick-верификация требует реального browser-фетча»), а не тот же баг. Фабриковать несуществующий verify-путь не стали. ## Test plan - [x] Полный прогон: **4210 passed, 18 skipped** (+13 новых, база 4197) - [x] `ruff check` / `ruff format` — чисто, pre-commit зелёный - [x] Тесты на все пять исходов для upload, бан + успех для auto-login, четыре reason + успех для `test-auth`
lekss361 added 1 commit 2026-08-11 05:10:18 +00:00
fix(tradein/admin): развести исходы проверки кук — бан не равен «плохим кукам»
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m12s
ac97c87f25
verify_session возвращает четыре разных исхода (бан 403 / источник
недоступен / смена вёрстки / реально протухшие куки) — это сделано
намеренно, с sentinel-объектами и подробными докстрингами. Но эндпоинты
загрузки проверяли только `if state is None`, и три sentinel'а
проваливались в одну ветку с текстом «Authenticated state missing userId».

На проде 2026-08-10 это стоило часа: реальный бан по IP показывался как
«плохие куки», и заведомо валидные куки перезаливались несколько раз.

- бан 403 → 503, текст указывает чинить egress (SCRAPER_PROXY_URL и баны
  в scrape_proxy_source_bans), а не перезаливать куки
- источник недоступен → 503, повторить позже, куки не трогать
- смена вёрстки → 500, нужен фикс парсера, перезалив не поможет
- 401/isAuthenticated=false → 401, куки перезалить
- успех без userId → 400 с точным описанием случая

Sentinel'ы сравниваются через `is`, как оговорено в их докстрингах.
Тексты по-русски: их читает человек в момент инцидента.
lekss361 merged commit 29db137375 into main 2026-08-11 06:08:17 +00:00
lekss361 deleted branch fix/tradein-cookerr 2026-08-11 06:08:17 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2829
No description provided.