~/guides/npm-dependency-cooldown-explained

JSON

Dependency cooldown в npm: почему GitHub теперь ждёт 3 дня перед обновлением зависимостей

Разбор новой дефолтной защиты Dependabot — окна ожидания перед автообновлением npm-пакетов — и как встроить ту же логику в свой CI, даже если вы не используете Dependabot.

В июле 2026 года GitHub включил по умолчанию новое поведение для Dependabot version updates: перед тем как открыть PR на обновление npm-зависимости, Dependabot теперь ждёт минимум 3 дня с момента публикации новой версии пакета. Это называется dependency cooldown, и это прямой ответ на схему атак, которая раз за разом срабатывала весь 2026 год — от компрометации Mastra AI в июне до атаки на репозитории AsyncAPI в июле.

Почему именно скорость обновления — это уязвимость

Логика атак, которые мы разбирали в других гайдах (Shai-Hulud, Mini Shai-Hulud, Mastra AI), одинаковая: злоумышленник получает доступ к аккаунту мейнтейнера или к CI/CD пайплайну публикации, выпускает вредоносную версию, и она начинает расходиться по проектам за минуты — просто потому, что автообновления подхватывают новый релиз мгновенно, ещё до того, как кто-либо успел заметить неладное.

Атака на Mastra AI в июне 2026 наглядно это показала: свыше 140 пакетов в scope @mastra были переопубликованы за 19 минут. Любой проект с автообновлением без задержки, который сделал npm install в этом окне, получал вредоносный код.

Как это устроено в Dependabot

Настройка cooldown задаётся в .github/dependabot.yml через поле cooldown:

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "daily"
    cooldown:
      default-days: 3
      semver-major-days: 7
      semver-minor-days: 3
      semver-patch-days: 1

Обратите внимание на дифференциацию по типу версии: major-обновления получают более долгое окно (7 дней) по умолчанию логичнее — они и так требуют больше ручного тестирования, а patch-версии (обычно баг-фиксы) — более короткое (1 день). Security-обновления не задерживаются — если Dependabot видит, что новая версия исправляет известную уязвимость, PR открывается немедленно, cooldown к таким обновлениям не применяется.

Что делать, если вы не используете Dependabot

Renovate поддерживает похожую механику через поле minimumReleaseAge в renovate.json:

{
  "packageRules": [
    {
      "matchUpdateTypes": ["major"],
      "minimumReleaseAge": "7 days"
    },
    {
      "matchUpdateTypes": ["minor", "patch"],
      "minimumReleaseAge": "3 days"
    }
  ]
}

Если у вас ручной или собственный скрипт обновления зависимостей — сама идея переносится без труда: перед npm update или npm install <pkg>@latest проверяйте дату публикации версии через npm view <пакет> time --json, которая вернёт объект с датами всех релизов, и сравнивайте с текущей датой. Ровно эту проверку и делает наш npm Dependency Cooldown Calculator — вставляете список пакетов с датами релизов, получаете статус по каждому.

Cooldown — не панацея, а один слой защиты

Три вещи, которые cooldown не решает:

Таргетированную атаку на конкретный проект — если атакующий целится именно в вас, а не рассылает вредоносный релиз массово, скорость обновления вообще не при чём.

Install-скрипты у пакета, который уже прошёл cooldown — это отдельный вектор, который закрывает allowScripts (см. гайд про npm v12 и allowScripts), а не окно ожидания.

Компрометацию транзитивной зависимости, которая не обновлялась годами и уже сидит в вашем package-lock.json — cooldown работает только на новые обновления, а не задним числом.

Итог

Dependency cooldown — это простой и почти бесплатный слой защиты: он не требует переписывать код, только настройку в dependabot.yml или renovate.json. Дефолтные 3 дня GitHub — разумный компромисс между скоростью получения багфиксов и временем на то, чтобы вредоносный релиз успели заметить и снять с публикации. Для критичных продакшен-зависимостей есть смысл увеличить окно до 7-14 дней вручную — особенно для major-обновлений, которые и так требуют больше внимания при тестировании.