fix(tradein): scope GET /estimate/{id} + /pdf to owner, close IDOR (#690) #702

Merged
bot-reviewer merged 3 commits from fix/690-idor-estimate-scope into main 2026-05-30 08:56:51 +00:00

3 commits

Author SHA1 Message Date
lekss361
bf55b4b80e fix(tradein): extend IDOR guard to all estimate_id routes (#690 review)
Ревью PR #702: #690 закрыл только GET /estimate/{id} + /pdf, но IDOR того же
класса оставался в 10 derived-роутах. Критично — photo-эндпоинты отдают сырые
байты фото интерьера квартиры, enumerable по UUID оценки.

Добавлен _assert_estimate_access_by_id() (тянет created_by по estimate_id →
_assert_estimate_access). Guard навешан на: GET/POST /photos,
/photos/{photo_id} (write+read PII), /houses, /placement-history,
/house-analytics, /cian-price-changes, /sell-time-sensitivity, /imv-benchmark.
upload_photo: existence SELECT 1 → SELECT created_by (без лишнего запроса).

Тесты: photo owner/other/admin/no-header (200/404/200/401), list_photos,
upload write-side 404, derived-роут houses+imv, + закрыты gap'ы ревью
(legacy NULL created_by → 404, unknown-user → 403 на /pdf). 27 passed.
2026-05-30 11:47:47 +03:00
lekss361
ddbaa10ada Merge remote-tracking branch 'forgejo/main' into fix/690-idor-estimate-scope 2026-05-30 11:42:08 +03:00
lekss361
5eeb12cff8 fix(tradein): scope GET /estimate/{id} + /pdf to owner, close IDOR (#690)
Любой авторизованный pilot мог читать/скачивать чужую оценку (включая
client_name/client_phone) по UUID — GET /estimate/{id} и /estimate/{id}/pdf
не проверяли владельца.

Добавлен guard _assert_estimate_access(): зеркало скоупинга /history (#656).
Эндпоинты тянут created_by из trade_in_estimates и пропускают только
владельца (created_by == X-Authenticated-User) или admin (get_role);
иначе 404 (скрываем существование), 401 без заголовка, 403 если юзер
вне roles.yaml.

ruff-format переформатировал весь файл (rev-дрейф pinned 0.7.4) — семантика
правок локальна: helper + header-param + created_by в двух SELECT.

Closes #690
2026-05-30 11:20:48 +03:00