Перейти к содержимому
>_ITDITDПлатформа веб-безопасности

Руководства по безопасности

osv-scanner на практике — pnpm, игнорирование CVE, офлайн-режим и типичные ошибки

Вопросы второго дня: поддержка pnpm-lock.yaml, игнорирование одной CVE через osv-scanner.toml, три офлайн-флага и ошибка, которую они вызывают, коды завершения 0/1/127/128 в CI и флаги, переименованные при переходе с v1 на v2.

Опубликовано 2026-09-04 Обновлено 2026-10-06 Последняя проверка 2026-10-06 10 мин чтения

Для кого: для тех, кто уже установил osv-scanner и столкнулся с трудностями. Здесь разобраны вопросы второго дня, а не установка. Всё изложенное следует официальной документации.

Указать lock-файл

# a single lockfile
osv-scanner scan source -L pnpm-lock.yaml
 
# walk a directory
osv-scanner scan source -r ./my-project/

Если формат нельзя определить по имени файла, добавьте перед путём нужный парсер:

# parse an oddly named file as requirements.txt
osv-scanner scan source -L 'requirements.txt:/path/to/extra-requirements.txt'
 
# path containing a colon — lead with a bare ':' to keep filename inference
osv-scanner scan source -L ':/path/to/my:projects/package-lock.json'

Почему lock-файл, а не манифест

Манифест (package.json и подобные) фиксирует диапазон, который вы запросили, а не то, что реально установлено. Lock-файл содержит разрешённые точные версии, поэтому скан совпадает с тем, что реально есть в дереве. Судить по диапазону и надеяться — гораздо более слабое доказательство.

Как проверить свою конфигурацию

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

1

Проверьте версию

Выполните osv-scanner --version. v2, выпущенная в 2025 году, изменила вид команд и названия ряда флагов, поэтому инструкции, написанные для v1, часто не работают как есть. Официальная документация теперь ориентирована на v2.
2

Проверьте, что он прочитал

Журнал запуска показывает каждый прочитанный файл и сколько пакетов в нём найдено. Убедитесь, что ожидаемые lock-файлы в списке и что числа не подозрительно малы. Для полного перечня используйте --format json --all-packages (--all-packages работает только с выводом JSON).
3

Проверьте код завершения

Сразу после запуска используйте echo $? в bash или $LASTEXITCODE в PowerShell. Значения — в таблице ниже.
4

Проверьте, что конфигурация реально применилась

osv-scanner.toml применяется только к файлам в той директории, где он лежит; на дочерние директории он не распространяется. В монорепозитории, сканируемом с -r, кладите его рядом с каждым lock-файлом или передайте один файл на всё через --config.
5

Проверьте, где лежит офлайн-база и насколько она стара

Локальная база читается из OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORY, если переменная задана, иначе из пользовательского каталога кэша, затем из системного временного каталога. Внутри файлы лежат по схеме osv-scanner/<ecosystem>/all.zip. Если даты их изменения старые, то и ваш вердикт устарел.

Коды завершения и как CI должен их обрабатывать

Код завершенияОфициальное значениеВ CI
0Пакеты найдены, известных уязвимостей не совпалоПроходит
1Пакеты найдены, уязвимости совпалиПадает (находка: исправьте или проигнорируйте со сроком)
127Общая ошибкаПадает (сломался сам скан)
128Пакеты не найдены (ничего читаемого не подхвачено)Падает (исправьте путь или добавьте lock-файл)

Документация резервирует 1–126 для исходов, связанных с результатами, а 129–255 — для ошибок, не связанных с результатами. Если разделить обработку в CI на три случая — 0 проходит, 1 — находка, всё остальное — сломанный скан, — сбои диагностируются гораздо быстрее.

Игнорирование конкретной уязвимости

Положите osv-scanner.toml в сканируемую директорию:

[[IgnoredVulns]]
id = "GHSA-xxxx-xxxx-xxxx"
ignoreUntil = 2026-12-31
reason = "Feature is not used and the code path is unreachable; re-evaluate at the December inventory"
  • id (обязательно): идентификатор уязвимости
  • ignoreUntil (необязательно): после этой даты она снова сообщается
  • reason (необязательно): всё равно пишите — для следующего человека, которым обычно оказываетесь вы

--config=/path/to/config.toml переопределяет файлы в директориях и действует везде. Учтите, что игнорирование уязвимости игнорирует и те, что считаются её псевдонимами (aliases).

