Опирается на правила:
R-SEC-FIND-1,R-SEC-FIND-2,R-SEC-FIND-3,R-SEC-FIND-X1из Security Style Guide → раздел 6. Реакция на findings.
Важно знать
- CRITICAL → сборка падает, hotfix ≤ 24 часов.
- HIGH → сборка падает, патч ≤ 2 недели (текущий спринт).
- MEDIUM → отчёт, патч ≤ 30 дней; без фикса — auto-escalate до HIGH.
- LOW → игнорим; не отображаем в dashboard, не создаём ticket.
# nosecбез кода и даты — не считается suppressed, считается долгом (R-SEC-SAST-X1).pip-audit --ignore-vulnбез срока — запрещено (R-SEC-DEP-X1).- SARIF из bandit / semgrep / Trivy / pip-audit — публикуем в GitHub Security tab (
R-SEC-FIND-3).- «Не уверен, эксплуатируется ли» — добавляй suppression с обоснованием; молчание = долг (
R-SEC-FIND-X1).
Findings — это входящий поток работы для security backlog. UCP формулирует SLA per severity, чтобы команда не тонула в noise (LOW) и быстро реагировала на критичное. Без SLA findings накапливаются, dashboard становится «море красного», команда привыкает игнорировать.
Severity → SLA
R-SEC-FIND-1: четыре уровня, четыре действия.
| Severity | Инструменты | SLA | Блокирует |
|---|---|---|---|
| CRITICAL | любой | ≤ 24 часа | Сборка падает, hotfix немедленно |
| HIGH | bandit / semgrep / Trivy / pip-audit | ≤ 2 недели | Сборка падает |
| MEDIUM | bandit / semgrep / Trivy / pip-audit | ≤ 30 дней | Отчёт, не блокирует |
| LOW | любой | — | Игнор |
CRITICAL — hotfix ≤ 24 часа
Примеры:
- Known active exploit (уровень Log4Shell) в runtime-зависимости.
- Hardcoded prod credential обнаружен Gitleaks в истории.
- Critical CVE с public PoC в пакете из
requirements.txt.
Action plan:
- Алерт security-team немедленно.
- Создание hotfix branch.
- Патч / temporary mitigation (pin безопасной версии в
uv.lock). - Deploy в проде в течение 24 часов.
- Post-mortem.
HIGH — текущий спринт
Примеры:
- CVE с CVSS ≥ 7.0 в зависимости
order-service. B608(SQL injection) от bandit наCustomerRepository.- semgrep rule
python.flask.security.injection.tainted-sql-stringна новом эндпоинте.
Action plan:
- Создание ticket с label
security/high. - Включение в текущий спринт.
- Прогон через PR review до merge.
MEDIUM — 30 дней
Примеры:
- CVE с CVSS 4.0–6.9 в транзитивной зависимости.
- bandit
B501(weak TLS) в нелегаси-коде. - Trivy finding в OS-пакете без активного эксплойта.
Action plan:
- Ticket с label
security/medium. - Включение в один из следующих спринтов.
- Если за 30 дней не fixed — auto-escalate до HIGH.
LOW — игнор
Примеры:
- bandit
B311(weak PRNG) в коде генерации order-id без security-контекста. - Теоретический CVE без known exploitation vector в test-зависимости.
Не отображаем в дашборде. Не создаём ticket. Не тратим время.
Suppressions имеют срок
R-SEC-FIND-2: срок обязателен везде.
bandit — # nosec с кодом и датой
customer_id = int(raw_id) # nosec B608 # justify: raw_id — Long из Path, validated @router path param; no string concat. до: 2026-12-01
customer_id = int(raw_id) # nosec
Второй вариант запрещён: R-SEC-SAST-X1.
Для глобального исключения — pyproject.toml (только если обоснование вынесено отдельным комментарием рядом с skips):
[tool.bandit]
skips = []
Не скипаем глобально — только точечно через # nosec BXXX с датой.
pip-audit — ignore-list с обоснованием
# pyproject.toml
[tool.pip-audit]
# PYSA-2024-XXXX: false positive в transitive dep requests 2.31.0,
# не в runtime path ProductService. Ждём fix в upstream 2.32.
# until: 2026-09-01
ignore-vulns = ["PYSA-2024-XXXX"]
Без комментария с until — не принимается на ревью.
semgrep — inline ignore
product_price = Decimal(raw) # nosemgrep: python.lang.security.audit.dangerous-eval-use
# justify: raw — Decimal-строка после Pydantic validation; не user-input напрямую. до: 2026-10-15
Раз в квартал — скрипт ищет просроченные suppressions:
#!/bin/bash
# tools/check-expired-suppressions.sh
NOW=$(date +%Y-%m-%d)
grep -rn "до: " src/ | while IFS=: read file line content; do
until=$(echo "$content" | grep -oP 'до: \K\S+')
if [[ -n "$until" && "$until" < "$NOW" ]]; then
echo "EXPIRED: $file:$line — $until"
fi
done
CI прогоняет скрипт weekly и постит issue в репо.
GitHub Code Scanning + SARIF
R-SEC-FIND-3: единый dashboard для всех инструментов.
# .github/workflows/security.yml
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: bandit
run: |
pip install bandit[sarif]
bandit -r src/ -f sarif -o bandit-results.sarif --severity-level medium
continue-on-error: false
- name: Upload bandit SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: bandit-results.sarif
- name: semgrep
run: |
pip install semgrep
semgrep --config=p/python --config=p/owasp-top-ten --sarif --output=semgrep-results.sarif src/
- name: Upload semgrep SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: semgrep-results.sarif
supply-chain:
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: pip-audit
run: |
pip install pip-audit
pip-audit --format sarif --output pip-audit-results.sarif
continue-on-error: false
- name: Upload pip-audit SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: pip-audit-results.sarif
- name: Trivy image scan
uses: aquasecurity/trivy-action@master
with:
image-ref: sber-order-service:${{ github.sha }}
format: sarif
output: trivy-results.sarif
severity: HIGH,CRITICAL
exit-code: '1'
- name: Upload Trivy SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: trivy-results.sarif
После публикации — все findings видны в GitHub Security tab:
- Фильтр по severity, tool, file.
- Tracking фикса (status: open / dismissed / fixed).
- Уведомления в Slack/PagerDuty через GitHub.
«Не уверен, эксплуатируется ли»
R-SEC-FIND-X1: главный антипаттерн.
bandit: B608 [HIGH] Possible SQL injection via string-based query construction
→ order_service/infrastructure/order_repository.py:87
Developer: "Ну там же validated_id, это безопасно"
[ничего не делает]
[finding остаётся в дашборде, security team анализирует повторно каждый квартал]
Что должно произойти:
async def find_by_customer_and_status(
self, customer_id: CustomerId, status: OrderStatus
) -> list[Order]:
query = (
select(order_table)
.where(order_table.c.customer_id == customer_id.value)
.where(order_table.c.status == status.value)
) # nosec B608 # justify: SQLAlchemy Core parametrized query; no string interpolation; validated CustomerId(UUID). до: 2027-01-01
result = await self._session.execute(query)
return [self._mapper.to_domain(row) for row in result]
Это:
- Запись в репо — security team может провести ревью.
- Срок — повторная проверка через заданный период.
- Обоснование — explicit аргумент, а не «кажется безопасно».
«Молчание = долг» — finding в дашборде остаётся, suppression нет, security team тратит время на повторный анализ при каждом ревью.
Baseline-механика
R-SEC-4: релиз блокируется только на новые findings.
{
"pipAudit": {
"knownVulnerabilities": [
{
"id": "PYSA-2024-12345",
"package": "requests",
"version": "2.31.0",
"firstSeen": "2026-03-15"
}
]
}
}
При release tag — сравниваем findings с config/security-baseline.json:
- Новый CVE → блокируем release.
- CVE уже в baseline → не блокируем (долг, отдельная задача).
Baseline отделяет накопленный долг от новых находок и позволяет выпускать релизы без накопления блокеров.
Что запрещено
| Антипаттерн | Правило | Что взамен |
|---|---|---|
# nosec без кода нарушения и даты | R-SEC-SAST-X1 | # nosec BXXX # justify: ... до: YYYY-MM-DD |
| Игнорирование finding без suppression | R-SEC-FIND-X1 | suppression с обоснованием |
--ignore-vuln в pip-audit без until | R-SEC-DEP-X1 | срок обязателен в комментарии |
| CRITICAL без hotfix в течение 24 часов | R-SEC-FIND-1 | немедленный hotfix branch |
| HIGH в backlog без включения в спринт | R-SEC-FIND-1 | ≤ 2 недели |
| MEDIUM без срока патча | R-SEC-FIND-1 | ≤ 30 дней, auto-escalate |
| LOW отображается в дашборде | R-SEC-FIND-1 | не показываем, не создаём ticket |
| Findings только в proprietary tool без SARIF | R-SEC-FIND-3 | GitHub Code Scanning через SARIF |
| Baseline не обновляется на merge в main | R-SEC-4 | auto-update на main |
| Просроченные suppressions не отслеживаются | R-SEC-FIND-2 | weekly скрипт + issue |
Куда дальше
- SAST по коду — suppressions в bandit и semgrep, severity-фильтр.
- CVE в зависимостях — pip-audit, ignore-list со сроком.
- Секреты в коде и истории — утечка = CRITICAL, rotate немедленно.
- Container/image-уязвимости — Trivy SARIF в Security tab.
- Криптография в коде — слабая крипта = HIGH по умолчанию.