9.6 KiB
9.6 KiB
Регистр дефектов и уязвимостей платформы
Заведён по решению владельца 04.08 («отдельно ведётся колонка багов и уязвимостей — тут опасно всё»). Правила: каждая находка любой сессии/приёмки/аудита — строкой сюда ДО закрытия; ID стабилен навсегда; закрытие — только с коммитом фикса и тестом, пинящим свойство (урок PD-1: свойство без пинящего теста считается НЕ закрытым). Класс:
vuln— эксплуатируемо или ослабляет защиту ·bug— неверное поведение ·hardening— защита в глубину / латентное. Статус:open·fixed(<commit>)·accepted-risk(<кем, когда>).
| ID | Класс | Серьёзность | Где | Суть | Статус | Источник |
|---|---|---|---|---|---|---|
| PD-1 | hardening | minor | internal/pgstore/pg_test.go:89 |
Свойство «в БД только SHA-256, не токен» НЕ запинено тестом: посадка «Digest возвращает плейнтекст» выживает — тест сверяет хранимое через тот же auth.Digest (self-consistent). Нужен тест с НЕЗАВИСИМО вычисленным хешом либо ассерт «плейнтекст в БД не находится» |
open | приёмка P0 (посадка №1) |
| PD-2 | vuln | major, ЖИВАЯ (не латентная) | cmd/tmplatformd/main.go:73-82 |
Нет ReadTimeout ⇒ соединения пиннятся уже СЕГОДНЯ, без единой body-принимающей ручки: net/http дренирует непрочитанное тело <256 КБ ВНУТРИ chunkWriter.writeHeader до отправки заголовка ответа (net/http/server.go:1389-1435), и этот чтение-шаг наследует отсутствующий дедлайн. Репродуцировано оркестратором на собранном бинаре: 50 полу-кормленных POST на охраняемый /v0/* → сервер отработал и залогировал 50×401 ms:0, клиенты получили НОЛЬ байт, fd 7→57 и держались, пока не закрыл КЛИЕНТ (агент-скептик независимо пинил 500 соединений). Ограничителя соединений и документированного edge-прокси в зоне нет. Фикс — одна строка (ReadTimeout; для будущего SSE — per-conn дедлайны через ResponseController). Вторая половина (MaxBytesReader) сегодня не эксплуатируема (ни один хендлер не читает body) — гейт P1: закрыть ДО первого POST-хендлера |
open | приёмка P0 (security-линза + скептик + собственная репродукция) |
| PD-3 | bug | minor | internal/httpapi/middleware.go:57 |
Recover логирует сырой r.URL.Path на ERROR — id книг/прогонов утекают в лог, против собственной дисциплины AccessLog (route-pattern, не путь) |
open | приёмка P0 (security-линза) |
| PD-4 | hardening | minor | internal/pgstore/sessions.go:41 |
WHERE у Touch слабее, чем у Lookup (нет idle_expires_at > now): прямой вызов воскресил бы idle-истёкшую сессию. Через Require недостижимо (Touch только после успешного Lookup) — одна строка защиты в глубину |
open | приёмка P0 (security-линза) |
| PD-5 | bug | minor | internal/auth/middleware.go:36,45 |
Ошибки стора невидимы: сбойный Lookup → 401 без единой строки лога (аутентификационный DB-outage выглядит как шторм 401), Touch глотается _ =. На проводе различать нельзя (оракул) — но лог обязан различать |
open | приёмка P0 (security+blind линзы) |
| PD-6 | hardening | info | internal/auth/csrf.go:51 |
GET освобождён от CSRF (верно), но SSE-хендшейк — GET с амбиентной кукой: origin-чек хендшейка потока (STACK §5) не покрыт ничем. Закрыть при постройке SSE (P1) | open | приёмка P0 (security-линза) |
| PD-7 | bug | info | internal/pgstore/sessions.go:78 |
DeleteExpiredSessions никем не вызывается — свип запланировать в P1 (периодическая джоба воркера) |
open | приёмка P0 |
| PD-8 | hardening | info | internal/auth/session.go:18 |
Писателя куки ещё нет; __Host- требует Secure ⇒ локальный dev по HTTP куку не поставит. Решить формой в P1 (dev-профиль), префикс не ослаблять в проде |
open | приёмка P0 |
| PD-9 | bug | minor | cmd/tmplatformd/main.go:81 |
BaseContext возвращает signal-контекст ⇒ SIGTERM мгновенно рубит контексты ВСЕХ in-flight запросов, и 15-секундный дренаж Shutdown мёртв для ctx-aware хендлеров. Fix: BaseContext без signal-ctx; сигнал ведёт только Shutdown |
open | приёмка P0 (faults-линза) |
| PD-10 | bug | minor | internal/ingest/decoder.go:42,61,67 |
Три ужесточения декодера: (а) hello с пустым engine_run_id принимается — а это половина ключа идемпотентности; (б) seq самого hello не пинится к 1 — потеря пре-хендшейковых строк недетектируема; (в) mid-stream hello (любой версии, вкл. мажор 9.9) уходит в Sink как обычное событие — version-гейт держит только строку 1 |
open | приёмка P0 (faults-линза) |
| PD-11 | bug | minor | internal/pgstore/migrations/00002_readmodel.sql:137,177 |
Неиндексированные FK-каскады: notes.chapter_id и bank_decisions.term_id — каскадное удаление сканирует таблицы |
open | приёмка P0 (faults-линза) |
| PD-12 | bug | info | internal/ingest/supervisor.go:82-84 |
Сбой Sink в начале прогона ⇒ платформа дренирует ВЕСЬ оставшийся поток в io.Discard часами: ceiling/bank_stop-события выбрасываются, никто не оповещён. Нужна политика «БД платформы упала посреди прогона» (ретраи синка / деградация с алармом) — дизайн-вопрос P1 |
open | приёмка P0 (faults-линза) |
| PD-13 | bug | info | internal/ingest/supervisor.go:64 |
Краш платформы осиротляет процесс движка: ни process-group, ни pidfile, ни пути реаттача (поток невосстановим, повторный спавн упрётся в EXCLUSIVE-лок). Дизайн супервизии P1 | open | приёмка P0 (faults-линза) |
| PD-14 | hardening | info | internal/httpapi/server.go:79 |
readyz: ping без собственного таймаута (WriteTimeout нет намеренно — SSE), эндпоинт неаутентифицирован и без rate-limit — задушить дешёво; таймаут на ping + прикрыть на ops-слое |
open | приёмка P0 |
| PD-15 | bug | info | internal/ingest/resync.go:32 |
Деньги в ре-синке — float64, а usage_windows хранит micro-USD именно против дрейфа: дрейф входит шагом раньше (JSON-парс + суммирование дельт). Принять осознанно или считать в целых |
open | приёмка P0 (faults-линза) |
| PD-16 | bug | minor | internal/httpapi/server.go:81 |
readyz глотает ошибку ping вопреки собственному комменту «the reason stays in the log» — лога нет |
open | приёмка P0 (blind-линза) |
| PD-17 | bug | minor | Makefile:41-42 |
Баннер «did NOT run (no database)» печатается и при ПРОГНАННЫХ БД-тестах (безусловный); батарея гоняет сьют дважды ради имён скипов (второй прогон без -race) | open | приёмка P0 (blind-линза) |
| PD-18 | bug | info | internal/pgstore/migrations/00002_readmodel.sql:9,139 |
Коммент шапки «engine vocabulary never crosses this seam» противоречит notes.reason (движковая причина хранится, не проецируется); коммент переписать честно |
open | приёмка P0 (canon-линза) |
| PD-19 | bug | info | internal/ingest/resync.go:44 |
WorstFlagReason задокументирован «stored», а колонки в chapters нет — доккоммент или схема, одно из двух |
open | приёмка P0 (canon-линза) |