Задавайте каждому исключению срок, а не удаляйте навсегда

Запись без ignoreUntil — это пункт, который исчезает навсегда и к которому никто не возвращается. Наше правило: у каждого исключения есть срок и причина — когда дата проходит, оно возвращается, поэтому отложенное решение нельзя тихо забыть. Бессрочные исключения, добавленные, чтобы заглушить CI, — именно то, что через полгода превращается в инцидент.

Офлайн-режим: три флага, три задачи

Здесь больше всего путаницы.

ФлагЧто делает
--offlineПолностью офлайн. Сверяет с ранее загруженной локальной базой, не обновляет её и никуда не отправляет информацию о проекте или зависимостях
--offline-vulnerabilitiesОфлайн только сопоставление с уязвимостями; остальная работа (например, разрешение транзитивных зависимостей) может по-прежнему использовать сеть
--download-offline-databasesРазрешает загрузку/обновление локальной базы — действует только вместе с офлайн-флагом

--offline

полностью офлайн; база не обновляется, наружу ничего не отправляется

--offline-vulnerabilities

локально только сопоставление; остальная работа может использовать сеть

--download-offline-databases (сам по себе не действует)

нужен один из флагов выше → в одиночку вы получите "databases can only be downloaded when running in offline mode"

Три офлайн-флага. --download-offline-databases сам по себе ничего не делает — отсюда ошибка, которую ищут в интернете.

Ошибки и их причины

  • databases can only be downloaded when running in offline mode → --download-offline-databases передан сам по себе; рядом нужен офлайн-флаг
  • --offline без локальной базы → вы получите ошибку о том, что база отсутствует; первый запуск должен её загрузить
  • no package sources found → по этому пути ничего не распознано как lock-файл или манифест; укажите файл через -L или обойдите дерево с -r

Как выбрать без догадок

  • Запуск в изолированной сети → --offline (плюс --download-offline-databases при первом запуске)
  • Локальными должны остаться только данные об уязвимостях → --offline-vulnerabilities
  • Зафиксировать, где лежит база → переменная окружения OSV_SCANNER_LOCAL_DB_CACHE_DIRECTORY
  • Отладка → сначала запустите без флагов, затем сужайте

Когда не работают флаги из статьи про v1

Многие результаты поиска написаны для v1. Основные изменения из официального руководства по миграции:

v1v2
--experimental-offline--offline
--experimental-download-offline-databases--download-offline-databases
--experimental-call-analysis--call-analysis
--experimental-licenses и связанныеобъединены в --licenses
--docker / -D (контейнеры)osv-scanner scan image <image>
--json--format=json
--skip-git--include-git-root (смысл обратный)

osv-scanner <dir> по-прежнему работает как сокращение для osv-scanner scan source <dir>. Если в скриптах писать scan source полностью, намерение будет понятно следующему читателю.

Частые ошибки и как их исправить

Частые ошибки

  • CI считает успехом «любой код завершения, кроме 1»
  • || true добавлен, чтобы сбои исчезли
  • Один osv-scanner.toml в корне репозитория, который якобы покрывает подкаталоги
  • Закоммичен только package.json, без lock-файла
  • Предположение, что --offline всё равно разрешает транзитивные зависимости pom.xml
  • Исключения без ignoreUntil

Исправление

  • Пропускать только 0, чтобы 128 (ничего не прочитано) не стал зелёным
  • Глушить отдельные находки исключениями со сроком
  • Класть файл в каждую директорию или применять один везде через --config
  • Коммитить lock-файл (для JavaScript: package-lock.json, pnpm-lock.yaml, yarn.lock или bun.lock)
  • Офлайн разрешение транзитивных зависимостей отключено; если сеть разрешена, используйте --offline-vulnerabilities
  • Всегда добавлять дату и причину и заново решать, когда дата наступит

Maven pom.xml не перечисляет все разрешённые версии так, как lock-файл. По умолчанию osv-scanner вычисляет транзитивный граф с помощью резолвера deps.dev (или Maven Central с --data-source=native). Для этого нужна сеть, и при --offline и --no-resolve этот шаг пропускается. При полностью изолированном запуске ожидайте, что результаты покрывают только зависимости, прямо записанные в pom.xml.

Реальный пример: почему на вопрос о Log4Shell было трудно ответить

