(low) search.py/_validate_geo_pair: cross-field min>max через truthiness — граничный 0 обходит проверку #1517

Closed
opened 2026-06-15 05:59:07 +00:00 by bot-backend · 0 comments
Collaborator

Severity: low · Категория: logic · Файл: tradein-mvp/backend/app/schemas/search.py:63-66

search.py:63-66. Валидатор инвертированных диапазонов написан через truthiness обоих операндов: if self.price_rub_min and self.price_rub_max and self.price_rub_min > self.price_rub_max и аналогично для area (строки 65-66). Все четыре поля объявлены с ge=0 (search.py:25-28), то есть 0 — валидное входное значение, но falsy в Python. Поэтому при max==0 (или min==0) условие min and max коротко замыкается на 0 и проверка min > max НЕ выполняется. Импакт: запрос price_rub_min=5_000_000, price_rub_max=0 проходит валидацию, а build_search_query (services/search_query.py:51-56) применяет оба предиката через is not Noneprice_rub >= 5000000 AND price_rub <= 0 → всегда пустая выборка вместо ожидаемого 422 'price_rub_min > price_rub_max'. То же для area_m2_max=0. Клиент получает молчаливо 0 результатов, нарушая контракт валидатора. Severity low: max=0 — вырожденный сценарий (поиск жилья с ценой/площадью ≤0 нетипичен для реального клиента). Как чинить: заменить truthiness на is not None, как уже сделано выше в этом же валидаторе для lat/lon (строка 59): if self.price_rub_min is not None and self.price_rub_max is not None and self.price_rub_min > self.price_rub_max.

Почему баг: Поля допускают 0 (ge=0), а защитный валидатор инвертированного диапазона завязан на truthiness, поэтому ровно при значении-границе 0 он молча отключается, и в SQL уходит самопротиворечивый предикат (>=X AND <=0), дающий пустой результат вместо явной ошибки валидации. Это нарушает заявленный контракт валидатора (он специально добавлен, чтобы ловить min>max).

Как чинить: Заменить truthiness на явную проверку на None, как в проверке lat/lon выше: if self.price_rub_min is not None and self.price_rub_max is not None and self.price_rub_min > self.price_rub_max: ... и аналогично для area_m2_min/max. Заодно стоит добавить такие же cross-field проверки для floor_min/floor_max, floors_total_min/floors_total_max и year_built_min/year_built_max — сейчас по ним cross-field валидации нет вовсе, и инвертированный диапазон (floor_min=10, floor_max=2) тоже молча даёт пустую выборку.


deep-audit «армия ботов 2» · verify+harden, confidence 0.92 · unit U123

**Severity:** low · **Категория:** logic · **Файл:** `tradein-mvp/backend/app/schemas/search.py:63-66` search.py:63-66. Валидатор инвертированных диапазонов написан через truthiness обоих операндов: `if self.price_rub_min and self.price_rub_max and self.price_rub_min > self.price_rub_max` и аналогично для area (строки 65-66). Все четыре поля объявлены с ge=0 (search.py:25-28), то есть 0 — валидное входное значение, но falsy в Python. Поэтому при max==0 (или min==0) условие `min and max` коротко замыкается на 0 и проверка `min > max` НЕ выполняется. Импакт: запрос price_rub_min=5_000_000, price_rub_max=0 проходит валидацию, а build_search_query (services/search_query.py:51-56) применяет оба предиката через `is not None` → `price_rub >= 5000000 AND price_rub <= 0` → всегда пустая выборка вместо ожидаемого 422 'price_rub_min > price_rub_max'. То же для area_m2_max=0. Клиент получает молчаливо 0 результатов, нарушая контракт валидатора. Severity low: max=0 — вырожденный сценарий (поиск жилья с ценой/площадью ≤0 нетипичен для реального клиента). Как чинить: заменить truthiness на `is not None`, как уже сделано выше в этом же валидаторе для lat/lon (строка 59): `if self.price_rub_min is not None and self.price_rub_max is not None and self.price_rub_min > self.price_rub_max`. **Почему баг:** Поля допускают 0 (ge=0), а защитный валидатор инвертированного диапазона завязан на truthiness, поэтому ровно при значении-границе 0 он молча отключается, и в SQL уходит самопротиворечивый предикат (>=X AND <=0), дающий пустой результат вместо явной ошибки валидации. Это нарушает заявленный контракт валидатора (он специально добавлен, чтобы ловить min>max). **Как чинить:** Заменить truthiness на явную проверку на None, как в проверке lat/lon выше: `if self.price_rub_min is not None and self.price_rub_max is not None and self.price_rub_min > self.price_rub_max: ...` и аналогично для area_m2_min/max. Заодно стоит добавить такие же cross-field проверки для floor_min/floor_max, floors_total_min/floors_total_max и year_built_min/year_built_max — сейчас по ним cross-field валидации нет вовсе, и инвертированный диапазон (floor_min=10, floor_max=2) тоже молча даёт пустую выборку. --- <sub>deep-audit «армия ботов 2» · verify+harden, confidence 0.92 · unit U123</sub>
bot-backend added the
week ревью 1
label 2026-06-15 05:59:07 +00:00
bot-backend added the
scope/backend
label 2026-06-15 11:02:17 +00:00
Sign in to join this conversation.
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#1517
No description provided.