Перейти к основному содержимому

ADR 0140 — Повторяющийся timeout pnpm audit — принятый риск; находка по-прежнему отклоняет

Контекст​

2026-09-04 endpoint advisories npm (POST registry.npmjs.org/-/npm/v1/security/advisories/bulk) часами работал с перебоями. Job Auditoria de dependências отклонил четыре разных PR (#464, #466, #396, #468), всегда с одной и той же сигнатурой:

[WARN] POST .../security/advisories/bulk error (23). Will retry in 10 seconds. 2 retries left.
[WARN] POST .../security/advisories/bulk error (503). Will retry in 1 minute. 1 retries left.
TimeoutError: The operation was aborted due to timeout

Дело было не в дереве зависимостей, не в runner и не в какой-либо из PR. Это было воспроизведено вне CI прямым обращением к endpoint с другой машины: то же отсутствие ответа за 20 с, тогда как обычный GET к тому же registry отвечал за 130 мс. pnpm audit уже повторяет три раза внутри (две строки retry выше); сервис просто не отвечал.

Цена была не только во времени. Она была в поведении, которому учит ситуация: каждый такой сбой решается перезапуском job, пока он не пройдёт, и тот, кто научился перезапускать до прохождения, перезапускает и тогда, когда красное настоящее. Gate, который кричит без причины, не остаётся нейтральным — он подрывает доверие к gates, которые кричат по делу.

Решение​

Решение владельца продукта: три попытки без ответа от registry — ПРИНЯТЫЙ РИСК. Job остаётся зелёным, с громким предупреждением в сводке.

Чего решение не ослабляет, и что делает его защитимым:

ситуациявердиктповтор
pnpm audit завершается с 0проходит—
отчёт об уязвимостиотклоняетникакого — ответ уже пришёл, и он "нет"
запрос не завершился (timeout/5xx/DNS/socket)infraдо 3
3 infra подрядпроходит, объявлено—
нераспознанный выводотклоняетникакого

Приоритет — суть этого. scripts/ci/auditoria-de-dependencias.ts классифицирует вывод, и маркеры НАХОДКИ проверяются до сетевых: отчёт, который случайно упоминает timeout — пакет с таким именем, CVE о timeout, — не может быть прощён как недоступность. Есть тест ровно на этот случай.

Неизвестный сбой отклоняет (fail closed). Если вывод не совпадает ни с одной из двух сигнатур, вердикт — achado. Единственное, что хуже gate, который отклоняет слишком много, — это gate, одобряющий то, чего он не понял.

Предупреждение говорит, чего выполнение НЕ утверждает. Когда риск принят, сводка job фиксирует, что дерево не было проверено — никогда, что оно чисто. Зелёное из-за недоступности и зелёное из-за чистого аудита — разные состояния, и job не путает их в тексте.

Логика живёт в тестируемом скрипте, а не в bash внутри YAML: классификация — чистая функция с 11 случаями, включая РЕАЛЬНЫЕ выводы прогона 33838507158 (того, что мотивировал всё это), а не перефразировки.

Последствия​

Существует реальное окно риска, и оно объявлено. При длительной недоступности npm PR входят без аудита зависимостей. Это зафиксировано в cadeia-de-suprimentos-do-ci.md, в разделе "What is still trusted on faith", рядом с другими принятыми доверительными допущениями — не спрятано в комментарии workflow.

Альтернатива была измерена, прежде чем её отвергли. Сохранение красного из-за падения третьей стороны стоило в тот день четырёх застрявших PR и нескольких повторных запусков по ~5 мин каждый, без того чтобы хоть один из них что-то обнаружил о коде. Что покупается этой жёсткостью — видимость строгости; что оплачивается — привычка игнорировать красное.

Меняется только сторона pnpm. mix hex.audit и mix deps.audit (Elixir) остаются как были — инцидент был с endpoint npm, а расширение исключения на тех, кто о нём не просил, превратило бы точечное решение в общую политику.

Что остаётся без ответа: нет оповещения, когда риск принимается часто. Три timeout в одиночной PR — шум третьей стороны; три timeout в каждой PR за неделю — фактически отключённый gate, и сегодня ничто не различает эти два случая, кроме того, что кто-то заметит. Остаётся объявленным пробелом, здесь не решённым.