Запрос с телом не в JSON отвечает 500 вместо 422 #52

Closed
opened 2026-09-13 14:10:31 +03:00 by Agent · 2 comments
Collaborator

Что происходит

Если JSON-эндпойнт получает тело с другим Content-Type (например, text/plain, так отправляет HTML-форма), сервер отвечает 500 вместо 422.

curl -H 'Content-Type: text/plain' -d '{"id":1}' http://127.0.0.1:8000/api/auth/telegram                 → 500
curl -H 'Content-Type: text/plain' -d '{"username":"a","password":"b"}' .../api/admin/auth/login        → 500

В логе:

File "backend/app/main.py", in _validation_handler
TypeError: Object of type bytes is not JSON serializable
when serializing dict item 'input'

Причина

_validation_handler в backend/app/main.py кладёт exc.errors() в details как есть. В этом случае в input лежит сырое тело типа bytes, и JSONResponse не может его сериализовать.

Сопутствующее

Тот же details[].input возвращает клиенту тело запроса целиком. Например, при нехватке поля во входе {"nickname": "x", "password": "..."} пароль уходит обратно в ответе 422. Постороннему это не утекает, но пароль может осесть в логах прокси и инструментах отладки. При исправлении стоит убирать input из ответа или хотя бы значения полей с паролями.

Влияние

Запрос в любом случае отклоняется, сессия не создаётся, поэтому login CSRF через HTML-форму по-прежнему не срабатывает. Страдают корректность ответа и логи: вместо понятной 422 появляется трейсбек.

Критерии готовности

  • Тело не в JSON на JSON-эндпойнте даёт 422 VALIDATION_ERROR, а не 500.
  • В ответе 422 нет значений паролей из тела запроса.
  • Тест на оба случая.

Обнаружено при работе над #24.

# Что происходит Если JSON-эндпойнт получает тело с другим `Content-Type` (например, `text/plain`, так отправляет HTML-форма), сервер отвечает **500** вместо 422. ``` curl -H 'Content-Type: text/plain' -d '{"id":1}' http://127.0.0.1:8000/api/auth/telegram → 500 curl -H 'Content-Type: text/plain' -d '{"username":"a","password":"b"}' .../api/admin/auth/login → 500 ``` В логе: ``` File "backend/app/main.py", in _validation_handler TypeError: Object of type bytes is not JSON serializable when serializing dict item 'input' ``` # Причина `_validation_handler` в `backend/app/main.py` кладёт `exc.errors()` в `details` как есть. В этом случае в `input` лежит сырое тело типа `bytes`, и `JSONResponse` не может его сериализовать. # Сопутствующее Тот же `details[].input` возвращает клиенту тело запроса целиком. Например, при нехватке поля во входе `{"nickname": "x", "password": "..."}` пароль уходит обратно в ответе 422. Постороннему это не утекает, но пароль может осесть в логах прокси и инструментах отладки. При исправлении стоит убирать `input` из ответа или хотя бы значения полей с паролями. # Влияние Запрос в любом случае отклоняется, сессия не создаётся, поэтому login CSRF через HTML-форму по-прежнему не срабатывает. Страдают корректность ответа и логи: вместо понятной 422 появляется трейсбек. # Критерии готовности - Тело не в JSON на JSON-эндпойнте даёт 422 `VALIDATION_ERROR`, а не 500. - В ответе 422 нет значений паролей из тела запроса. - Тест на оба случая. Обнаружено при работе над #24.
Agent added this to the 1.37 - смена аутентификации и смежный пен тест milestone 2026-09-13 14:10:31 +03:00
Agent added the Kind/Bug label 2026-09-13 14:10:32 +03:00
Agent self-assigned this 2026-09-13 14:14:31 +03:00
Agent added the
Reviewed
Confirmed
1
label 2026-09-13 14:14:32 +03:00
Author
Collaborator

План выполнения

  1. _validation_handler в backend/app/main.py отдаёт в details только type, loc и msg каждой ошибки. Поля input (эхо тела, в том числе паролей и сырых bytes) и ctx (может содержать объекты исключений) в ответ не попадают.
  2. Формат конверта и код VALIDATION_ERROR не меняются. Фронт details ошибок валидации не читает, поэтому интерфейс не затрагивается.
  3. Новые тесты в backend/tests/test_validation_errors.py:
    • text/plain на /api/auth/telegram и /api/admin/auth/login → 422;
    • в ответе на неполное тело входа нет пароля;
    • loc по-прежнему указывает на поле.

Критерии готовности

  • Тело не в JSON на JSON-эндпойнте даёт 422 VALIDATION_ERROR, а не 500.
  • В ответе 422 нет значений из тела запроса.
  • Тесты падают на старом коде и проходят на новом; вся сюита зелёная.

Ветка: issue-52-validation-500 от dev

## План выполнения 1. `_validation_handler` в `backend/app/main.py` отдаёт в `details` только `type`, `loc` и `msg` каждой ошибки. Поля `input` (эхо тела, в том числе паролей и сырых `bytes`) и `ctx` (может содержать объекты исключений) в ответ не попадают. 2. Формат конверта и код `VALIDATION_ERROR` не меняются. Фронт `details` ошибок валидации не читает, поэтому интерфейс не затрагивается. 3. Новые тесты в `backend/tests/test_validation_errors.py`: - `text/plain` на `/api/auth/telegram` и `/api/admin/auth/login` → 422; - в ответе на неполное тело входа нет пароля; - `loc` по-прежнему указывает на поле. **Критерии готовности** - Тело не в JSON на JSON-эндпойнте даёт 422 `VALIDATION_ERROR`, а не 500. - В ответе 422 нет значений из тела запроса. - Тесты падают на старом коде и проходят на новом; вся сюита зелёная. **Ветка:** `issue-52-validation-500` от `dev`
Agent added the
Status
In Review
label 2026-09-13 15:02:26 +03:00
Author
Collaborator

Работа выполнена, открыт PR: #54

Итог: ошибка валидации отдаёт в details только type, loc и msg. Тело не в JSON теперь получает 422 вместо 500, и тело запроса (пароли в том числе) больше не возвращается в ответе.
Проверки:

  • pytest — 105 passed, 3 новых теста; на старом коде 2 из них падают.
  • curl: text/plain → 422, пароля в ответе нет.

Статус: Status/In Review
Осталось за вами: ревью и мёрж PR — задача закроется автоматически.

Работа выполнена, открыт PR: https://gitea.arseniev.info/NotBigGhost/ForbiddenStarsApp/pulls/54 **Итог:** ошибка валидации отдаёт в `details` только `type`, `loc` и `msg`. Тело не в JSON теперь получает 422 вместо 500, и тело запроса (пароли в том числе) больше не возвращается в ответе. **Проверки:** - `pytest` — 105 passed, 3 новых теста; на старом коде 2 из них падают. - `curl`: `text/plain` → 422, пароля в ответе нет. **Статус:** `Status/In Review` **Осталось за вами:** ревью и мёрж PR — задача закроется автоматически.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: NotBigGhost/ForbiddenStarsApp#52