Опирается на правила: R-SEC-DEP-1R-SEC-DEP-4 и R-SEC-DEP-X1R-SEC-DEP-X2 из Security Style Guide → раздел 2. CVE в зависимостях.

Важно знать

  • npm audit проверяет зависимости против GitHub Advisory Database; osv-scanner — шире: OSV + NIST NVD + GHSA, отдаёт SARIF из коробки.
  • Запускается на merge в main + nightly + release-tag, не на каждом PR — CVE появляется в базах, а не из твоего PR.
  • npm ci (не npm install) в CI обязателен — воспроизводимая установка из lock-файла.
  • package-lock.json / pnpm-lock.yaml коммитятся в репо; без lock-файла гарантия воспроизводимости исчезает.
  • Renovate / Dependabot — авто-PR на minor/patch, major — manual review.
  • CVSS ≥ 7.0 (HIGH/CRITICAL) ломает сборку; 4.0–6.9 — отчёт + 30 дней на патч; ниже — игнор.
  • Suppressions с reason и сроком обязательны — бессрочное подавление запрещено.
  • Незапиненные/pre-release зависимости (*, next-теги, установка без lock-файла) в production запрещены.

Отдельный класс уязвимостей — не в твоём коде, а в библиотеках. Log4Shell жил в транзитивной зависимости; разработчики не писали уязвимый код, но получили CVE в production. Для Node-экосистемы с её глубокими деревьями транзитивных зависимостей это особенно актуально: node_modules среднего NestJS-сервиса — тысячи пакетов. UCP формулирует обязательный сканер + workflow обновления.

npm audit и osv-scanner

R-SEC-DEP-1: сканирование зависимостей.

npm audit — встроенный инструмент, проверяет package-lock.json против GitHub Advisory Database:

npm audit --audit-level=high

osv-scanner — более широкая база (OSV DB агрегирует GHSA, NIST NVD, OSS-Fuzz и другие), поддерживает SARIF, подходит для GitHub Code Scanning:

osv-scanner --lockfile package-lock.json --format sarif --output osv-results.sarif

Оба инструмента дополняют друг друга: npm audit быстрее и встроен, osv-scanner полнее и отдаёт стандартный SARIF. В сервисе order-service (NestJS, управляет заказами Customer) типичная конфигурация CI-шага:

- name: Scan dependencies (osv-scanner)
  run: |
    osv-scanner --lockfile package-lock.json \
      --format sarif \
      --output osv-results.sarif \
      --config osv-scanner.toml

- name: Upload SARIF
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: osv-results.sarif
    category: osv-scanner

- name: Fail on HIGH+
  run: npm audit --audit-level=high

Расслоение по скорости фидбека R-SEC-2:

ЭтапЗапускать сканер?Причина
IDE / каждый buildНетЗамедляет разработку
Pre-commitНетCVE не появляется из кода
PRНетCVE появляется в базах независимо от PR
Merge в mainДаBaseline обновляется
NightlyДаНовые CVE в существующих deps
Release tagДаФинальная проверка перед выпуском

Lock-файл и npm ci

R-SEC-DEP-1 / R-SEC-DEP-X2: воспроизводимость установки.

npm install обновляет package-lock.json при каждом запуске — версии транзитивных зависимостей могут измениться. npm ci устанавливает строго по lock-файлу, завершается ошибкой при расхождении:

- name: Install dependencies
  run: npm ci

package-lock.json должен быть закоммичен. Без него:

  • два разных npm install в разное время дадут разные версии транзитивных deps
  • невозможно воспроизвести сборку прошлого релиза
  • osv-scanner без lock-файла не работает корректно

Renovate / Dependabot

R-SEC-DEP-2: автоматические PR на обновления зависимостей.

{
  "extends": ["config:base"],
  "schedule": ["before 8am on monday"],
  "labels": ["dependencies"],
  "packageRules": [
    {
      "matchUpdateTypes": ["minor", "patch"],
      "automerge": true,
      "automergeType": "pr",
      "platformAutomerge": true
    },
    {
      "matchUpdateTypes": ["major"],
      "automerge": false,
      "labels": ["dependencies", "major-update"]
    },
    {
      "matchPackagePatterns": ["@nestjs/", "fastify"],
      "matchUpdateTypes": ["minor"],
      "automerge": false
    }
  ],
  "vulnerabilityAlerts": {
    "enabled": true,
    "labels": ["security"]
  }
}

