Опирается на правила: 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 немедленно
HIGHbandit / semgrep / Trivy / pip-audit≤ 2 неделиСборка падает
MEDIUMbandit / 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:

  1. Алерт security-team немедленно.
  2. Создание hotfix branch.
  3. Патч / temporary mitigation (pin безопасной версии в uv.lock).
  4. Deploy в проде в течение 24 часов.
  5. 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 без suppressionR-SEC-FIND-X1suppression с обоснованием
--ignore-vuln в pip-audit без untilR-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 без SARIFR-SEC-FIND-3GitHub Code Scanning через SARIF
Baseline не обновляется на merge в mainR-SEC-4auto-update на main
Просроченные suppressions не отслеживаютсяR-SEC-FIND-2weekly скрипт + issue

Куда дальше

  • SAST по коду — suppressions в bandit и semgrep, severity-фильтр.
  • CVE в зависимостях — pip-audit, ignore-list со сроком.
  • Секреты в коде и истории — утечка = CRITICAL, rotate немедленно.
  • Container/image-уязвимости — Trivy SARIF в Security tab.
  • Криптография в коде — слабая крипта = HIGH по умолчанию.