security: phase D pre-release review — pass; harden saslpasswd2 argv

Fable review of the full diff from the v1.0 audit (Phase 11, bd64e80) to
HEAD plus a complete pass over the docs/security.md checklist (former spec
7.6). No exploitable findings. One defence-in-depth fix: the application
login is passed to saslpasswd2 behind a -- end-of-options marker so a
login starting with - can never be parsed as a flag. Accepted risks
unchanged; plan § D closed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 13:32:52 +03:00
parent 2595b0b673
commit 429f9ad232
6 changed files with 45 additions and 34 deletions
+17 -25
View File
@@ -1,6 +1,7 @@
# План реализации: SelfPost
**Статус:** для линии v1.0/v1.x до тега релиза остаётся **один пункт** (ниже).
**Статус:** линия v1.0/v1.x до тега релиза **закрыта** — предрелизная ревизия
безопасности (§ D) выполнена 2026-08-06, релизный гейт (e2e + ревизия) открыт.
B.1–B.3 и C.4 закрыты — as-built в [architecture.md](architecture.md),
e2e/CI в [development.md](development.md), принятые риски в
[security.md](security.md). Комплексное рецензирование кодовой базы и план
@@ -11,29 +12,20 @@ e2e/CI в [development.md](development.md), принятые риски в
---
## D. Предрелизная ревизия безопасности
## D. Предрелизная ревизия безопасности — ВЫПОЛНЕНО (2026-08-06)
**Проверка на уязвимости моделью Fable** — после B.1B.3 и C.4, **до тега
релиза**. Не выполнено.
**Проверка моделью Fable** по дифу от аудита v1.0 (Фаза 11, `bd64e80` тега
`v1.0.0` в репозитории нет, это его фактический эквивалент) до HEAD, плюс
полный повторный проход по чек-листу безопасности (бывшее ТЗ 7.6, теперь
[security.md](security.md)). Приоритеты из плана покрыты: аутентификация и
сессии, валидация ввода, запись в конфиги/map-файлы, `os/exec`, права в
`/data`, секреты.
**Почему один проход по итоговому состоянию, а не по каждому пункту.** B.1C.4
меняли одну и ту же поверхность (сессии в SQLite, logrotate + `postfix reload`,
гейт `SELFPOST_HOSTNAME`, e2e-override с ослабленными настройками). Срезы по
отдельным коммитам не заменяют просмотр дифа от `v1.0.0` до HEAD.
**Объём.** Диф от тега `v1.0.0` до состояния перед следующим тегом — вместе с
Фазами 1214, которых в аудите v1.0 не было — плюс повторный проход по чек-листу
ТЗ 7.6 целиком, а не только по изменённым строкам. Приоритет: аутентификация и
сессии, валидация ввода, запись в конфиги и map-файлы (injection), `os/exec` без
shell, права на файлы в `/data`, обращение с секретами (пароли приложений,
`sasldb2`, архив бэкапа).
**Модель — Fable** (не Opus): код писал Opus, проверка собственной работы
систематически слабее независимой. Правило progress.md «безопасность/инфра →
Opus» — про написание; здесь ревизия. Форма: `/security-review` по изменениям
(скилл смотрит диф) + ручной проход по 7.6.
**Гейт.** Вместе с e2e (C.4, готов): до тега. Находка класса «эксплуатируется
снаружи» откладывает тег. Каждая находка закрывается явно: правка до тега либо
запись в [security.md](security.md) как принятый риск с обоснованием. «Посмотрели
и ладно» — не закрытие.
**Результат.** Эксплуатируемых уязвимостей (high/medium) не найдено. Одна
находка defence-in-depth закрыта правкой до тега: логин приложения передаётся
в `saslpasswd2` после `--`, чтобы значение, начинающееся с `-` (допустимо
whitelist'ом), не могло быть разобрано getopt как флаг
([internal/app/sasl.go](../internal/app/sasl.go)). Принятые риски в
[security.md](security.md) не пополнились — существующие записи (origin-check
fallback, отсутствие CSRF-токенов, send-log gap) покрывают всё найденное.
Сводка ревизии — в записи `Security` CHANGELOG `[Unreleased]`.