Files
transcriber/tasks/items/identity-headers-reject-multivalue.md
T
av 11269c1567 учёт: заведён урожай ревью входа по конфигу
- пять задач партии review-2026-08-23: многозначность Remote-Name и Remote-Email,
  мелочи входа, механизация правила о зависимостях конфига, снятие мёртвого
  читателя .env, управляющие знаки в расширении
- две существующие разведки дополнены находками того же прогона: адрес объекта в
  тексте отказа SpeechKit и выводимость имени копии из журнала
2026-08-23 14:03:52 +03:00

5.7 KiB
Raw Blame History

🐞 Судить многозначность Remote-Name и Remote-Email тем же правилом, что и логин

  • Тип: fix
  • Категория: Очередь — Правило узнавания правится там же, где личность связывается с учётной записью: обе задачи трогают один сбор contract.Identity
  • Зачем: чужой адрес, занятый первым пришедшим, навсегда и молча оставляет без почты настоящего владельца: EnsureUser найденную запись не переписывает, ветвь отката заводит владельца без почты и без строки журнала
  • Теги: review-2026-08-23

Многозначный Remote-User даёт 401 — многозначный Remote-Email даёт 200, и в базу ложится первое значение. Правило узнавания судит логин и не судит остальные заголовки личности.

Один и тот же адрес почты, занятый чужим логином, достаётся первому пришедшему навсегда: EnsureUser найденную запись не переписывает, а ветвь отката уникальности заводит настоящего владельца без почты — и делает это молча, без строки журнала.

Нашёл прогон ревью config-test-headers-login 2026-08-23, оракул воспроизведён.

Воспроизведение

Свой падающий сценарий, прогнан на настоящей поверхности сервиса (setupEnv + AppChain, база SQLite во временном каталоге, маршрут /app/me). Два шага:

  1. Запрос с Remote-User: squatter и Remote-Email: victim@corp.example с доверенного адреса → 200, SELECT email FROM users WHERE provider_login='squatter'victim@corp.example.
  2. Запрос настоящего владельца Remote-User: victim, Remote-Email: victim@corp.example200, SELECT email … WHERE provider_login='victim'"", строк журнала о занятом адресе ноль.

Ожидалось: многозначный заголовок судится тем же правилом, что и логин, а откат по занятому адресу виден владельцу сервиса строкой журнала.

Затрагивает

  • internal/controller/http/identity.go:134-138 — сбор contract.Identity из заголовков: Login берётся из проверенного перечня значений, Name и Email — через Header.Get, то есть первым значением без суждения;
  • internal/adapter/repo/sqlite/identity.go:77-110 — ветвь отката уникальности почты: заводит запись с пустым адресом и не пишет ни строки;
  • требование спеки access: сегодня оно нормирует многозначность только для Remote-User;
  • docs/security.md, раздел «Периметр» — что именно сервис принимает от контура на веру.

Критерии приёмки

  • Многозначный Remote-Email даёт пустое значение, а не первое. Оракул: тест на настоящей поверхности сервиса — запрос с двумя значениями Remote-Email заводит учётную запись с пустым адресом; тот же тест на Remote-Name.
  • Узнавание принимает однозначные Remote-Name и Remote-Email по-прежнему. Оракул: тот же тест — одно значение доезжает до колонки без изменений.
  • Ветвь отката уникальности почты оставляет строку журнала видимого уровня. Оракул: тест хранилища — второй логин с занятым адресом заводится, и в журнале стоит строка о занятом адресе; значения адреса в ней нет.

Рамки

Правится боевой путь узнавания, а не отладочная подстановка заголовков. Владение адресом почты между учётными записями не пересматривается: занятый адрес по-прежнему достаётся тому, кто завёлся первым, — задача только о том, чтобы это перестало происходить молча и по многозначному заголовку.

Отдельно: требование к контуру про X-Forwarded-For не записано ни в модели угроз, ни в спеке и этой правкой не покрывается — правило «справа налево до первого недоверенного» верно постольку, поскольку правое значение приписал прокси, а шаблон лежит в files/caddyproxy/Caddyfile.template репозитория pet-project-server. Отдельной записи не заводится — решение владельца.