Files
orchestrator/docs/architecture/adr/adr-0012-security-gate.md

5.6 KiB
Raw Permalink Blame History

adr-0012: Security-гейт — secret-scanning + dependency audit перед мержем

  • Статус: proposed
  • Дата: 2026-06-07
  • Задача: ORCH-022
  • Детальный ADR: docs/work-items/ORCH-022/06-adr/ADR-001-security-gate.md

Контекст

Оркестратор автономен: developer пишет код без человека-фильтра. Перед слиянием ветки в main нет проверки на утёкший секрет (ключ/токен/пароль/приватный ключ) и уязвимую зависимость (CVE). Для self-hosting один общий прод-инстанс обслуживает все проекты с общей БД — секрет/CVE через одну задачу попадает в прод всех (CLAUDE.md §self-hosting, §8). Фактический мерж PR в main делает deployer в начале стадии deploy.

Решение

Детерминированный (без LLM) security-гейт как под-гейт ребра deploy-staging → deploy, рядом с merge-gate (ORCH-043) и image-freshness (ORCH-058), исполняемый ПЕРВЫМ среди edge-под-гейтов (ДО merge-gate). STAGE_TRANSITIONS не меняется; в QG_CHECKS добавлен check_security_gate. Паттерн — как у соседей: leaf-модуль src/security_gate.py (never-raise) + тонкая обёртка в QG_CHECKS + врезка _handle_security_gate в advance_stage.

  • Secret-scanning (gitleaks, offline): скан origin/main..HEAD; любой секрет вне аллоулиста (.gitleaks.toml) → вклад в FAIL. Offline → гарантия «секрет всегда блокирует» не зависит от сети.
  • Dependency audit (pip-audit, OSV/PyPI): severity ≥ security_dep_block_severity (дефолт HIGH) → FAIL; ниже / UNKNOWN → warning. Недоступность фида → fail-open + громкий warning (анти-петля; флаг security_dep_audit_fail_closed для строгого режима).
  • ПЕРВЫМ на ребре, ДО merge-gate: дёшево фейлить до дорогих rebase/rebuild; скан ветки ДО rebase не «обвиняет» задачу в CVE, притащенной обновившимся main (анти-петля ORCH-061); до захвата merge-lease → при FAIL lease освобождать не нужно.
  • Артефакт 17-security-report.md с YAML-frontmatter (security_status, secrets_found, deps_blocking, deps_warning, deps_audit_degraded); вердикт читается ТОЛЬКО из frontmatter (канон), negative-токен авторитетен; битый/нет → fail-closed.
  • FAIL → откат на development + developer-retry (общий _developer_retry_count, cap 3, затем set_issue_blocked + Telegram); task_desc несёт дословные находки (ORCH-046).
  • Условность (как ORCH-35/43/58): security_gate_enabled + security_gate_repos; пусто → реально только self-hosting (orchestrator), прочие репо — no-op pass.
  • never-raise, таймаут security_scan_timeout_s, гейт не деплоит/не рестартит прод.

Альтернативы

  • Вариант R (review-стадия): diff может разойтись с мержем в main; merge-edge — последняя страховка. Отклонено.
  • Вариант C (CI-job через check_ci_green): пороги/severity/аллоулист/артефакт плохо выражаются статусом коммита; коуплинг с раннером. Отклонено для v1 (точка расширения).
  • Новая стадия security: «пустая» стадия без агента не имеет триггера (как в ORCH-043). Отклонено.
  • fail-closed dep-audit / аудит после rebase: ложные откаты → петля. Отклонено.
  • Новая колонка retry в БД: не нужна (переиспользуем _developer_retry_count).

Последствия

  • Класс «тихо влитый секрет/CVE» закрыт: секреты — безусловно (offline), CVE — best-effort при доступности фида. Самоприменение CLAUDE.md §8 без человека.
  • Плата: ещё один «скрытый» под-гейт ребра (нет в STAGE_TRANSITIONS); внешние инструменты (gitleaks в образе, pip-audit в зависимостях); время скана на каждом прогоне (ограничено таймаутом); v1 — Python-only (SAST/мульти-стек — follow-up WI).
  • Сквозное изменение (новый QG + edge-под-гейт) → arch:major-change; прод-деплой ORCH-022 — строго через staging-гейт (8501), без рестарта прод-контейнера.

Связи

adr-0006 (merge-gate — паттерн edge-под-гейта/отката), adr-0008 (image-freshness — условность/never-raise/fail-closed), adr-0003 (условный гейт / is_self_hosting_repo), adr-0009 (анти-петля ложных FAIL, ORCH-061), ORCH-046 (дословный reason в task_desc), ORCH-9/15 (мульти-стек — будущая зависимость), ORCH-2 (worktree-изоляция).