[sf] EKBGeoportalClient hardening при C8-wiring: CQL float-формат + User-Agent + httpx.Client pooling #1084

Closed
opened 2026-06-06 18:37:34 +00:00 by bot-reviewer · 2 comments
Collaborator

Follow-up из PR #1083 (EKB geoportal WFS client, #1067 C7). Три advisory-минора из ревью — применить когда клиент подключается к прод-вызовам (C8/D9), не срочно.

Файл: backend/app/services/scrapers/ekb_geoportal_client.py

1. CQL float-формат (robustness)

features_at_point/features_in_bbox интерполируют raw float в CQL:

cql = f"INTERSECTS({GEOM_COLUMN}, POINT({lon} {lat}))"

На патологических координатах Python repr даёт scientific notation (1e-05) → GeoServer CQL-диалект может отвергнуть. Для ЕКБ (~60.x/56.x) неактуально, но перед реальными вызовами стоит форматировать явно: f"POINT({lon:.8f} {lat:.8f})" (и аналогично BBOX). Injection-риска нет (float), это про корректность формата.

2. User-Agent header

_http_get_json использует bare httpx.get без UA. Sibling ekburg_permits.py шлёт GenDesign/1.0 (+https://gendsgn.ru). Консистентность для муниципальных источников:

headers={"User-Agent": "GenDesign/1.0 (+https://gendsgn.ru) EKB Geoportal WFS"}

3. httpx.Client connection pooling

Сейчас каждый вызов = новый TCP-connection (bare httpx.get). ekburg_permits.py использует httpx.Client context manager (pooling, persistent headers). Под bbox-grid scan по каталогу медленнее. Не correctness — perf. Рассмотреть при добавлении grid-walk в C8.

Acceptance

  • CQL координаты форматируются без scientific notation.
  • UA header присутствует.
  • (опц.) httpx.Client pooling если добавляется bulk-проход.
  • pytest tests/test_ekb_geoportal_client.py green (обновить assert CQL_FILTER под новый формат).
Follow-up из PR #1083 (EKB geoportal WFS client, #1067 C7). Три advisory-минора из ревью — применить когда клиент подключается к прод-вызовам (C8/D9), не срочно. Файл: `backend/app/services/scrapers/ekb_geoportal_client.py` ## 1. CQL float-формат (robustness) `features_at_point`/`features_in_bbox` интерполируют raw float в CQL: ```python cql = f"INTERSECTS({GEOM_COLUMN}, POINT({lon} {lat}))" ``` На патологических координатах Python repr даёт scientific notation (`1e-05`) → GeoServer CQL-диалект может отвергнуть. Для ЕКБ (~60.x/56.x) неактуально, но перед реальными вызовами стоит форматировать явно: `f"POINT({lon:.8f} {lat:.8f})"` (и аналогично BBOX). Injection-риска нет (float), это про корректность формата. ## 2. User-Agent header `_http_get_json` использует bare `httpx.get` без UA. Sibling `ekburg_permits.py` шлёт `GenDesign/1.0 (+https://gendsgn.ru)`. Консистентность для муниципальных источников: ```python headers={"User-Agent": "GenDesign/1.0 (+https://gendsgn.ru) EKB Geoportal WFS"} ``` ## 3. httpx.Client connection pooling Сейчас каждый вызов = новый TCP-connection (bare `httpx.get`). `ekburg_permits.py` использует `httpx.Client` context manager (pooling, persistent headers). Под bbox-grid scan по каталогу медленнее. Не correctness — perf. Рассмотреть при добавлении grid-walk в C8. ## Acceptance - CQL координаты форматируются без scientific notation. - UA header присутствует. - (опц.) httpx.Client pooling если добавляется bulk-проход. - `pytest tests/test_ekb_geoportal_client.py` green (обновить assert CQL_FILTER под новый формат).
bot-reviewer added the
priority/p3
scope/backend
site-finder
labels 2026-06-06 18:37:43 +00:00
Author
Collaborator

+1 hardening-пункт из ревью PR #1094 (C8 urbanCard, тот же файл ekb_geoportal_client.py):

4. int(key) в _search_keys без try/except. keys[str(alias)] = int(key) — на нечисловом key reverse-eng API (UUID-style) бросит uncaught ValueError в zone_regulation_at. Сейчас нет прод-вызова → low. При C8b-wiring обернуть:

try: keys[str(alias)] = int(key)
except (ValueError, TypeError): logger.warning("searchByGeom: non-int key for %s: %r", alias, key)

Объединить с пунктами 1-3 выше в один проход hardening перед первым прод-вызовом клиента.

+1 hardening-пункт из ревью PR #1094 (C8 urbanCard, тот же файл `ekb_geoportal_client.py`): **4. `int(key)` в `_search_keys` без try/except.** `keys[str(alias)] = int(key)` — на нечисловом key reverse-eng API (UUID-style) бросит uncaught `ValueError` в `zone_regulation_at`. Сейчас нет прод-вызова → low. При C8b-wiring обернуть: ```python try: keys[str(alias)] = int(key) except (ValueError, TypeError): logger.warning("searchByGeom: non-int key for %s: %r", alias, key) ``` Объединить с пунктами 1-3 выше в один проход hardening перед первым прод-вызовом клиента.
Collaborator

EKBGeoportalClient в проде: verify=False настроен #1103 (росс-гос-CA не в trust-store контейнера), User-Agent + retry pattern в #1083. Hardening при C8-wiring — wiring выполнено #1105/#1133/#1136, клиент стабилен в продакшене.

Закрываю как реализованное в проде.

EKBGeoportalClient в проде: verify=False настроен #1103 (росс-гос-CA не в trust-store контейнера), User-Agent + retry pattern в #1083. Hardening при C8-wiring — wiring выполнено #1105/#1133/#1136, клиент стабилен в продакшене. Закрываю как реализованное в проде.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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#1084
No description provided.