Перейти к содержанию

Граф зависимостей и границы доверия

Веб-проект использует чужой код не только в браузере. Пакет может исполняться при установке, читать исходники при сборке или влиять на готовый JavaScript. Поэтому перечень dependencies ещё не описывает границы доверия. В первом уроке составим карту стадий и объясним, какие ресурсы доступны каждому участнику.

Наша лаборатория называется «Контур выпуска». Она содержит маленькую статическую страницу и авторские JSON-модели вымышленных пакетов. Реальных зависимостей у исполняемой части нет. Пакеты lab-render и lab-native придуманы; их нельзя устанавливать из реестра по этому уроку. Примеры и сборка не выполнялись.

Пакет в разных стадиях

Представим статический каталог, который превращает данные уроков в HTML. Один пакет разбирает текст, другой ускоряет обработку изображения, третий попадает в браузерный результат. Все три могут находиться в одном дереве установки, но их возможности различаются. Риск зависит не только от количества строк пакета, но и от того, где и с какими полномочиями он исполняется.

В dependency-graph.json у узла есть имя, версия, источник и стадии install, build, browser. У ребра указано, кто запросил зависимость. Прямая зависимость объявлена проектом, транзитивная приходит через другую. Обновляя один пакет, мы можем получить изменение второго, о котором не думали при чтении короткого списка package.json. Поэтому карта должна отражать дерево выбранного состояния, а не только пожелание автора.

Для lab-native задан install-скрипт. Он исполнялся бы в среде установки, если политика разрешает такой запуск. Эта среда может иметь доступ к рабочему каталогу, сети и переменным процесса. Установка не является исключительно скачиванием данных. В лаборатории данный механизм описывается как вымышленное свойство пакета: никакой соответствующий скрипт не загружается и не запускается.

lab-render участвует в сборке. Даже если его install-скрипты запрещены, явный запуск сборщика может исполнить его код. Это другая граница. Для готового статического сайта пакет может не существовать как отдельный файл node_modules, однако повлиять на текст или JavaScript результата. Именно поэтому отсутствие пакета на рабочем хостинге не делает его незначимым для выпуска.

В реальном package.json devDependencies обозначают назначение зависимости при разработке, но не гарантируют отсутствие риска. Сборщик из этой группы читает исходники и создаёт публичный артефакт. Напротив, некоторые зависимости runtime могут быть устранены упаковкой или не попасть в выбранный результат. Наша карта фиксирует фактическую либо планируемую роль отдельно от исходной классификации.

Маленький проект и договор модели

Откройте project/package.json: здесь private:true, обычный локальный build-скрипт и нет внешних dependencies. Файл build.mjs читает собственный учебный JSON и создаёт одну HTML-страницу. Это полный небольшой пример, который можно изучить без установки сторонних пакетов. Он не представляет производственный генератор ProfessorWeb.

Отдельная папка fixtures содержит модель расширенного дерева. В ней указано fictional:true, адреса .example и пустые значения реальной integrity. Мы не выдаём эту модель за созданный npm lockfile. Она нужна, чтобы рассуждать о стадиях и полномочиях, не придумывая настоящие контрольные суммы чужих архивов. В следующем уроке различим модель и реальный договор lockfile.

Для каждой стадии запишем доступные ресурсы. Установка получает только необходимое чтение источников и доступ к реестру; сборка — выбранные исходники и каталог результата; публикация — утверждённый артефакт и ограниченные полномочия назначения. Это план архитектуры, а не выполненная изоляция. Конкретная CI-система потребует собственной конфигурации и проверки.

Ожидаемый результат — три стадии, несколько вымышленных узлов и объяснение каждого ребра. Карта отвечает, где искать последствия изменения пакета. Она не утверждает безопасность по количеству зависимостей и не считает чистый audit полным доказательством доверия. Последовательность серии будет добавлять разные основания: версии, разрешения скриптов, ревью, состав артефакта и происхождение выпуска.