Что настроено:

  • Minor/patch — автомерж после зелёного CI.
  • Major — manual review (breaking changes в npm-пакетах часты).
  • @nestjs/* minor — manual review (NestJS minor может содержать внутренние breaking при нестандартных DI-конфигурациях).
  • Vulnerability alerts — отдельные PR с меткой security, приоритет над плановыми обновлениями.

Без Renovate зависимости product-service (управляет Product) за полгода накапливают десятки CVE, обновление превращается в ручную работу на несколько дней.

CVSS → действие

R-SEC-DEP-3: severity по CVSS score.

CVSS ScoreSeverityДействие
9.0–10.0CRITICALСборка падает, hotfix ≤ 24ч
7.0–8.9HIGHСборка падает, патч ≤ 2 недели
4.0–6.9MEDIUMОтчёт, патч ≤ 30 дней
0.1–3.9LOWИгнор

npm audit --audit-level=high соответствует failBuildOnCVSS = 7.0 в Java-эквиваленте.

Релиз блокируется только на новые HIGH+ findings относительно baseline (R-SEC-4). Исторический долг (HIGH CVE без upstream-патча) трекается отдельной задачей — иначе первый протухший CVE остановит CI навсегда и команда начнёт обходить проверки.

Suppressions с причиной и сроком

R-SEC-DEP-4: каждое подавление CVE имеет обоснование и срок пересмотра.

osv-scanner.toml:

[[IgnoredVulns]]
id = "GHSA-xxxx-yyyy-zzzz"
reason = "CVE-2024-12345 в пакете order-utils используется только в тестах, не попадает в production bundle. Ждём fix в order-utils 3.2.0. Пересмотр: 2026-09-01."

[[IgnoredVulns]]
id = "CVE-2024-67890"
reason = "Транзитивная зависимость через @nestjs/platform-express. False positive — уязвимый код-путь недостижим при нашей конфигурации. Patch ожидается в express 5.1. Пересмотр: 2026-08-01."

audit-ci.json (для npm audit):

{
  "high": true,
  "allowlist": [
    {
      "module": "some-legacy-parser",
      "reason": "Используется только в CLI-утилите, не в HTTP-пути customer-service. Upstream CVE-2024-11111. Пересмотр: 2026-07-15.",
      "expiry": "2026-07-15"
    }
  ]
}

Для транзитивных зависимостей без upstream-патча — принудительный override через package.json:

{
  "overrides": {
    "vulnerable-transitive-dep": "^2.1.0"
  }
}

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

Что запрещено

АнтипаттернПравилоЧто взамен
Suppression без срока и причины в reasonR-SEC-DEP-X1reason с датой пересмотра обязателен
*/next/beta зависимости в productionR-SEC-DEP-X2только release-версии с lock-файлом
npm install вместо npm ci в CIR-SEC-DEP-1npm ci — воспроизводимая установка
Сканирование на каждом PRR-SEC-DEP-1merge в main + nightly + release
--audit-level не установленR-SEC-DEP-3--audit-level=high минимум
Без Renovate / DependabotR-SEC-DEP-2один из двух обязателен
Auto-merge major обновленийR-SEC-DEP-2major — manual review
npm audit \|\| true в CIR-SEC-1\|\| true полностью обнуляет смысл проверки

Куда дальше

  • Container/image-уязвимости — Trivy сканирует Docker-образ включая OS-пакеты и npm-зависимости.
  • Криптография в коде — crypto.randomBytes, argon2, AES-GCM в Node.
  • Реакция на findings — SLA по severity, SARIF в GitHub Security tab.
  • SAST по коду — eslint-plugin-security и semgrep для кода NestJS-сервиса.
  • Секреты в коде и истории — Gitleaks, husky pre-commit hook.