This commit is contained in:
2026-03-04 19:33:47 +03:00
parent 42def69dcc
commit 2820e491d5
28 changed files with 972 additions and 365 deletions

View File

@@ -178,6 +178,90 @@ module CleanReleaseRouter:
---
# [DEF:backend.src.services.clean_release.config_loader:Module]
# @TIER: CRITICAL
# @SEMANTICS: clean-release, config, yaml, policy-source, declarative
# @PURPOSE: Load and validate .clean-release.yaml from repository root, providing typed config to all pipeline stages.
# @LAYER: Infrastructure
# @RELATION: CONSUMED_BY -> backend.src.services.clean_release.policy_engine
# @RELATION: CONSUMED_BY -> backend.src.services.clean_release.compliance_orchestrator
# @INVARIANT: Config load must fail fast on invalid/missing required fields for enterprise-clean profile.
# @TEST_CONTRACT: YamlFilePath -> CleanReleaseConfig
# @TEST_FIXTURE: valid_enterprise_config -> {"profile":"enterprise-clean","scan_mode":"repo","prohibited_categories":["test-data"],"allowed_sources":["*.corp.local"]}
# @TEST_EDGE: missing_yaml -> repo without .clean-release.yaml must raise ConfigNotFoundError
# @TEST_EDGE: missing_allowed_sources -> enterprise-clean without allowed_sources must fail validation
# @TEST_EDGE: invalid_scan_mode -> scan_mode="unknown" must raise ValueError
# @TEST_INVARIANT: config_validation_integrity -> VERIFIED_BY: [valid_enterprise_config, missing_allowed_sources]
class CleanReleaseConfigLoader:
# @PURPOSE: Discover and load .clean-release.yaml from target path.
# @PRE: Path to repository root or explicit config path provided.
# @POST: Returns validated CleanReleaseConfig or raises ConfigError.
def load_config(self): ...
# @PURPOSE: Validate config schema and business rules.
# @PRE: Raw YAML parsed.
# @POST: Returns typed config with all required fields populated.
def validate_config(self): ...
# [/DEF:backend.src.services.clean_release.config_loader:Module]
---
# [DEF:backend.src.services.clean_release.filesystem_scanner:Module]
# @TIER: CRITICAL
# @SEMANTICS: clean-release, scanner, filesystem, artifacts, url-detection
# @PURPOSE: Scan filesystem (repo/build/docker) for prohibited artifacts and external URLs in text files.
# @LAYER: Domain
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.config_loader
# @RELATION: CONSUMED_BY -> backend.src.services.clean_release.compliance_orchestrator
# @INVARIANT: Scanner must respect ignore_paths and never modify scanned files.
# @TEST_CONTRACT: ScanTarget + CleanReleaseConfig -> ScanResult
# @TEST_FIXTURE: repo_with_test_data -> {"path":"test/data.csv","category":"test-data","classification":"excluded-prohibited"}
# @TEST_EDGE: binary_file_skip -> binary files must be skipped during URL extraction
# @TEST_EDGE: symlink_loop -> circular symlinks must not cause infinite recursion
# @TEST_EDGE: ignore_path_respected -> files in ignore_paths must never appear in results
# @TEST_INVARIANT: scan_completeness -> VERIFIED_BY: [repo_with_test_data, ignore_path_respected]
class FilesystemScanner:
# @PURPOSE: Scan target for prohibited artifacts using prohibited_paths and prohibited_categories.
# @PRE: Config loaded with prohibited rules.
# @POST: Returns list of classified artifacts with violations.
def scan_artifacts(self): ...
# @PURPOSE: Extract URLs/hosts from all text files and match against allowed_sources.
# @PRE: Config loaded with allowed_sources patterns.
# @POST: Returns list of external endpoint violations.
def scan_endpoints(self): ...
# [/DEF:backend.src.services.clean_release.filesystem_scanner:Module]
---
# [DEF:backend.src.services.clean_release.db_cleanup_executor:Module]
# @TIER: CRITICAL
# @SEMANTICS: clean-release, database, cleanup, test-data, enterprise
# @PURPOSE: Execute database cleanup rules from .clean-release.yaml to remove test users and demo data.
# @LAYER: Domain
# @RELATION: DEPENDS_ON -> backend.src.services.clean_release.config_loader
# @RELATION: CONSUMED_BY -> backend.src.services.clean_release.compliance_orchestrator
# @INVARIANT: Preserve-listed records must never be deleted regardless of condition match.
# @TEST_CONTRACT: DatabaseCleanupConfig -> CleanupResult
# @TEST_FIXTURE: cleanup_test_users -> {"table":"ab_user","condition":"username IN ('test_user')","preserve":["admin"]}
# @TEST_EDGE: preserve_overrides_condition -> preserved record matching condition must survive cleanup
# @TEST_EDGE: empty_tables_list -> enabled=true with empty tables must raise ConfigError
# @TEST_EDGE: dry_run_mode -> dry run must report planned deletions without executing them
# @TEST_INVARIANT: preserve_integrity -> VERIFIED_BY: [cleanup_test_users, preserve_overrides_condition]
class DatabaseCleanupExecutor:
# @PURPOSE: Execute cleanup rules in dry-run mode first, then optionally apply.
# @PRE: Database connection and cleanup config available.
# @POST: Returns cleanup report with deleted/preserved counts per table.
def execute_cleanup(self): ...
# @PURPOSE: Verify that preserve rules are respected post-cleanup.
# @PRE: Cleanup executed.
# @POST: Returns validation result confirming preserved records exist.
def verify_preserves(self): ...
# [/DEF:backend.src.services.clean_release.db_cleanup_executor:Module]
---
## Contract Trace (Key User Scenario)
Сценарий: оператор запускает TUI-проверку и получает BLOCKED из-за внешнего источника.