Когда в декабре 2021 года появилась Log4Shell (CVE-2021-44228), многие организации не могли быстро сказать, затронуты ли они. Сами они Log4j не добавляли; он приходил через фреймворки и другие библиотеки. Рекомендации CISA просили организации полностью перечислить установленное ПО и сравнить его со списком затронутого ПО.

Применительно к osv-scanner это значит проверить две вещи:

  • Транзитивные зависимости действительно читаются: lock-файлы там, где они есть в экосистеме, а для Maven — запуск с включённым разрешением (не офлайн, не --no-resolve)
  • Перечень действительно содержит библиотеки, приходящие косвенно, — выведите его через --format json --all-packages и посмотрите

Сможете ли вы ответить на вопрос «затронуты ли мы?» за минуты в день раскрытия, зависит от того, насколько правильно этот перечень собран заранее.

Чем он отличается от соседних инструментов

ИнструментКакие данные читаетВ чём силён
osv-scannerOSV.devПо разным экосистемам, по lock-файлам, может работать офлайн
npm audit / pnpm auditБаза уведомлений npmСразу для npm-проектов, ничего не нужно устанавливать дополнительно
DependabotGitHub AdvisoryОткрывает PR с обновлениями — предлагает исправление, а не только находку
TrivyНесколько (включая OSV)Образы контейнеров и пакеты ОС, а также зависимости приложения

Ошибка — выбрать ровно один. Разные базы показывают разное. Мы запускаем pnpm audit как ежедневную автоматическую проверку и используем osv-scanner для проверок по нескольким lock-файлам.

Взгляд этого сайта: сканер не скажет, что ваша фиксация устарела

Одна проблема проявляется только в реальной эксплуатации. Когда вы фиксируете транзитивную зависимость на точной версии через overrides, этот пакет перестаёт получать патч-обновления — а сканер подаёт голос, только когда существует известная уязвимость. Поэтому состояние «версия, которую вы заморозили, уже старая» никто не сообщает. Мы столкнулись с этим трижды, прежде чем добавили собственную ежедневную проверку, сравнивающую каждую фиксацию с новейшим патчем в её основной линии. Сканер показывает известные уязвимости сегодня; условия, которые, вероятно, оставят вас уязвимыми позже, требуют другого механизма.

Источники

Читать дальше

FAQ

QПоддерживает ли osv-scanner pnpm-lock.yaml?
A

Да. Поддерживаются lock-файлы npm, yarn и pnpm. Укажите файл напрямую через `osv-scanner scan source -L pnpm-lock.yaml` или обойдите дерево с `-r`.

QКак игнорировать одну CVE?
A

Положите `osv-scanner.toml` в сканируемую директорию и добавьте запись `[[IgnoredVulns]]` с `id`. Можно также задать `ignoreUntil` (дату окончания) и `reason`, чтобы исключение фиксировало, когда и почему, а не исчезало навсегда. Передача `--config=/path/to/config.toml` переопределяет файлы в директориях и действует глобально.

QПочему я получаю «databases can only be downloaded when running in offline mode»?
A

Потому что `--download-offline-databases` действует только вместе с офлайн-флагом — сам по себе он не используется. Сочетайте его с офлайн-флагом, если хотите, чтобы локальная база загружалась в рамках запуска.

QЕсли у меня уже есть npm audit и Dependabot, нужен ли osv-scanner?
A

Они читают разные базы данных и покрывают разное. npm audit использует базу уведомлений npm и работает только с npm; Dependabot в основном предлагает обновления на GitHub; osv-scanner читает OSV.dev по разным экосистемам (npm, PyPI, Go, Maven и др.), работает по lock-файлам и может работать полностью офлайн. То, что один находит что-то, что пропускает другой, — нормально, поэтому считайте их разными задачами, а не выбирайте один.

QКак CI должен обрабатывать коды завершения osv-scanner?
A

По официальной документации: 0 — пакеты найдены, и ни один не совпал с известной уязвимостью, 1 — найдены уязвимости, 127 — общая ошибка, 128 — пакеты вообще не найдены. Считайте успехом только 0. Правило вида «всё, кроме 1, проходит» превращает 128 — когда ничего не было просканировано — в зелёную галочку.

QПочему не работают --experimental-offline или --docker из старых статей?
A

В v2 они были переименованы или перенесены. Официальное руководство по миграции сопоставляет --experimental-offline с --offline, --experimental-download-offline-databases с --download-offline-databases, --docker с командой `osv-scanner scan image`, а --json с `--format=json`. Сначала выполните `osv-scanner --version`, чтобы узнать, какая у вас основная версия.