Проверка целостности зависимостей

Сборка JavaScript-проектов в экосистеме Node.js опирается на огромное количество внешних пакетов, что делает цепочку зависимостей сложной и уязвимой к изменениям на любом уровне. Даже незначительное расхождение версий или подмена содержимого пакета способно изменить поведение итогового бандла. В контексте сборщика Rollup это особенно критично, поскольку его модель основана на статическом анализе модулей и строгом выводе графа зависимостей.

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


Граф зависимостей и точка возникновения риска

Rollup строит граф зависимостей начиная с entry point и последовательно разрешает импорты:

  • анализирует import и export
  • резолвит пути через алгоритм Node resolution
  • подхватывает пакеты из node_modules
  • применяет плагины трансформации

Риск нарушения целостности возникает в момент перехода от логического имени модуля к физическому файлу на диске. Любая подмена в node_modules, кешах или в процессе установки способна изменить результат анализа.

Особенно уязвимыми являются:

  • транзитивные зависимости (dependencies of dependencies)
  • пакеты с постинсталляционными скриптами
  • модули с динамическим экспортом
  • зависимости с разными форматами (ESM/CJS)

Контроль на уровне менеджера пакетов

Основная линия защиты реализуется не внутри Rollup, а на уровне package manager’ов.

npm integrity field

В package-lock.json и в записи пакета используется поле integrity, содержащее криптографический хеш содержимого:

  • алгоритм: SHA-512 (чаще всего)
  • формат: sha512-...
  • проверка: при установке сравнивается скачанный tarball с эталонным хешем

Если содержимое отличается хотя бы на байт, установка прерывается.

yarn.lock и pnpm-lock.yaml

  • Yarn фиксирует точные версии и контрольные суммы
  • pnpm дополнительно использует content-addressable storage, где содержимое физически идентифицируется через хеш

Это снижает вероятность появления “разных node_modules” при одинаковых lock-файлах.


Проверка целостности на уровне npm registry

При скачивании пакета из реестра:

  1. клиент получает tarball
  2. вычисляет хеш содержимого
  3. сверяет с integrity из метаданных
  4. отклоняет установку при несовпадении

Этот механизм защищает от:

  • подмены пакета в сети
  • повреждения архива
  • неконсистентной публикации

Однако он не защищает от изменения уже установленного пакета в node_modules.


Уязвимости локального окружения

После установки контроль целостности частично теряется:

  • пользователь или скрипт может изменить файл в node_modules
  • CI может кэшировать поврежденные зависимости
  • монорепозитории могут переиспользовать общий кеш

Rollup в этом случае работает с тем, что уже лежит на диске, не выполняя криптографическую проверку.


Роль Rollup в проверке структуры модулей

Хотя Rollup не выполняет хеш-контроль, он обеспечивает косвенную целостность через строгий анализ модулей:

1. Статический анализ импортов

Rollup строит граф только на основе статических выражений:

import x from 'pkg';

Динамические конструкции ограничены или требуют плагинов.

2. Проверка формата модулей

Rollup различает:

  • ESM (import/export)
  • CJS (require/module.exports)
  • AMD/UMD через плагины

Несовместимость форматов может привести к ошибкам сборки, что фактически является формой структурной проверки целостности.

3. External зависимости

При конфигурации external Rollup исключает пакеты из бандла:

  • важно для контроля версий runtime-зависимостей
  • снижает риск дублирования кода разных версий одной библиотеки

package.json exports как механизм защиты структуры

Современные пакеты используют поле exports, которое ограничивает доступ к внутренним файлам:

{
  "exports": {
    ".": "./dist/index.js",
    "./utils": "./dist/utils.js"
  }
}

Это создает контракт целостности API:

  • запрещает доступ к внутренним файлам
  • фиксирует публичную поверхность пакета
  • снижает риск использования нестабильных внутренних модулей

Rollup учитывает exports при резолвинге, что делает граф зависимостей более предсказуемым.


Влияние кеширования на целостность

Rollup использует кеш для ускорения сборки:

  • кешируется граф модулей
  • сохраняются результаты трансформаций плагинов

Риски:

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

Поэтому важным аспектом является:

  • корректная настройка cache
  • очистка кеша в CI
  • контроль версий зависимостей

Проверка целостности через lockfile в CI

В CI-средах часто применяются стратегии:

  • установка зависимостей только через lockfile (npm ci)
  • запрет изменения package-lock.json
  • сравнение хешей между окружениями

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


Влияние транзитивных зависимостей

Наибольшая сложность возникает не в прямых зависимостях, а в цепочке вложенных пакетов:

  • A зависит от B
  • B зависит от C
  • C может измениться без изменения A

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

Поэтому целостность определяется не только хешами, но и детерминированностью дерева зависимостей.


Проверка целостности в пользовательских плагинах Rollup

В Rollup можно внедрить дополнительную проверку через плагины:

  • проверка SHA256 файлов перед трансформацией
  • валидация версии пакета
  • контроль содержимого импортируемых модулей

Пример логики:

  • перехват load или resolveId
  • чтение файла
  • вычисление хеша
  • сравнение с эталонным значением

Это позволяет реализовать строгую модель trust-boundary внутри сборки.


Интеграция с системой контроля изменений

В крупных проектах проверка целостности расширяется до уровня:

  • git hooks (preinstall, prebuild)
  • CI validation dependency graph
  • SCA (Software Composition Analysis)
  • audit инструментов npm/yarn

Rollup в этой цепочке выступает финальной стадией, где уже собранный граф проверяется косвенно через успешность анализа и сборки.


Детерминированность как форма целостности

Отдельный аспект — воспроизводимость сборки:

  • одинаковый вход → одинаковый bundle
  • фиксированные версии зависимостей
  • отсутствие случайных источников данных

Rollup усиливает детерминированность за счет:

  • фиксированного графа модулей
  • предсказуемого порядка включения
  • стабильного tree-shaking

Любое нарушение целостности проявляется как:

  • изменение AST
  • изменение экспортов
  • появление новых модулей в графе

Косвенные признаки нарушения целостности в сборке

Даже без криптографических проверок можно обнаружить аномалии:

  • неожиданный рост размера bundle
  • появление новых внешних зависимостей
  • изменение структуры chunks
  • изменение порядка исполнения модулей

Rollup в таких случаях выступает диагностическим инструментом, выявляющим нарушение контрактов зависимостей на уровне графа.