Опирается на правила:
R-SEC-DEP-1…R-SEC-DEP-4иR-SEC-DEP-X1…R-SEC-DEP-X2из Security Style Guide → раздел 2. CVE в зависимостях.
Важно знать
pip-audit(PyPA Advisory Database) — основной инструмент CVE-сканирования для Python.- Запускается на merge в main + nightly + release, не на каждом PR (новые CVE появляются в NVD, не из PR).
- Lock-файл (
uv.lock/poetry.lock) обязательно коммитится — без него сборка невоспроизводима.- Renovate / Dependabot — авто-PR на minor/patch, major — manual review.
- CVSS ≥ 7.0 (HIGH/CRITICAL) ломает сборку; 4.0–6.9 — отчёт + 30 дней на патч; ниже — игнор.
- Suppressions со сроком (
until) обязательны — бессрочное подавление запрещено (R-SEC-DEP-X1).- Pre-release / незапиненные зависимости в production запрещены (
R-SEC-DEP-X2).
Уязвимость живёт не в твоём коде, а в библиотеках. requests с SSRF, pydantic с парсинг-ошибкой, cryptography с padding-оракулом — CVE в зависимостях. Код OrderService и CustomerRepository написан чисто, но production скомпрометирован через транзитивный пакет. UCP задаёт обязательный сканер + workflow обновления.
pip-audit — основной сканер
R-SEC-DEP-1: подключение pip-audit.
# pyproject.toml
[tool.pip-audit]
# PyPA Advisory Database + OSV
vulnerability-service = ["pypi", "osv"]
# fail на любой CVSS ≥ 7.0
fail-on-vuln = true
output = "json"
# CI-команда
pip-audit --requirement requirements.txt --output json --format json \
--vulnerability-service pypi --vulnerability-service osv \
-o pip-audit-report.json
С uv (рекомендуется для скорости):
uv pip audit --format json -o pip-audit-report.json
Где запускается — расслоение R-SEC-2:
| Этап | Запускать pip-audit? | Причина |
|---|---|---|
| IDE / каждый build | Нет | Лишние секунды на каждый запуск |
| Pre-commit | Нет | CVE не появляется из PR |
| PR | Нет | То же |
| Merge в main | Да | Baseline обновляется |
| Nightly | Да | Новые CVE в существующих deps |
| Release tag | Да | Финальная проверка перед выпуском |
# .github/workflows/security.yml
name: Security — CVE scan
on:
push:
branches: [main]
schedule:
- cron: "0 3 * * *" # nightly в 03:00 UTC
release:
types: [created]
jobs:
pip-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: uv sync --frozen
- name: Run pip-audit
run: |
uv pip audit --format json -o pip-audit-report.json
uv pip audit --format sarif -o pip-audit.sarif
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: pip-audit.sarif
category: pip-audit
- name: Upload report artifact
uses: actions/upload-artifact@v4
with:
name: pip-audit-report
path: pip-audit-report.json
SARIF идёт в GitHub Security tab — единый дашборд без отдельной инфры (R-SEC-FIND-3).
Lock-файл обязателен
R-SEC-DEP-4: воспроизводимость сборки.
# uv — рекомендуется
uv lock # генерирует uv.lock
uv sync --frozen # CI: только из lock, никакого resolve
# poetry
poetry lock
poetry install --no-root --sync
# pip-compile (pip-tools)
pip-compile requirements.in -o requirements.txt --generate-hashes
Lock-файл коммитится в репо. Без него:
pip installв CI может поднять другую версию пакета, чем на локальной машине.- CVE в
OrderService-зависимости незаметно появляется между двумя деплоями. - Rollback impossible — старые версии без lock не восстановить детерминированно.
# pyproject.toml — пиним minor-диапазон, lock дожимает до точной версии
[project]
dependencies = [
"fastapi>=0.111,<0.112",
"pydantic>=2.7,<3",
"sqlalchemy>=2.0,<3",
"httpx>=0.27,<0.28",
]
Диапазон в pyproject.toml — семантика совместимости; точная версия — в lock. Renovate читает оба.
Renovate / Dependabot
R-SEC-DEP-2: авто-PR на обновления.
// renovate.json
{
"extends": ["config:base"],
"schedule": ["before 8am on monday"],
"labels": ["dependencies"],
"packageRules": [
{
"matchManagers": ["pip_requirements", "poetry", "uv"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"matchUpdateTypes": ["major"],
"automerge": false,
"labels": ["dependencies", "major-update"]
},
{
"matchPackagePatterns": ["fastapi", "pydantic", "sqlalchemy"],
"matchUpdateTypes": ["minor"],
"automerge": false
}
],
"vulnerabilityAlerts": {
"enabled": true,
"labels": ["security"]
}
}
Что делает:
- Minor/patch автоматически merge-ятся после прохождения CI.
- Major — manual review (breaking changes).
- FastAPI / Pydantic / SQLAlchemy minor — manual review (часто содержат поведенческие изменения).
- Vulnerability alerts — отдельные PR с меткой
security, вне расписания, немедленно.
Без Renovate зависимости устаревают: через полгода CustomerService тянет pydantic 1.x с известным CVE, а обновление до 2.x — уже миграция на неделю.
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 | Игнор |
Release блокируется только на новые HIGH+ findings относительно baseline (R-SEC-4). Старый долг — отдельная задача, не блокирует выпуск: иначе первый протухший CVE без upstream-патча останавливает pipeline на месяц, и команда начнёт отключать сканер.
Baseline-файл для сравнения:
# После merge в main — обновляем baseline
pip-audit --format json -o security-baseline.json
git add security-baseline.json
git commit -m "chore: update security baseline"
На release сравниваем текущий отчёт с baseline — блокируем только новое.
Suppressions со сроком
R-SEC-DEP-4: каждая suppression имеет срок.
pip-audit поддерживает ignore-файл:
# pyproject.toml
[tool.pip-audit]
ignore-vuln = [
# CVE-2024-12345: httpx используется только для внутренних API за mTLS.
# Exploitable только через MITM на публичный endpoint — у нас его нет.
# Ждём httpx 0.28.0. Пересмотр: 2026-09-01
"GHSA-xxxx-yyyy-zzzz",
]
Либо через отдельный файл pip-audit-ignore.toml:
# pip-audit-ignore.toml
[[ignore]]
id = "PYSEC-2024-12345"
reason = "CVE в requests multipart — ProductService не обрабатывает multipart, только JSON. Патч ожидается в requests 2.33. Пересмотр: 2026-08-15."
until = "2026-08-15"
[[ignore]]
id = "GHSA-abcd-efgh-1234"
reason = "false positive — cryptography используется в test scope, не в production artifact CustomerService. Пересмотр: 2026-07-01."
until = "2026-07-01"
Правила:
until— обязательная дата пересмотра в ISO 8601.reason— объяснение: почему false positive / когда патч / почему acceptable risk.- Точечное подавление по CVE/GHSA ID — не на всю библиотеку.
Квартальный аудит просроченных suppression — R-SEC-FIND-2. Просроченная suppression = невыполненная задача.
Что запрещено
| Антипаттерн | Правило | Что взамен |
|---|---|---|
Suppression без until | R-SEC-DEP-X1 | срок обязателен |
Pre-release / >=-диапазон без lock в production | R-SEC-DEP-X2 | lock-файл в репо |
| pip-audit на каждом PR | R-SEC-DEP-1 | merge в main + nightly + release |
--exit-zero в CI (игнор findings) | R-SEC-1 | fail на HIGH/CRITICAL |
| Suppression на всю библиотеку без CVE ID | R-SEC-DEP-4 | точечно по CVE/GHSA |
| Без Renovate / Dependabot | R-SEC-DEP-2 | один из двух |
| Auto-merge major updates | R-SEC-DEP-2 | major — manual review |
| Lock-файл не коммитится | R-SEC-DEP-4 | uv.lock / poetry.lock в репо |
Куда дальше
- Container/image-уязвимости — Trivy сканирует образ включая Python-пакеты.
- Криптография — argon2,
secrets, AES-GCM. - Реакция на findings — SLA per severity, SARIF в Security tab.
- SAST по коду — bandit, semgrep, ruff
S-правила. - Секреты в коде и истории — Gitleaks,
.env-дисциплина.