15 KiB
15 KiB
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, capMAX_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_freshstage_engine._handle_image_freshness(то же ребро). Оба: leaf-модуль с чистой логикой (never-raise) + тонкая обёртка вQG_CHECKS+ врезка-обработчик вadvance_stage, kill-switch*_enabled+ scope*_repos, реально только для self-hosting при пустом scope.
- merge-gate ORCH-043 —
- CI:
.gitea/workflows/ci.yml— один jobtest(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+ enqueuedeveloper, по образцу_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).
- cap
FR-5. Условный раскат (как ORCH-35/43/58)
- Глобальный kill-switch
security_gate_enabled(envORCH_SECURITY_GATE_ENABLED, дефолт по согласованию; рекомендуетсяtrueс safety-net, как у соседних фич). - Scope
security_gate_repos(CSV); пусто → реально толькоis_self_hosting_repo(repo)(orchestrator). Прочие репо →(True, "security-gate N/A for <repo>")(мгновенный pass). - Отдельные пороги-флаги (A4/BR-10): напр.
security_dep_block_severity(HIGHпо умолчанию), при желанииsecurity_secrets_block(true).
FR-6. never-raise
- Любая внутренняя ошибка гейта (сбой сканера, отсутствие бинаря, таймаут) →
(False, "<reason>")без проброса исключения в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. Инварианты (НЕ нарушать)
STAGE_TRANSITIONSи реестрQG_CHECKSостаются консистентными; при варианте «под-гейт ребра» —STAGE_TRANSITIONSне меняется (триггер — то же событие стадии).- Машинный вердикт — только из YAML-frontmatter, не из прозы.
- never-raise: гейт никогда не пробрасывает исключение в
advance_stage. - Условность как ORCH-35/43/58: не-self репо при пустом scope не затрагиваются (no-op).
- Гейт не деплоит и не рестартит прод-контейнер (self-hosting safety).
- Откат и retry-счётчик developer не ломаются (cap=3, затем эскалация).
- Документация (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-<slug>.md(решение: размещение, инструменты, degrade-поведение фида, пороги). При сквозном влиянии — global ADR вdocs/architecture/adr/. - Обновить:
CLAUDE.md(раздел «Артефакты задачи» — добавить 17-…),docs/architecture/README.md(таблица гейтов + реестрQG_CHECKS+ новый раздел),CHANGELOG.md,.env.example(новыеORCH_SECURITY_*).