# Аудит uNews 0.2.1 — 18 июля 2026

## Итог

Проект работал, но до аудита имел несколько отказов с высоким риском: повреждённое состояние могло быть принято за пустое, некорректная новость теряла версию при сортировке, а сетевой текст ошибки Telegram потенциально мог раскрыть URL с токеном. Эти дефекты исправлены.

## Исправленные проблемы

| Приоритет | Проблема | Исправление |
|---|---|---|
| Критический | Невалидный `published.json` мог незаметно превратиться в пустое состояние и вызвать дубли | Загрузка теперь останавливается при любой ошибке JSON или структуры |
| Высокий | Плохой патчноут терял front matter, поэтому следующие версии проекта могли пройти вперёд | Метаданные сохраняются в ошибочной записи; блокировка работает по версии проекта |
| Высокий | `queued_at` был заявлен обязательным, но код допускал резервное время | Поле обязательно; разрешён только точный UTC-формат `YYYY-MM-DDTHH:mm:ssZ` |
| Высокий | Имя изображения допускало путь за пределы `news/` | Разрешены только безопасные имена файлов изображений без каталогов и `..` |
| Высокий | Сетевая ошибка Telegram могла содержать секретный URL | Ошибки сетевого клиента санитизированы; токен не включается в сообщение |
| Средний | Telegram-ответ без `message_id` мог быть принят за публикацию | Состояние меняется только при наличии идентификатора сообщения |
| Средний | GitHub и Telegram логика дублировалась в координаторах | Выделены клиенты и общие модули front matter, очереди и state |
| Средний | Диагностический workflow проверял только репозиторий `500` | Он теперь запускает общий dry-run всех публичных проектов |
| Средний | Не было CI на изменения кода и pull request | Добавлен workflow `Quality checks` и обязательный тест перед публикацией |
| Низкий | Сайт полностью терял статус при недоступности GitHub API | Добавлен независимый fallback на `data/health.json` |

## Проверки

- синтаксическая проверка всех JavaScript-файлов;
- fixtures политики: обязательные поля, русский текст uSugar, секреты, повреждённый текст, безопасные изображения;
- fixtures очереди: FIFO, порядок версий, блокировка проекта, cooldown и строгий state;
- аутентифицированный `publish:all:check` в GitHub Actions обнаружил 35 публичных репозиториев, одну готовую новость и ноль ошибок;
- приватные репозитории не перечисляются и не сканируются.

## Секреты и срок действия

GitHub Actions скрывает значения и не сообщает календарный срок repository secrets. Telegram bot token также не имеет встроенной даты истечения: он действует до отзыва владельцем. Поэтому перед каждой реальной публикацией workflow выполняет `getMe` и `getChat`. На момент контрольной проверки бот и канал отвечали успешно. Автоматический `GITHUB_TOKEN` создаётся GitHub заново для каждого запуска и ограничен разрешениями workflow.

## Остаточный риск

У Telegram API и Git нет общей транзакции. Если процесс будет принудительно уничтожен в узком промежутке после принятия сообщения Telegram и до атомарной записи `published.json`, теоретически возможен дубль. Обычные ошибки следующей новости этот сценарий больше не создают, потому что за запуск обрабатывается один пост и состояние записывается сразу.

## Восстановление

- `stable/manual-publishing-v0.1.0` — ручная стабильная версия до автоматизации;
- `release/v0.2.1-audit-hardening` — стабильная точка после этого аудита;
- публикация по расписанию запускается только из `main`, release-ветка остаётся пассивной.
