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

69 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🐞 Судить многозначность 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.example` → `200`, `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`. Отдельной записи не заводится — решение владельца.