View File

@@ -218,6 +218,39 @@
---
## 8) CleanReleaseConfig
**Purpose**: Декларативный конфиг `.clean-release.yaml` в корне репозитория — центральный source of truth для политики clean-валидации.
### Top-Level Fields
- `profile` (enum, required): `enterprise-clean`, `development`.
- `scan_mode` (enum, required): `repo`, `build`, `docker`.
- `prohibited_categories` (array[string], required): категории запрещённых артефактов.
- `prohibited_paths` (array[string], required): glob-паттерны запрещённых путей.
- `allowed_sources` (array[string], required): glob-паттерны допустимых endpoint'ов.
- `ignore_paths` (array[string], optional): пути, исключённые из сканирования.
- `database_cleanup` (DatabaseCleanupConfig, optional): правила очистки БД.
### DatabaseCleanupConfig (nested)
- `enabled` (boolean, required)
- `tables` (array[TableCleanupRule], required when enabled)
- `preserve` (array[string], optional): whitelist записей, защищённых от очистки.
### TableCleanupRule (nested)
- `name` (string, required): имя таблицы.
- `condition` (string, required): SQL WHERE-условие для идентификации тестовых записей.
### Validation Rules
- Для `profile=enterprise-clean` поля `prohibited_categories` и `allowed_sources` обязательны.
- При `database_cleanup.enabled=true` список `tables` не может быть пустым.
- `preserve` записи не могут пересекаться с `condition` в `tables`.
---
## Relationships
1. `ReleaseCandidate` 1—N `DistributionManifest`

View File

@@ -126,6 +126,48 @@
---
## Decision 7: Вся конфигурация валидации определяется через `.clean-release.yaml` в корне репозитория
**Decision**
Ввести единый конфигурационный файл `.clean-release.yaml` в корне репозитория, определяющий:
- `profile` и `scan_mode` (repo | build | docker);
- `prohibited_categories` и `prohibited_paths` — классификация запрещённых артефактов;
- `allowed_sources` — список допустимых внутренних endpoint'ов (glob-паттерны);
- `ignore_paths` — исключения из сканирования;
- `database_cleanup` (tables + preserve) — правила очистки БД от тестовых данных.
**Rationale**
Централизация конфигурации в одном файле обеспечивает прозрачность и версионируемость правил. Владелец проекта явно контролирует политику clean-поставки через декларативный конфиг, что снижает операционные ошибки.
**Alternatives considered**
- Хранение правил в БД: отклонено — усложняет версионирование и аудит policy drift.
- Отдельные файлы для каждой секции: отклонено — фрагментация ухудшает обзорность и повышает вероятность рассинхронизации.
- Hardcode в коде: отклонено — нарушает принцип конфигурируемости и делает проект-специфичные правила невозможными.
---
## Decision 8: Очистка БД от тестовых пользователей и демо-данных — обязательная стадия
**Decision**
Добавить стадию `database_cleanup` в compliance pipeline. Правила очистки задаются в секции `database_cleanup` файла `.clean-release.yaml`:
- `tables` — список таблиц с SQL-условиями для удаления тестовых записей;
- `preserve` — whitelist записей, которые MUST быть сохранены (напр. системный admin).
**Rationale**
Одной файловой очистки недостаточно: тестовые пользователи (`test_user`, `sample_analyst`) и демо-дашборды в БД являются таким же нарушением enterprise clean-профиля, как наличие тестовых файлов в дистрибутиве.
**Alternatives considered**
- Только предупреждение без очистки: отклонено — не обеспечивает SC-001 (100% отсутствие тестовых данных).
- Автоматическая очистка по паттернам имён: отклонено — высокий риск ложных удалений без явного whitelist.
---
## Open Clarifications Status
По итогам Phase 0 `NEEDS CLARIFICATION` не осталось: все критичные решения по scope, security/policy и UX зафиксированы.
По итогам Phase 0 + speckit.clarify (2026-03-04) все `NEEDS CLARIFICATION` сняты:
- Режимы ввода: 3 режима (папка, репозиторий, Docker-образ) — FR-015;
- Классификация артефактов: `.clean-release.yaml` — FR-016;
- Определение внутренних источников: `allowed_sources` в конфиге — FR-017;
- Область сканирования NO_EXTERNAL_ENDPOINTS: все текстовые файлы — FR-018;
- Очистка БД: секция `database_cleanup` — FR-019;
- Структура конфига: полная схема зафиксирована — FR-020.

View File

