Опирается на правила:
R-SEC-DEP-1…R-SEC-DEP-4иR-SEC-DEP-X1…R-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 Score | Severity | Действие |
|---|---|---|
| 9.0–10.0 | CRITICAL | Сборка падает, hotfix ≤ 24ч |
| 7.0–8.9 | HIGH | Сборка падает, патч ≤ 2 недели |
| 4.0–6.9 | MEDIUM | Отчёт, патч ≤ 30 дней |
| 0.1–3.9 | LOW | Игнор |
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 без срока и причины в reason | R-SEC-DEP-X1 | reason с датой пересмотра обязателен |
*/next/beta зависимости в production | R-SEC-DEP-X2 | только release-версии с lock-файлом |
npm install вместо npm ci в CI | R-SEC-DEP-1 | npm ci — воспроизводимая установка |
| Сканирование на каждом PR | R-SEC-DEP-1 | merge в main + nightly + release |
--audit-level не установлен | R-SEC-DEP-3 | --audit-level=high минимум |
| Без Renovate / Dependabot | R-SEC-DEP-2 | один из двух обязателен |
| Auto-merge major обновлений | R-SEC-DEP-2 | major — manual review |
npm audit \|\| true в CI | R-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.