Управление цепочкой поставок
Современные проекты на JavaScript получают код из множества источников (JSR, npm, локальные рабочие пространства). Грамотное управление цепочкой поставок помогает достичь четырёх целей:
- Детерминированность: все (включая CI) используют один и тот же код.
- Безопасность: неожиданные изменения или компрометации в исходных репозиториях обнаруживаются на ранней стадии.
- Скорость: вы можете обновлять зависимости тогда, когда сами решите.
- Устойчивость: сборки продолжают работать без подключения к сети или при сбоях реестров.
Эта страница дополняет рекомендации по файлам блокировки и вендорингу из руководства по управлению зависимостями.
Основные практики
- Фиксируйте версии осознанно
- Для приложений предпочтительны точные версии (например,
jsr:@luca/cases@1.2.3). - Для библиотек диапазон с кареткой (
^1.2.3) позволяет пользователям получать обратно совместимые исправления. - Избегайте неограниченных (
*) или слишком широких диапазонов в производственных приложениях.
- Для приложений предпочтительны точные версии (например,
- Добавьте файл
deno.lockв систему контроля версий. - Включите режим замороженного файла блокировки в CI / production (
--frozenили"lock": { "frozen": true }), чтобы новые, ранее не встречавшиеся зависимости приводили к сбою сборки, а не появлялись незаметно. - Используйте вендоринг, если нужны герметичные сборки или сборки без подключения к сети (
"vendor": true), либо если требуется локально исправить сторонний код. Вендоринг не отменяет необходимости в файле блокировки — он дополняет его. - Используйте спецификаторы
jsr:иnpm:с записями import map (imports), чтобы централизованно управлять версиями. - Периодически снимайте фиксацию и осознанно обновляйте зависимости (например, еженедельно или в каждом спринте), а не обновляйте их от случая к случаю во время работы над функциями.
- Установите минимальный возраст зависимости, чтобы только что опубликованные версии не попадали в установку до того, как экосистема успеет выявить скомпрометированный релиз.
- Включите политику доверия к публикации, чтобы версии, опубликованные менее надёжным способом, чем версия, уже зафиксированная вами, отклонялись, а не принимались незаметно.
Минимальный возраст зависимости
Deno отказывается устанавливать версии пакетов, возраст которых меньше заданного. Это простой способ широко защититься от атак на цепочку поставок npm: обнаружение и удаление вредоносных версий обычно занимает несколько дней, поэтому задержка установки на аналогичный срок позволяет отсеять большинство таких версий.
Начиная с Deno 2.9 эта настройка по умолчанию включена и равна 24 часам: версии, опубликованные за последние сутки, пропускаются даже без конфигурации. Приведённые ниже параметры изменяют это окно или отключают его (задайте возраст 0).
Один и тот же параметр можно настроить в нескольких местах; выберите подходящее для проекта:
-
deno.json, действует на весь проект:deno.json{ "minimumDependencyAge": "P3D" } -
Флаг CLI, применяется для разового использования, например при однократной установке или на шаге CI:
›_deno install --minimum-dependency-age=P3D -
.npmrc(Deno 2.8+), соответствует принятому в npm соглашению и удобен, если один и тот же.npmrcиспользуется инструментами npm и Deno. Параметр npm принимает только целое количество дней:.npmrcmin-release-age=3
deno.json и --minimum-dependency-age принимают длительность в формате ISO-8601, например P3D (3 дня) или PT72H (72 часа), целое число (интерпретируется как количество минут), абсолютную дату отсечения (2025-09-16) или временную метку RFC3339 либо 0 для отключения. Поле также поддерживает объектную форму, позволяющую исключать отдельные пакеты; полный формат см. в справочнике minimumDependencyAge, а другие параметры реестра npm, которые считывает Deno, описаны в разделе конфигурации .npmrc.
Политика доверия к публикации
Если минимальный возраст зависимости задерживает появление новых версий, политика доверия к публикации проверяет, как была опубликована версия, и не позволяет незаметно принимать версии, опубликованные менее надёжным способом, чем уже зафиксированные вами.
Полные метаданные пакета npm содержат сведения о способе публикации каждой версии. Deno считывает из них три сигнала:
- Доверенная публикация (OIDC) — версия опубликована из CI посредством механизма доверенной публикации npm через OIDC, без долгоживущего токена.
-
Аттестация происхождения — версия содержит подписанную аттестацию происхождения SLSA, связывающую её с исходным коммитом и сборкой (публикуется с помощью
--provenance). - Поэтапная публикация — сопровождающий одобрил версию с помощью действующего запроса 2FA до того, как она стала доступна для установки.
Deno объединяет эти сигналы в единый уровень доверия — от наименьшего к наибольшему:
| Уровень доверия | Сигнал |
|---|---|
| Обычный | Сигналов доверия к публикации нет |
| Происхождение | Только аттестация происхождения |
| Доверенная публикация | Только доверенная публикация через OIDC |
| Доверенная публикация + происхождение | Оба перечисленных выше сигнала |
| Поэтапная публикация | Публикация поэтапно, одобренная человеком (имеет наивысший уровень) |
Поэтапная публикация имеет наивысший уровень, поскольку её одобрил человек с помощью действующего запроса 2FA. Такой порядок соответствует trustPolicy: no-downgrade в pnpm.
Причина — безопасность цепочки поставок: если токен сопровождающего украдут, злоумышленник сможет опубликовать новую версию менее надёжным способом, чем версии, которым вы доверяли. Политика no-downgrade превращает такое незаметное снижение уровня доверия в явную ошибку.
Включите её в .npmrc:
trust-policy=no-downgrade
Как это работает:
- Deno записывает разрешённый уровень доверия для каждой записи npm в
deno.lock(полеtrust, которое не указывается для обычной версии). Записанный уровень становится базовым для следующего разрешения. - Когда
no-downgradeвключён, Deno отказывается разрешать любые версии-кандидаты, уровень доверия которых ниже зафиксированного базового уровня для этого пакета, и загружает полный packument, чтобы получить доступ к сигналам. - Базовый уровень — это наивысший уровень доверия, записанный для пакета в файле блокировки, поэтому защита усиливается по мере фиксации версий с более высоким уровнем доверия. При первой фиксации пакета сравнивать ещё не с чем.
- По умолчанию политика отключена. На существующие файлы блокировки она не влияет, пока вы её не включите; любое значение, кроме
no-downgrade, оставляет её отключённой.
Типичный сценарий CI
В Deno 2.8+ одна команда deno ci объединяет рекомендуемые шаги установки в CI (замороженный файл блокировки + скрипты жизненного цикла):
deno ci
Для более старых версий Deno или если нужно выполнить шаги вручную:
# Install (resolve) dependencies exactly as locked; fail if drift or new deps
deno install --frozen --entrypoint main.ts
# (optional) Run with only cached modules to guarantee no network access
deno run --cached-only main.ts
Если вы используете пакеты npm (указано package.json), добавьте deno install (или deno ci) в CI перед запуском тестов, чтобы каталог node_modules создавался детерминированно.
Осознанное обновление зависимостей
Когда вы решите выполнить обновление:
- Временно разрешите запись в файл блокировки: добавьте
--frozen=falseили установите"lock": { "frozen": false }. - Измените версии (отредактируйте
deno.json, используйтеdeno add <specifier>@<newVersion>или удалите с помощьюdeno remove). - Повторно запустите
deno install --entrypoint main.ts(при необходимости--reload), чтобы обновить разрешённые версии и хеши целостности. - Проверьте diff для
deno.lock(иvendor/, если используется) в pull request. - Снова включите режим замороженного файла блокировки.
Устранение неполадок с замороженным файлом блокировки
Вы можете столкнуться с такими ошибками:
error: The lockfile is frozen. Cannot add new entry for "jsr:@scope/pkg@1.3.0".
или:
error: Module not found in frozen lockfile: https://example.com/dependency/mod.ts
Распространённые причины и решения:
| Симптом | Причина | Решение |
|---|---|---|
| Нужно повысить версию, но команда завершается с ошибкой из-за замороженного файла | Файл блокировки находится в замороженном режиме | Повторно запустите команду с --frozen=false (однократно) или временно установите "lock": { "frozen": false }, затем обновите зависимости и снова включите замороженный режим |
| После редактирования кода появляется новая транзитивная зависимость | Теперь код импортирует то, чего нет в файле блокировки | Снимите заморозку (--frozen=false) и запустите deno install --entrypoint <entry>.ts, чтобы записать зависимость |
| Импорты удалены, но в файле блокировки остались старые записи | Файл блокировки дополняется; записи сохраняются | (Необязательно) пересоздайте файл: отложите deno.lock в сторону (mv deno.lock deno.lock.old), запустите установку, чтобы создать его заново, сравните изменения и зафиксируйте результат |
| Повреждение файла блокировки / конфликт слияния | Ручное редактирование или конфликт привели к несогласованному JSON | Удалите конфликтующие разделы и повторно запустите установку либо пересоздайте файл целиком |
| Используются вендорированные зависимости, но файл блокировки сообщает об ошибке | Каталог вендорированных зависимостей не синхронизирован с файлом блокировки | Повторно запустите deno install --entrypoint <entry> (без заморозки), чтобы синхронизировать оба объекта, и зафиксируйте результат |
Контрольный список безопасного пересоздания
Пересоздавайте весь файл deno.lock только при необходимости (повреждение, массовая очистка). Если это необходимо:
- Создайте резервную копию:
cp deno.lock deno.lock.bak. - Удалите файл:
rm deno.lock. - (При использовании вендоринга) удалите или переместите каталог
vendor/. - Запустите
deno install --entrypoint main.ts, чтобы создать файл заново. - Изучите различия между старой и новой версиями, чтобы выявить неожиданные добавления.
Вендоринг и файл блокировки
Эти подходы дополняют друг друга:
- Файл блокировки: хранит точные разрешённые версии и хеши целостности для удалённых зависимостей, а также зависимостей npm/JSR.
- Каталог вендорированных зависимостей: хранит фактический исходный код локально для герметичных сборок, сборок без подключения к сети и сборок с возможностью внесения исправлений.
Для максимальной воспроизводимости используйте оба подхода. Один лишь замороженный файл блокировки не обеспечивает полной герметичности сборки, если исходный код удалённого источника исчезнет; вендоринг устраняет этот пробел.
Краткое руководство по выбору
| Задача | Решение |
|---|---|
| Обнаружить подмену в исходном репозитории | Файл блокировки (добавить в систему контроля версий и заморозить) |
| Сборка без подключения к сети / в изолированной среде |
vendor: true + файл блокировки |
| Исправить сторонний код | Вендоринг или переопределения scopes (краткосрочное решение) |
| Быстрый CI с проверкой целостности | deno install --frozen |
| Осознанно обновить версии | Временно снять заморозку, запустить установку, проверить diff |
| Заблокировать снижение уровня доверия к публикации |
trust-policy=no-downgrade в .npmrc
|
Минимальные базовые меры для цепочки поставок (рекомендуется)
{
"imports": {/* centralize versions */},
"vendor": true,
"lock": { "frozen": true }
}
Добавьте в систему контроля версий deno.json, deno.lock и (при использовании вендоринга) весь каталог vendor/.
Запланированное задание CI, которое снимает заморозку, запускает deno add --latest (или вручную обновляет ключевые пакеты), выполняет тесты и открывает pull request с обновлённым файлом deno.lock (и vendor/), обеспечивает регулярное поступление исправлений безопасности и сохраняет детерминированность повседневных сборок.
Последнее обновление:
© 2018–2025 the Deno authors
Licensed under the MIT License.
https://docs.deno.com/runtime/packages/supply_chain