@@ -95,6 +95,12 @@
- **FR-012**: Документация MUST включать отдельный регламент изолированного развертывания, включая требования к внутренним серверам ресурсов и действия при недоступности внутренних источников.
- **FR-013**: Документация MUST чётко разделять сценарии development и enterprise clean, чтобы исключить случайное использование внешних интернет-ресурсов в enterprise-контуре.
- **FR-014**: Система MUST вести аудитный журнал этапов подготовки, проверки и выпуска clean-поставки, включая результаты контроля изоляции от внешнего интернета.
- **FR-015**: Валидатор MUST поддерживать три режима ввода артефактов: (A) указанная папка сборки (CLI-аргумент), (B) рекурсивное сканирование файлов текущего репозитория, (C) Docker-образ или архив поставки (.tar.gz). Режим указывается при запуске.
- **FR-016**: Классификация артефактов (включения/исключения, запрещённые категории) MUST определяться через внешний конфигурационный файл `.clean-release.yaml` в корне репозитория, явно задаваемый владельцем проекта.
- **FR-017**: Допустимые внутренние источники ресурсов MUST определяться в секции `allowed_sources` файла `.clean-release.yaml` с glob-паттернами. Любой endpoint, не подпадающий под указанные паттерны, является нарушением политики изоляции.
- **FR-018**: Стадия `NO_EXTERNAL_ENDPOINTS` MUST сканировать все текстовые файлы (включая код, конфиги, скрипты) на наличие URL/хостов и сверять каждый найденный endpoint с `allowed_sources`.
- **FR-019**: Процесс clean-подготовки MUST включать стадию очистки БД от тестовых пользователей и демо-данных. Правила очистки (таблицы, условия, исключения) задаются в секции `database_cleanup` файла `.clean-release.yaml`.
- **FR-020**: Структура `.clean-release.yaml` MUST включать секции: `profile`, `scan_mode`, `prohibited_categories`, `prohibited_paths`, `allowed_sources`, `ignore_paths`, `database_cleanup` (с подсекциями `tables` и `preserve`).
### Key Entities *(include if feature involves data)*
@@ -104,6 +110,7 @@
- **Compliance Check Report**: Результат проверки соответствия с итоговым статусом, списком нарушений, ссылкой на релиз-кандидат и метаданными аудита.
- **Distribution Manifest**: Зафиксированный состав итогового дистрибутива для контроля полноты, воспроизводимости и дальнейшего аудита.
- **Isolated Deployment Runbook**: Документированная операционная последовательность для развертывания и восстановления в изолированном контуре.
- **Clean Release Config** (`.clean-release.yaml`): Единый конфигурационный файл в корне репозитория, определяющий правила классификации артефактов, допустимые источники, правила очистки БД и режим сканирования.
## Success Criteria *(mandatory)*
@@ -123,3 +130,13 @@
- Для продукта допустимо формальное разделение профилей на development и enterprise clean в рамках единого релизного процесса.
- Базовая первичная инициализация системы без демо-данных остаётся обязательной и должна сохраняться в clean-поставке.
- Роли владельца релиза и инженера сопровождения назначены и несут ответственность за прохождение проверок и соблюдение регламента.
## Clarifications
### Session 2026-03-04
- Q: Что именно сканирует валидатор — папку сборки, файлы репозитория, Docker-образ или JSON-манифест? → A: Поддерживаются три режима: (A) папка сборки через CLI-аргумент, (B) рекурсивное сканирование файлов репозитория, (C) Docker-образ или архив поставки.
- Q: Как определяются запрещённые категории артефактов — по паттернам пути, расширению, содержимому или конфигу? → A: Через внешний конфигурационный файл `.clean-release.yaml` в корне репозитория, где владелец явно перечисляет включения и исключения.
- Q: Что считается «внутренним источником» — точное совпадение хоста, доменные суффиксы или конфиг? → A: Определяется в `.clean-release.yaml` — секция `allowed_sources` с glob-паттернами.
- Q: Что сканирует стадия NO_EXTERNAL_ENDPOINTS — конфиги, код или зависимости? → A: Все текстовые файлы, включая код (.py, .js, .svelte) — поиск URL/хостов и сверка с allowed_sources.
- Q: Какова структура `.clean-release.yaml` и включает ли очистку БД? → A: Подтверждена полная структура с секциями `profile`, `scan_mode`, `prohibited_categories`, `prohibited_paths`, `allowed_sources`, `ignore_paths`, `database_cleanup` (tables + preserve).

View File

@@ -18,7 +18,7 @@
- [X] T001 Create feature package skeleton for clean release modules in `backend/src/services/clean_release/__init__.py`
- [X] T002 [P] Create clean release domain models module in `backend/src/models/clean_release.py`
- [X] T003 [P] Create clean release API route module placeholder in `backend/src/api/routes/clean_release.py`
- [X] T004 [P] Create TUI script entrypoint placeholder in `backend/src/scripts/clean_release_tui.py`
- [X] T004 [P] Implement full interactive ncurses TUI script in `backend/src/scripts/clean_release_tui.py`
- [X] T005 Register clean release router export in `backend/src/api/routes/__init__.py`
---