Commit graph

5 commits

Author SHA1 Message Date
bot-backend
2cfd112c53 fix(tradein/security): audit findings — proxy-key leak, admin audit, login status, lead IDOR
Четыре независимых security-audit находки:

1. rotate_proxy_ip (admin.py) отдавал str(httpx exc) клиенту и в Sentry —
   mobileproxy changeip-URL несёт API-ключ в query-string. Ответ теперь
   нейтральный ("changeip request failed"); sentry_scrub.py получил
   composable full-text редактор секрет-подобных query-параметров
   (?token=/?proxy_key=/?api_key=/... — не завязан на конкретного провайдера),
   встроенный в scrub_pii_event (in-place, сохраняет event identity) — main.py
   не тронут, редактор подключается автоматически через существующую ссылку.

2. Меняющие состояние /api/v1/admin/* ручки (куки, авто-логin, прокси,
   настройки скрапера, bulk) были полностью исключены из user_events —
   установить, кто их вызвал, было нельзя. RequestAuditMiddleware теперь
   пишет `admin_action` для POST/PUT/PATCH/DELETE на /admin/* с атрибуцией
   (username/ip/path/method/status), БЕЗ тела запроса (там секреты). GET
   дашборды по-прежнему не логируются (design как раньше — не шумят).

3. login-событие писалось безусловно, без учёта response.status_code —
   отражённая RBAC-попытка (протухший внутренний секрет / неизвестная роль /
   scope-блок) была неотличима от настоящего входа. Теперь event_type
   расходится на login/login_failed по фактическому статусу ответа; payload
   несёт status_code. Дедуп-бакет (once/user+ip+ua+day) оставлен как есть —
   разбивка на success/fail потребовала бы правки user_events.py (вне
   scope); задокументировано как известный trade-off.
   Побочный вопрос (шум basic_auth 401 от сканеров) не требует доп. фильтра
   здесь: Caddy гейтит basic_auth ДО проксирования — трафик без валидного
   X-Authenticated-User в этот код вообще не попадает.

4. POST /lead принимал estimate_id без проверки владельца — можно было
   привязать заявку к чужой оценке. Переиспользован owner-or-admin guard
   _assert_estimate_access из trade_in.py (тот же подход, что #690).

Тесты на каждый пункт (secret не в ответе/событии, admin_action с
атрибуцией и без body, login vs login_failed, чужой estimate_id -> 404) +
regression на существующие сьюты. Полный `pytest -q --deselect
tests/test_search_api.py::test_search_cache_hit`: 2635 passed, 8 skipped.
2026-07-26 23:54:12 +03:00
bot-backend
c65a73ca9a fix(tradein/security): admin.py — generic detail + error-id вместо сырого текста исключений (#2234) 2026-07-03 09:16:34 +03:00
b6634ea40c feat(scrapers): live pacing-регулятор (GET/PUT /scraper/pacing) + data-quality coverage endpoint
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
- browser/server.py: GET /pacing и PUT /pacing — read/write _MIN_PAGE_INTERVAL_BY_PROVIDER
  in-memory; валидация source in PROVIDERS, interval_s >= 0; логирует изменения; сбрасывается
  к env-дефолту на рестарте by design
- backend/admin.py: GET /scraper/pacing — прокси к browser /pacing, PacingResponse pydantic;
  PUT /scraper/pacing/{source} — прокси PUT с валидацией ge=0 le=120; 502/503 при недоступности
- backend/admin.py: GET /scraper/data-quality — single-pass FILTER-агрегаты по listings WHERE
  is_active GROUP BY source (description/photo_urls/address/lat/lon/kitchen_area_m2/living_area_m2/
  ceiling_height/ceiling_height_m/metro_stations); houses (total/avito_validated_at%/rating_score%/
  house_type%); house_reviews count
- browser/test_server_pacing.py: 10 новых тестов GET+PUT /pacing (16 total, все зелёные)
- backend/tests/test_scraper_admin_apis.py: тесты pacing-прокси + data-quality shape/pct-range
  (21 total, все зелёные)
2026-06-20 12:47:03 +03:00
8361961a44 fix(scrapers/backend): socks5 health-probe (httpx[socks]) + status Literal whitelist + #1820 ext_id=0 dedup-гард
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
- pyproject.toml: httpx[socks]>=0.27.0 — тянет socksio, socks5:// прокси в _probe_current_ip работает прозрачно
- admin.py list_scrape_runs_unified: status Literal["done","running","banned","zombie","failed","cancelled"] вместо str|None — невалидные значения → 422
- avito_houses.py save_house_catalog_enrichment: гард ext_id=0 + нет адреса → skip without DB touch; два id-less novostroyka URL не схлопываются в bogus avito:0 в house_sources
- тесты: test_unified_runs_invalid_status_422 + test_unified_runs_valid_statuses_200; test_save_house_catalog_enrichment_skips_zero_ext_id_no_address + *_with_address_persists
2026-06-20 10:17:37 +03:00
2f88a71915 feat(admin): unified scrape/runs + scraper/health + rotate-ip API для единой scrapers-страницы
All checks were successful
CI / changes (pull_request) Successful in 5s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
2026-06-20 09:25:57 +03:00