docs: drop the closed phase-14 hardening out of the plan

The plan holds only open work, but section A still retold what phase 14
implemented — security headers, the origin check, the __Host- cookie, the
setup-token documentation — which the CHANGELOG and git history already
describe in full. What is left of A is the two things deliberately *not*
closed: the POST that carries neither Sec-Fetch-Site nor Origin, and the
absence of session-bound CSRF tokens. The XSS note folds into the token item,
which is the only place it was doing work; the "proxy must pass Host through"
requirement is documented in the README and in the CHANGELOG, so it goes.

Section A's numbered items are gone with it, so B-D renumber from 1. Nothing
else in the file has been implemented: the README still says nothing about
restarts signing the admin out or about the copytruncate rotation window,
SELFPOST_HOSTNAME still falls back to localhost without a warning, and CI
still runs only vet and unit tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 21:38:40 +03:00
parent 6c61d53239
commit 934972ce48
+18 -28
View File
@@ -11,20 +11,16 @@
## Открытые вопросы — требует внимания и обсуждения (перед фиксацией v1.0) ## Открытые вопросы — требует внимания и обсуждения (перед фиксацией v1.0)
Базовый план 0→11 выполнен и **соответствует ТЗ** (все обязательные пункты 7.6 Ниже — то, что **выходит за букву ТЗ**, но заслуживает решения перед тем, как
подтверждены аудитом Фазы 11). Ниже — то, что **выходит за букву ТЗ**, но считать v1.0 «финальным». Ничего из этого **не является дефектом
заслуживает решения перед тем, как считать v1.0 «финальным». Ничего из этого соответствия**; это осознанные компромиссы и потенциальные улучшения. Каждый
**не является дефектом соответствия**; это осознанные компромиссы и пункт — решение «делаем в v1.x / откладываем в 2.x / оставляем как есть»,
потенциальные улучшения. Каждый пункт — решение «делаем в v1.x / откладываем в принимается пользователем.
2.x / оставляем как есть», принимается пользователем.
### A. Безопасность — hardening сверх обязательного 7.6 ### A. Безопасность — принятые риски
Все четыре пункта раздела — security-заголовки, проверка origin, cookie Hardening сверх обязательного 7.6 закрыт. Здесь остаётся только то, что закрыто
`__Host-` с обнаружением дублей и документация про `/data/setup-token` **сознательно не было**, чтобы это не потерялось:
**реализованы (Фаза 14)**; что именно сделано, см. [CHANGELOG.md](../CHANGELOG.md)
и `git log`. Здесь остаётся только то, что закрыто **сознательно не было**,
чтобы это не потерялось:
- **Принятый риск: `POST` без `Sec-Fetch-Site` и без `Origin` пропускается.** - **Принятый риск: `POST` без `Sec-Fetch-Site` и без `Origin` пропускается.**
Клиент, не посылающий ни одного из двух — по-настоящему старый браузер или Клиент, не посылающий ни одного из двух — по-настоящему старый браузер или
@@ -38,31 +34,25 @@
закрывает соседний поддомен, но зависит от поведения браузера; токен — нет. закрывает соседний поддомен, но зависит от поведения браузера; токен — нет.
Цена — скрытое поле примерно в двух десятках форм. Триггером вернуться к Цена — скрытое поле примерно в двух десятках форм. Триггером вернуться к
вопросу считать появление требования «устойчиво независимо от браузера». вопросу считать появление требования «устойчиво независимо от браузера».
- **XSS внутри самой панели** не закрывается ни проверкой origin, ни токенами: От XSS внутри самой панели не спас бы и токен: код, исполняющийся в origin
код, исполняющийся в origin панели, отправит запрос сам. Против него панели, отправит запрос сам — против этого работают автоэкранирование
работают автоэкранирование `html/template` (7.6.7) и CSP поэтому шаблоны `html/template` (7.6.7) и CSP, поэтому шаблоны не должны содержать
не должны содержать inline-скриптов и inline-стилей; это закреплено inline-скриптов и inline-стилей.
тестом-стражем в [internal/web/templates_test.go](../internal/web/templates_test.go),
а не только договорённостью.
- **Требование к развёртыванию, появившееся вместе с проверкой origin:**
reverse-proxy обязан передавать исходный заголовок `Host`. Все четыре
поставляемых фрагмента это делают; чужой прокси, переписывающий `Host`,
превратит каждый `POST` в `403` (в лог пишутся обе сравниваемые стороны).
### B. Надёжность и эксплуатация ### B. Надёжность и эксплуатация
5. **Сессии только в памяти** — рестарт/редеплой разлогинивает админа (по ТЗ 9 допустимо). Плюс: absolute TTL 12ч без отдельного idle-timeout и без ротации токена при логине (session-fixation здесь неактуален, т.к. токен выдаётся только после аутентификации). Оставить; отметить поведение в README. 1. **Сессии только в памяти** — рестарт/редеплой разлогинивает админа (по ТЗ 9 допустимо). Плюс: absolute TTL 12ч без отдельного idle-timeout и без ротации токена при логине (session-fixation здесь неактуален, т.к. токен выдаётся только после аутентификации). Оставить; отметить поведение в README.
6. **Окно потери строк мониторингового лога при ротации** (`copytruncate`, Фаза 10) — несколько строк `mail.log` могут потеряться в момент ротации. Приемлемо для мониторинга; зафиксировать как известное свойство. 2. **Окно потери строк мониторингового лога при ротации** (`copytruncate`, Фаза 10) — несколько строк `mail.log` могут потеряться в момент ротации. Приемлемо для мониторинга; зафиксировать как известное свойство.
7. **Поведение при незаданном `SELFPOST_HOSTNAME`** — realm SASL и хост setup-ссылки падают в `localhost`. Для реального деплоя hostname обязателен. **Вопрос:** делать ли фатальную проверку «hostname обязателен» на старте (сейчас — мягкий fallback) — предложение: предупреждать громко в лог, но не падать. 3. **Поведение при незаданном `SELFPOST_HOSTNAME`** — realm SASL и хост setup-ссылки падают в `localhost`. Для реального деплоя hostname обязателен. **Вопрос:** делать ли фатальную проверку «hostname обязателен» на старте (сейчас — мягкий fallback) — предложение: предупреждать громко в лог, но не падать.
### C. CI и тесты ### C. CI и тесты
8. **Нет интеграционного/e2e-теста в CI.** Контейнерные e2e каждой фазы прогонялись вручную и задокументированы в git-истории, но не автоматизированы. Для v1.0 — вероятно, оставить ручными; для долгой поддержки — кандидат на smoke-тест (поднять контейнер, setup→login→add domain→auth SMTP) в CI. 4. **Нет интеграционного/e2e-теста в CI.** Контейнерные e2e каждой фазы прогонялись вручную и задокументированы в git-истории, но не автоматизированы. Для v1.0 — вероятно, оставить ручными; для долгой поддержки — кандидат на smoke-тест (поднять контейнер, setup→login→add domain→auth SMTP) в CI.
### D. Указатель на объём 2.x ### D. Указатель на объём 2.x
9. **Входящий релей и pluggable-антиспам** вынесены в опциональные фазы O1+ ниже (линия 2.x.x, вне v1.0, только по согласованию — ТЗ 12.6). Здесь перечислены лишь как напоминание, что это **сознательно отложенный объём**, а не забытый. 5. **Входящий релей и pluggable-антиспам** вынесены в опциональные фазы O1+ ниже (линия 2.x.x, вне v1.0, только по согласованию — ТЗ 12.6). Здесь перечислены лишь как напоминание, что это **сознательно отложенный объём**, а не забытый.
10. **2FA и несколько администраторов** — вне объёма v1.x (ТЗ этого не требует: один админ). Кандидаты на 2.x, если понадобятся. 6. **2FA и несколько администраторов** — вне объёма v1.x (ТЗ этого не требует: один админ). Кандидаты на 2.x, если понадобятся.
--- ---