test(tradein/auth): правило про синхронную сверку — сторожем, а не комментарием (#2715) #2735

Merged
bot-backend merged 2 commits from test/2715-verify-password-call-site-guard into main 2026-08-06 14:35:33 +00:00

2 commits

Author SHA1 Message Date
3a7dbd4dda test(tradein/auth): сторож не должен зеленеть от съехавшей области (#2715)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m6s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
Ревью: `rglob` по несуществующему каталогу не падает — отдаёт пусто, нарушителей
ноль, тест зелёный навсегда. Достаточно переложить файл в подкаталог tests/
(их восемь, и прецедент такого сторожа лежит именно в подкаталоге), чтобы
`parents[1]` уехал и сторож замолчал. Ровно тот класс, от которого детектор уже
защищён (test_detector_actually_detects), а область — нет. Одна строка:
`_OWNER.exists()` в теле теста; проверено мутацией области — краснеет.

Плюс слепые зоны названы и закреплены исполняемо: строковый доступ
`getattr(mod, "verify_password")` и прямой `bcrypt.checkpw` в обход модуля
сторож НЕ ловит. От намеренного обхода он не защищает и не может — только от
нечаянного. Было требованием ревью назвать это в коде, а не подразумевать.

Refs #2715
2026-08-06 19:20:19 +05:00
1dc3a08563 test(tradein/auth): правило про синхронную сверку — сторожем, а не комментарием (#2715)
All checks were successful
CI / changes (pull_request) Successful in 9s
CI Trade-In / changes (pull_request) Successful in 11s
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 / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 3m15s
Пункт 3 хвоста #2715. Правило «из async def зови ТОЛЬКО verify_password_bounded»
жило в docstring app/core/password.py, а синхронная verify_password оставалась
публичной и импортируемой. Одной строчки asyncio.to_thread(verify_password, …)
в будущем коде хватит, чтобы получить вынос в поток вообще без учёта слотов:
внешне быстро, а потолок перебора (#2665) и доля на ключ (#2714) тихо исчезают.

Сторож по AST, а не по grep: verify_password поминается в комментариях и
docstring'ах (auth.py, config.py) — текстовый поиск краснел бы на них, а форму
без скобок (to_thread(verify_password, …)) — ровно ту, ради которой всё и
пишется — не поймал бы вовсе.

Сам детектор проверен на себе (test_detector_actually_detects): пустой детектор
прошёл бы все файлы и выглядел бы рабочим сторожем. Проверено мутацией: импорт
verify_password в app/api/v1/team.py красит тест.

Прецедент такого сторожа: backend/tests/sql/test_auth_sql_migrations.py.

Refs #2715
2026-08-06 17:39:49 +05:00