# 02 — ТЗ: Security-гейт (secret-scanning + dependency audit) Work Item: **ORCH-022** · Стадия: analysis · См. `01-brd.md`, `03-acceptance-criteria.md`. > **Граница ответственности аналитика.** Ниже — *функциональные требования и точки > касания* кода. Выбор размещения гейта в пайплайне, конкретных инструментов и схемы > модулей — **решение архитектора** (см. §4 и `01-brd.md` §8). ТЗ фиксирует требования к > любому из допустимых вариантов и инварианты, которые нельзя нарушать. --- ## 1. Контекст кода (как есть) - **Стадии:** `src/stages.py::STAGE_TRANSITIONS` — линейный конвейер `… review → testing → deploy-staging → deploy → done`. Фактический merge ветки в `main` делает агент `deployer` **в начале стадии `deploy`** (CLAUDE/README). - **Quality Gates:** `src/qg/checks.py` — реестр `QG_CHECKS` (имя → функция), сигнатуры диспетчеризуются в `src/stage_engine.py::_run_qg`. - **Существующий паттерн «красный гейт → возврат developer»:** `check_ci_green` (PR #18) и rollback-ветки в `stage_engine._handle_qg_failure_rollbacks` (откат на `development`, developer-retry, cap `MAX_DEVELOPER_RETRIES = 3`, затем `set_issue_blocked` + Telegram). - **Эталонный паттерн детерминированного под-гейта на ребре** (без LLM, never-raise, условный раскат, откат на `development`): - merge-gate **ORCH-043** — `src/merge_gate.py` + `check_branch_mergeable` + `stage_engine._handle_merge_gate` (ребро `deploy-staging → deploy`); - image-freshness **ORCH-058** — `src/image_freshness.py` + `_check_staging_image_fresh` + `stage_engine._handle_image_freshness` (то же ребро). Оба: leaf-модуль с чистой логикой (never-raise) + тонкая обёртка в `QG_CHECKS` + врезка-обработчик в `advance_stage`, kill-switch `*_enabled` + scope `*_repos`, реально только для self-hosting при пустом scope. - **CI:** `.gitea/workflows/ci.yml` — один job `test` (pytest) на `self-hosted` раннере, push в `feature/**` и PR в `main`. `check_ci_green` читает комбинированный статус коммита из Gitea API. - **Артефакты задачи** нумерованы до `16-post-deploy-log.md`. - **Зависимости Python:** `requirements.txt` (корень репо). --- ## 2. Функциональные требования к реализации ### FR-1. Secret-scanning ветки перед мержем - Сканировать ветку задачи / её diff относительно `origin/main` на секреты (ключи, токены, пароли, приватные ключи). - **Любой** подтверждённый секрет (не из аллоулиста) → вердикт **FAIL** (порог A4: секреты всегда блокируют). - Инструмент (gitleaks / trufflehog) — выбор архитектора. Должен запускаться offline-/ детерминированно (без LLM) и иметь конфиг правил/аллоулиста в репозитории. ### FR-2. Dependency audit - Аудит зависимостей целевого стека на известные CVE. Для Python — манифест `requirements.txt` (инструмент pip-audit / trivy — выбор архитектора). - Классификация по severity. **Порог блокировки (A4, конфигурируемо BR-10):** - `CRITICAL`, `HIGH` → вклад в **FAIL**; - `MEDIUM`, `LOW` → **warning** (фиксируется в артефакте, не блокирует). - Недоступность CVE-фида: degrade-поведение по решению ADR (дефолт-предложение — fail-open + громкий warning, чтобы не плодить ложные завороты). Поведение должно быть детерминированным и протестированным. ### FR-3. Машиночитаемый артефакт-вердикт - Гейт порождает артефакт security-отчёта с **YAML-frontmatter**, напр.: ``` --- security_status: PASS # PASS | FAIL secrets_found: 0 deps_blocking: 0 # число уязвимостей уровня блокировки deps_warning: 2 --- ``` Имя артефакта — предложение: **`17-security-report.md`** (следующий свободный номер; финализирует архитектор). Тело — человекочитаемый список находок. - Вердикт читается гейтом **ТОЛЬКО из frontmatter** (канон проекта: «машинные вердикты — строго YAML-frontmatter, никогда проза»), по образцу `_parse_deploy_status` / `_parse_staging_status` / `check_reviewer_verdict`. Negative-токен (FAIL) авторитетен. - Отсутствие/битый frontmatter → `(False, reason)` (fail-closed на чтении вердикта, как у существующих парсеров). ### FR-4. Поведение красного гейта (откат) - `security_status: FAIL` → откат на `development` + enqueue `developer`, по образцу `_handle_qg_failure_rollbacks` (merge-gate-ветка — точный шаблон): - cap `MAX_DEVELOPER_RETRIES` (3); при исчерпании — `set_issue_blocked` + Telegram-алерт; - `task_desc` для developer несёт **дословную причину** (какие секреты/CVE), по образцу ORCH-046 (встраивание must-fix в `task_desc`), а не только ссылку на артефакт; - Plane-коммент + `notify_qg_failure` (наблюдаемость BR-11). ### FR-5. Условный раскат (как ORCH-35/43/58) - Глобальный kill-switch `security_gate_enabled` (env `ORCH_SECURITY_GATE_ENABLED`, дефолт по согласованию; рекомендуется `true` с safety-net, как у соседних фич). - Scope `security_gate_repos` (CSV); пусто → реально только `is_self_hosting_repo(repo)` (`orchestrator`). Прочие репо → `(True, "security-gate N/A for ")` (мгновенный pass). - Отдельные пороги-флаги (A4/BR-10): напр. `security_dep_block_severity` (`HIGH` по умолчанию), при желании `security_secrets_block` (`true`). ### FR-6. never-raise - Любая внутренняя ошибка гейта (сбой сканера, отсутствие бинаря, таймаут) → `(False, "")` **без** проброса исключения в `advance_stage`. Контракт — как у `check_branch_mergeable` (внешний + внутренний guard). - Таймаут сканирования ограничен (по образцу `merge_retest_timeout_s`). ### FR-7. Наблюдаемость - Блокировка → Telegram + Plane-коммент (BR-11). Проход → лог-строка, без шумных нотификаций (по образцу merge-gate pass). - Желательно: краткий снимок в `GET /queue` (опционально, по образцу блоков `reconcile`/ `reaper`/`post_deploy`) — на усмотрение архитектора. --- ## 3. Задействованные модули `src/` (точки касания) | Модуль | Изменение | |--------|-----------| | `src/security_gate.py` (**новый leaf-модуль**) | Чистая логика гейта: запуск сканеров, классификация по severity, применение порогов/аллоулиста, формирование вердикта + парсер frontmatter. **never-raise.** По образцу `src/merge_gate.py` / `src/image_freshness.py` / `src/post_deploy.py`. | | `src/qg/checks.py` | Новый чек `check_security_gate` (тонкая обёртка над `security_gate`, ленивый импорт во избежание циклов) + регистрация в `QG_CHECKS`. Условность (kill-switch/scope/self-hosting) — как `check_branch_mergeable` / `_check_staging_image_fresh`. | | `src/stage_engine.py` | Врезка-обработчик `_handle_security_gate(...)` по образцу `_handle_merge_gate` / `_handle_image_freshness`: вызов в `advance_stage` на выбранном архитектором ребре; FAIL → откат на `development` (FR-4); never-raise. **`STAGE_TRANSITIONS` НЕ меняется**, если выбран вариант «под-гейт ребра». | | `src/config.py` | Новые настройки: `security_gate_enabled`, `security_gate_repos`, `security_dep_block_severity`, `security_scan_timeout_s` (+ при необходимости пути к бинарям/конфигам сканеров). С docstring-комментариями по образцу ORCH-043/058. | | `.gitea/workflows/ci.yml` | **Если** архитектор выберет CI-путь: новый job `security` (secret-scan + dep-audit), влияющий на комбинированный статус коммита (тогда срабатывает `check_ci_green`-паттерн PR #18). Иначе — не трогается. | | `requirements.txt` / Dockerfile | Установка выбранных сканеров (если они Python-пакеты — в `requirements.txt`; если бинари — в Dockerfile/раннер). | | Конфиг сканера + аллоулист | Версионируемые файлы в репозитории (напр. `.gitleaks.toml` / аллоулист) — BR-13. | | `.openclaw/agents/developer.md` | (Если нужно) краткая инструкция developer'у про устранение security-находок при заворотах. | > Если выбран вариант «гейт на стадии `review`» — врезка делается в соответствующую > ветку `advance_stage`/обработчик ревью вместо ребра `deploy-staging → deploy`. --- ## 4. Размещение в пайплайне — варианты для архитектора Требование BRD: **«перед слиянием ветки в `main`»**. Допустимы (выбор + обоснование — в ADR): - **Вариант R (review):** security-проверка на стадии `review` (раньше отлов, дешевле откат — задача ещё близко к development). Минус: дальше по конвейеру `main` может уйти вперёд (но это закрывает merge-gate). - **Вариант M (merge-edge, рекомендуемый к рассмотрению):** под-гейт на ребре `deploy-staging → deploy`, рядом с merge-gate (ORCH-043) и image-freshness (ORCH-058) — непосредственно перед фактическим мержем `deployer`'ом. Плюс: единое место «последней страховки перед main», переиспользование готового паттерна врезки/отката/lease. - **Вариант C (CI-job):** добавить job в `ci.yml`; вердикт течёт через `check_ci_green`. Плюс: меньше нового кода в движке. Минус: пороги/severity-логика и артефакт-вердикт сложнее выразить только статусом коммита. ТЗ не предписывает вариант; реализация обязана сохранить инварианты §6. --- ## 5. Изменения API - Новых HTTP-endpoint'ов **не требуется**. - Допустимо (опционально, FR-7): расширить ответ `GET /queue` блоком `security` (counts/last_run) — по образцу блоков `reconcile`/`reaper`/`post_deploy`. Не обязательно. ## 6. Изменения схемы БД - **Не требуется.** Состояние гейта — артефакт-файл + (при необходимости) sentinel-файлы, по образцу merge-lease / deploy-state / post-deploy-state. Миграций БД нет. - Если архитектор сочтёт нужным считать security-retry отдельно от developer-retry — предпочесть подсчёт по `jobs`/`agent_runs` (как `_developer_retry_count` / `_merge_defer_count`), без новых колонок. ## 7. Инварианты (НЕ нарушать) 1. `STAGE_TRANSITIONS` и реестр `QG_CHECKS` остаются консистентными; при варианте «под-гейт ребра» — `STAGE_TRANSITIONS` не меняется (триггер — то же событие стадии). 2. Машинный вердикт — только из YAML-frontmatter, не из прозы. 3. never-raise: гейт никогда не пробрасывает исключение в `advance_stage`. 4. Условность как ORCH-35/43/58: не-self репо при пустом scope не затрагиваются (no-op). 5. Гейт **не деплоит и не рестартит** прод-контейнер (self-hosting safety). 6. Откат и retry-счётчик developer не ломаются (cap=3, затем эскалация). 7. Документация (CLAUDE.md, README, CHANGELOG, ADR) обновлена в том же PR (BR-12). ## 8. Артефакты pipeline, создаваемые/обновляемые - **Новый:** `docs/work-items/ORCH-022/17-security-report.md` (имя финализирует архитектор) с `security_status:`-frontmatter (FR-3) — порождается гейтом per-task. - **ADR:** `docs/work-items/ORCH-022/06-adr/ADR-001-.md` (решение: размещение, инструменты, degrade-поведение фида, пороги). При сквозном влиянии — global ADR в `docs/architecture/adr/`. - **Обновить:** `CLAUDE.md` (раздел «Артефакты задачи» — добавить 17-…), `docs/architecture/README.md` (таблица гейтов + реестр `QG_CHECKS` + новый раздел), `CHANGELOG.md`, `.env.example` (новые `ORCH_SECURITY_*`).