HTTP API закрыт за вход через OIDC у Authelia
- шаг схемы закрывает поверхность, которую хранилище приносит открытой: собственную регистрацию, вход по паролю и одноразовый код — без этого закрытие приёма обходилось двумя запросами - продление сессии выключено, срок семь суток: иначе отзыв доступа у провайдера до сервиса не доходит никогда - файл записи отдаётся вошедшему по токену файла — пересмотр ADR-2026-08-12-file-link-open-but-not-logged
This commit is contained in:
+105
-2
@@ -132,6 +132,20 @@
|
||||
(CLAUDE.md, «Инварианты»).
|
||||
- `autotests`: покрыт ли изменённый шаг конвейера хоть одним тестом — сегодня
|
||||
тестов два файла, и оба мимо конвейера.
|
||||
- `autotests`: судит ли проверка ответа по готовому ответу, а не по изменяемому
|
||||
состоянию обработчика — класс всплывал трижды, последний раз 2026-08-12 на
|
||||
уборке носителя состояния входа.
|
||||
- `security`: не открылась ли снова поверхность, которую приносит хранилище, —
|
||||
собственная регистрация, вход по паролю, одноразовый код, восстановление
|
||||
доступа, продление сессии. Всё это приходит включённым и закрывается нами
|
||||
(задача `oidc-login` 2026-08-12).
|
||||
- `security`: не появился ли второй способ получить сессию к тому же человеку —
|
||||
заголовок вместо куки назван осознанно, прочие способы обязаны быть закрыты.
|
||||
- `operations`: доходит ли отзыв доступа у провайдера до сервиса и за какой срок —
|
||||
после входа сервис к провайдеру не обращается, и канал здесь один
|
||||
(ADR-2026-08-12-session-without-refresh).
|
||||
- `architecture`: не зовётся ли на каждый запрос то, что меняет состояние
|
||||
приложения, — сборка роутера хранилища оказалась именно такой.
|
||||
|
||||
### Триггеры метки
|
||||
|
||||
@@ -179,7 +193,14 @@ API и имя не откатываются обратной правкой по
|
||||
- `operations`: реальный профиль нагрузки. Проект работает на единицах записей в
|
||||
день, и утверждения о росте остаются условиями, а не замерами;
|
||||
- `security`: стойкость `ffmpeg` к вредоносному входу — разбор чужого формата
|
||||
отдан внешней программе, и она вне нашей границы.
|
||||
отдан внешней программе, и она вне нашей границы;
|
||||
- `security`: поведение настоящей Authelia и её правило на нашего клиента.
|
||||
Провайдера в прогоне нет, подменяет его свой сервер; кто допущен — настройка
|
||||
выкладки вне репозитория, и по коду её не проверить
|
||||
([adr/ADR-2026-08-12-access-delegated-to-provider.md](adr/ADR-2026-08-12-access-delegated-to-provider.md));
|
||||
- `security`: поведение браузера с куками — применение `SameSite`, приём
|
||||
`Set-Cookie` при переходе с чужого сайта. Браузера в прогоне нет, и находки
|
||||
этого рода остаются гипотезами.
|
||||
|
||||
**Перестали проверять сознательно:**
|
||||
|
||||
@@ -187,7 +208,13 @@ API и имя не откатываются обратной правкой по
|
||||
2026-08-11 — правда, звали так, что он всегда отказывал, — а теперь получают
|
||||
длительность от подставного источника. Своего теста у
|
||||
`adapter/metaviewer/ffmpeg` нет; решение и его цена — в
|
||||
[adr/ADR-2026-08-11-stub-adapters-in-tests.md](adr/ADR-2026-08-11-stub-adapters-in-tests.md).
|
||||
[adr/ADR-2026-08-11-stub-adapters-in-tests.md](adr/ADR-2026-08-11-stub-adapters-in-tests.md);
|
||||
- **всё, что требует поднять сервис целиком.** Локальный запуск роняет адаптер
|
||||
Telegram: он проверяет токен обращением к Telegram, а боевым токеном
|
||||
запускаться запрещено. Значит поведенческая верификация живым прогоном
|
||||
недоступна ни одной задаче, и заменяют её проверки поверх настоящего роутера
|
||||
хранилища. Замечено 2026-08-12 задачей `oidc-login`; своей задачи на это пока
|
||||
нет.
|
||||
|
||||
## Журнал дефектов
|
||||
|
||||
@@ -197,6 +224,82 @@ API и имя не откатываются обратной правкой по
|
||||
поймать их было некому. У восстановленных нет поля «Чем воспроизведён», и
|
||||
выдумывать его задним числом нельзя.
|
||||
|
||||
## 2026-08-12 — закрыли поверхность так, что войти не мог никто [пойман ревью]
|
||||
|
||||
- **Где:** шаг схемы `202608120001` задачи `oidc-login`, правило создания записи
|
||||
в коллекции пользователей
|
||||
- **Симптом:** `users.CreateRule = nil` закрывало создание записи для всех, кроме
|
||||
владельца панели. Запись при первом входе заводит внутренний запрос самого
|
||||
обмена, идущий без таких прав, — значит после выкладки вход не сработал бы ни
|
||||
у кого, включая владельца, а приём и опрос уже были закрыты. Сервис остался бы
|
||||
доступен только через Telegram, и чинилось бы это руками в панели
|
||||
- **Причина:** закрывали ровно то, ради чего задача затевалась, — самостоятельную
|
||||
регистрацию, которую хранилище приносит открытой. Глухое `nil` выглядит самым
|
||||
надёжным её закрытием и отвергает заодно единственный законный путь заведения
|
||||
записи. Различить их можно: обмен помечает свой запрос контекстом `oauth2`
|
||||
- **Чем воспроизведён:** тестом против настоящего хранилища с подставным
|
||||
провайдером: возврат от провайдера отвечал `401`, обращений к токен-эндпоинту
|
||||
`1`, учётных записей после входа `0`. Причина изолирована тем же прогоном —
|
||||
с открытым правилом возврат давал `302` и запись появлялась
|
||||
- **Почему не поймали раньше:** все проверки задачи заводили учётную запись
|
||||
прямым сохранением, мимо входа, и потому шли по коду, который в бою не
|
||||
исполняется. Гейт был зелёным. Поймали два прохода независимо — разбор кода по
|
||||
исходникам библиотеки и враждебный проход падающим тестом
|
||||
- **Что меняем:** правило сузили до контекста обмена
|
||||
(`@request.context = "oauth2"`), а в набор проверок добавили вход целиком через
|
||||
подставного провайдера — от увода до куки сессии. Проверка, заводящая запись
|
||||
мимо входа, больше не считается покрытием входа
|
||||
|
||||
## 2026-08-12 — проверка не могла упасть: читала живую карту заголовков вместо ответа [пойман ревью]
|
||||
|
||||
- **Где:** `internal/controller/http/auth_test.go`, проверка уборки носителя
|
||||
состояния входа; сам дефект — в `auth.go`, уборка стояла в `defer`
|
||||
- **Симптом:** носитель состояния и проверочного кода не убирался ни на успешном
|
||||
возврате, ни на отказном, и жил свои десять минут. Одноразовость возврата
|
||||
держалась ровно на этой уборке, то есть тоже не работала. Проверка при этом
|
||||
была зелёной и утверждала обратное
|
||||
- **Причина:** двойная. В коде — `defer` исполняется после того, как ответ уже
|
||||
начали писать, а заголовки к этому моменту зафиксированы снимком, и позднейшая
|
||||
правка их карты до браузера не доезжает. В проверке — `httptest` устроен
|
||||
зеркально: `Header()` отдаёт живую карту, а снимок лежит отдельно и читается
|
||||
через `Result()`. Проверка смотрела в живую карту и видела то, чего клиент не
|
||||
получит
|
||||
- **Чем воспроизведён:** отдельной программой вне проекта: на настоящем сервере
|
||||
ответ приходил с пустым `Set-Cookie`, а тот же обработчик под `httptest`
|
||||
показывал куку в `Header()` и не показывал в `Result()`
|
||||
- **Почему не поймали раньше:** оракул был ложным по построению, и никакая
|
||||
регрессия его не разбудила бы. Гейт зелёный. Поймали два прохода — сверка
|
||||
требований и разбор кода, — оба воспроизведением, а не чтением
|
||||
- **Что меняем:** уборка перенесена до записи ответа; все проверки этого файла
|
||||
судят по `Result()`. Класс всплывает **третий раз** (2026-08-10 «тесты
|
||||
http-обработчика ни разу не были зелёными», 2026-08-11 «проверка приёма не
|
||||
могла упасть»), поэтому он же уходит кандидатом в конвенции: проверка ответа
|
||||
судит по готовому ответу, а не по изменяемому состоянию обработчика
|
||||
|
||||
## 2026-08-12 — каждый анонимный запрос навсегда замедлял запись в хранилище [пойман ревью]
|
||||
|
||||
- **Где:** `internal/controller/http/auth.go`, обмен кода собирал роутер
|
||||
хранилища на каждый вызов
|
||||
- **Симптом:** сборка роутера вешает девять обработчиков на само приложение и
|
||||
без идентификатора, поэтому повторная не заменяет прежние, а добавляет.
|
||||
Обработчики исполняются на каждой записи в хранилище, а конвейер пишет задачу на
|
||||
каждом шаге. Освобождения нет — только перезапуск. Раскачивалось анонимно:
|
||||
атакующий ставит себе куку состояния сам, и сверка сравнивает две его же
|
||||
величины, а обмен исполняется раньше обращения к провайдеру
|
||||
- **Причина:** функция сборки выглядит чистой — она возвращает роутер, и по имени
|
||||
не видно, что она правит приложение. Решение звать собственный адрес хранилища
|
||||
внутри процесса сделало эту сборку частью горячего пути
|
||||
- **Чем воспроизведён:** замером на настоящем приложении: пять вызовов подряд
|
||||
подняли число обработчиков одного события с 4 до 14; 3000 анонимных возвратов
|
||||
довели сотню сохранений записи с 3.86 мс до 59.8 мс и кучу на 5013 КиБ. При
|
||||
недоступном провайдере утечка сохранялась
|
||||
- **Почему не поймали раньше:** ни один шаг гейта не смотрит на побочные эффекты
|
||||
вызова библиотеки, а замер требует прогона. Поймали три прохода — архитектурный
|
||||
зондом, враждебный падающим тестом, сверка требований чтением
|
||||
- **Что меняем:** роутер собирается один раз и живёт полем обработчика; в набор
|
||||
проверок добавлена та, что считает длину очереди обработчиков после двадцати
|
||||
входов
|
||||
|
||||
## 2026-08-12 — образ не собирался, и этого не увидел никто [проскочил]
|
||||
|
||||
**Что сломалось.** `go mod tidy` поднял директиву `go` в `go.mod` до `1.25.0` —
|
||||
|
||||
Reference in New Issue
Block a user