Что выполняется при npm install: хуки, зависимости и границы ignore-scripts

Прослеживаем путь от установки к импорту подозрительного модуля и отделяем воспроизводимость установки от безопасности.

«Я только поставил зависимости, приложение не запускал». Для JavaScript-проекта это важное уточнение, но ещё не ответ на вопрос об исполнении кода.

npm поддерживает скрипты жизненного цикла. В зависимости от команды, версии и типа пакета установка может вызвать preinstall, install, postinstall, prepare и связанные этапы. Такие скрипты встречаются в обычной разработке: например, для подготовки сборки. Их наличие требует проверки назначения, а не автоматического обвинения в распространении вредоносного кода. [1]

Где заканчивается «скачивание файлов»

Рассмотрим учебный фрагмент корневого package.json. Для этой цепочки и задачи в конце статьи используем npm 11 с разрешёнными скриптами жизненного цикла:

1
2
3
4
5
6
7
{
  "scripts": {
    "start": "node server/app.js",
    "postinstall": "npm start",
    "prepare": "node tools/build.js"
  }
}

Здесь postinstall вызывает команду запуска приложения. Чтобы понять риск, нужно прочитать server/app.js, затем проследить его импорты. Название tools/build.js тоже не гарантирует, что файл только собирает проект.

Важен весь путь:

1
2
3
4
5
6
установка с разрешёнными lifecycle scripts
    → postinstall
    → npm start
    → server/app.js
    → импорт модуля
    → действие на верхнем уровне этого модуля

Любое звено может изменить результат: пакет не установился, команда завершилась с ошибкой, импорт не достигнут, сетевая ветвь не сработала. Статический разбор показывает возможную цепочку; журнал конкретного запуска помогает установить состоявшуюся.

Почему недостаточно корневого package.json

Проверка начинается с корневого файла, но продолжается в рабочих пакетах монорепозитория и зависимостях. Отдельного внимания требуют Git-зависимости и операции подготовки пакета: условия вызова prepare отличаются от простого запуска произвольного скрипта. Точную последовательность нужно сверять с документацией используемой версии npm. [1]

Также стоит прочитать .npmrc. Это конфигурация менеджера пакетов, а значит часть условий выполнения: откуда берутся зависимости и какие параметры применяются. Не копируйте в чужую среду свой пользовательский конфиг вместе с токенами доступа. [2]

Само получение проекта из репозитория ещё не доказывает компрометацию публичного реестра пакетов. В нашем учебном сценарии достаточно вредоносной логики в корневом проекте. Поэтому точнее говорить о запуске недоверенного проекта через установочные хуки, а не объявлять любой такой случай атакой на npm registry.

Что даёт --ignore-scripts

При установке этот параметр отключает выполнение скриптов из package.json. Но явная команда вроде npm start, npm test или npm run build запускает выбранный скрипт даже при действующем ignore-scripts; подавляются только сопутствующие pre/post-этапы. Это прямо описано в документации npm. [2]

Флаг одного вызова не становится постоянной настройкой: после npm ci --ignore-scripts отдельный npm start не наследует его. Если запрет не задан в конфигурации или окружении, вместе со start могут сработать и prestart/poststart.

Следовательно, сочетание «установил с --ignore-scripts, затем сразу запустил npm start» не сохраняет запрет на выполнение кода. Оно только переносит момент исполнения.

Для анализа известного снимка с согласованным lockfile в отдельной подготовленной среде возможна команда:

1
npm ci --ignore-scripts

Это не первый шаг проверки неизвестного архива. Сначала читают конфигурацию и оценивают происхождение зависимостей. Даже такая установка получает и обрабатывает внешние данные, поэтому её не следует представлять как безопасное действие на машине с рабочими секретами.

OWASP рекомендует ограничивать установочные скрипты и проверять чужой проект до их разрешения. [3]

Почему lockfile не выдаёт сертификат безопасности

npm ci требует lockfile, проверяет согласованность с package.json и не обновляет его по ходу установки. Но команда не запрещает lifecycle scripts сама по себе. [4]

Фиксация версии и контроль целостности помогают установить, какие именно байты получены. Они не доказывают, что эти байты доброкачественны. Зафиксированная вредоносная версия воспроизводится не хуже обычной.

То же относится к результату проверки известных уязвимостей. npm audit запрашивает сведения об известных проблемах зависимостей; отсутствие отчёта не является экспертизой произвольного JavaScript, скрытого в файлах проекта. [5]

Разрешение скриптов как отдельное решение

После первичного анализа может выясниться, что конкретной зависимости действительно требуется подготовительный этап. Тогда разрешение привязывают к известному пакету, версии и понятной команде, а не ко всему содержимому репозитория.

При редакции в ноябре 2026 года документация npm 12 уже описывает политику allowScripts: установочные скрипты зависимостей без соответствующего разрешения пропускаются. Это важное отличие от прежних выпусков, но оно не отменяет проверку корневого проекта. Его собственные хуки при обычном локальном npm install обрабатываются отдельно и не становятся безопасными из-за запрета скриптов зависимостей. [6][7]

В этой статье мы намеренно ограничиваемся npm. У Yarn и pnpm есть собственные настройки и меняющееся между версиями поведение. Переносить один флаг и считать результат одинаковым без проверки документации не стоит.

Задача: что изменится в трёх запусках?

Файлы для самостоятельного разбора доступны в синтетическом практикуме.

Для фрагмента выше рассматриваются действия:

1
2
3
А. npm ci
Б. npm ci --ignore-scripts
В. npm ci --ignore-scripts, затем npm start

В упражнении предполагаются npm 11, согласованный lockfile, разрешённые политикой окружения lifecycle scripts и отсутствие дополнительных ограничений.

Показать разбор

Разбор. В А разрешена цепочка lifecycle scripts, поэтому postinstall может запустить приложение. В Б эта автоматическая цепочка подавлена. В В отдельная команда npm start всё же вызывает приложение. Ни один ответ не устанавливает, что именно приложение сделало: для этого нужно продолжить чтение и сопоставить его с журналами.

Что стоит запомнить

Установка, сборка и тестирование — разные команды, но каждая может стать точкой исполнения. Проверять нужно не название действия, а программу, до которой оно доводит управление.

Источники

[1] npm: Scripts — lifecycle events и условия prepare.

[2] npm: Config — ignore-scripts и другие параметры.

[3] OWASP: NPM Security Cheat Sheet.

[4] npm: npm ci.

[5] npm: npm audit.

[6] npm 12: управление разрешениями установочных скриптов зависимостей.

[7] npm CLI: реализация install и отдельный вызов root lifecycle scripts.

Установочное поведение следует проверять для фактической версии npm. Статья не переносит возможности нынешних выпусков на событие января 2026 года.

Создано при помощи Hugo
Тема Stack, дизайн Jimmy