Прочитайте карту в обратную сторону от HTML. Какие компоненты могли изменить готовую страницу? Какие были доступны только при установке? Какие полномочия не должны присутствовать до публикации? Такой проход помогает увидеть, почему один общий контейнер со всеми секретами создаёт большую область воздействия. При этом запись стадий в JSON не обеспечивает реальную границу: она лишь задаёт требование к будущей среде.

Для собственной библиотеки начните с одного маршрута выпуска и нескольких ключевых компонентов. Не нужно сразу описывать весь реестр npm. Карта полезна, когда вы можете объяснить происхождение и назначение каждого узла. Если роль транзитивного пакета неизвестна, сохраните unknown и задачу на разбор, вместо произвольного вывода, что маленький пакет автоматически безопасен.

Чтение карты доступа

В сохранённом графе начните с ребра root → lab-render. Оно сообщает, что компонент выбран напрямую. Следующее ребро ведёт к parser и объясняет транзитивное происхождение. Затем посмотрите стадии узлов. Parser может вообще не иметь автоматического install-скрипта, но всё равно исполняться при преобразовании контента. Именно поэтому граф зависимостей и карта стадии нужны одновременно.

Сведение Значение для доверия
Запросивший пакет Показывает, каким изменением верхнего дерева пришёл компонент и где обсуждать необходимость его присутствия.
Выбранная версия Определяет предмет содержательного ревью, не распространяя решение на любой будущий код с таким именем.
Стадия выполнения Помогает определить возможный доступ к исходникам, выходу и сети, независимо от группы dependencies.
Статус модели Сохраняет отличие авторского рассуждения от фактически установленного окружения или полученного артефакта.

Откройте карту, не читая статью, и попробуйте объяснить, почему lab-native не должен иметь права публикации. Если ответ получается только из вашего личного знания, добавьте соответствующее ограничение в договор стадии. Но не меняйте статус на isolated: описание требований не является доказательством настройки реального runner.

Для варианта добавьте собственный пакет, который только читается как данные. Его роль будет отличаться от исполняемой библиотеки. Затем представьте, что проект начинает загружать его содержимое как JavaScript. Граница доверия изменилась, хотя имя источника осталось тем же. Так карта помогает замечать продуктовые изменения, которые не видны в коротком отчёте версий.

Завершённая карта пригодна для разговора с владельцем выпуска: у каждого узла есть происхождение, стадия и объяснение ресурсов. Она не предсказывает все способы компрометации. Для нового механизма добавляется самостоятельное отношение, а неизвестное назначение получает конкретную задачу разбора. Такой небольшой сопровождаемый документ полезнее огромного списка пакетов без связи с операциями проекта.

Полный основной артефакт dependency-graph.json выбранного состояния:

{
  "fictional": true,
  "nodes": [
    {
      "id": "root",
      "name": "contour-release",
      "version": "1.0.0",
      "source": "own_project",
      "stages": [
        "build"
      ]
    },
    {
      "id": "render-v1",
      "name": "lab-render",
      "version": "1.4.2",
      "source": "fictional_registry",
      "stages": [
        "build"
      ]
    },
    {
      "id": "parser-v1",
      "name": "lab-parser",
      "version": "0.8.0",
      "source": "fictional_registry",
      "stages": [
        "build"
      ]
    },
    {
      "id": "native-v1",
      "name": "lab-native",
      "version": "1.2.3",
      "source": "fictional_registry",
      "stages": [
        "install",
        "build"
      ]
    }
  ],
  "edges": [
    {
      "from": "root",
      "to": "render-v1",
      "kind": "direct"
    },
    {
      "from": "render-v1",
      "to": "parser-v1",
      "kind": "transitive"
    },
    {
      "from": "root",
      "to": "native-v1",
      "kind": "direct"
    }
  ],
  "execution": "not_run"
}

Все исходники серии: папка lesson-01 содержит полный самостоятельный снимок, собственный проект и авторские модели. Исполнение примеров, npm, установки, аудит, тесты, CI, настройки и публикация не выполнялись.

Первичные источники: manifest, lockfile.