5.6 KiB
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-изоляция).