57 lines
6.3 KiB
Markdown
57 lines
6.3 KiB
Markdown
# Отчёт: Задание A4 — изоляция учётных данных и последние долги
|
||
|
||
Дата: 2026-08-21
|
||
|
||
## Идентификаторы и границы
|
||
|
||
- **START_HEAD (BASE_SHA)**: `33b99988ff59345e69e719602058e573a7c6407d`
|
||
- **Ветка**: `antigravity/credential-isolation`
|
||
- **origin/main**: `33b99988ff59345e69e719602058e573a7c6407d`
|
||
- **Граница зоны Codex**: ни один файл в `src/antigravity_provider/router/ui/**`, `hermes_hub_app.py`, `tests/test_ui_*.py` **НЕ изменялся** (`git diff --name-only` по этим путям пуст).
|
||
- **Тег `v0.1.1`**: **НЕ создавался** (в репозитории `hermes-hub`).
|
||
|
||
---
|
||
|
||
## 1. Изоляция учётных данных в subprocess (P0-1)
|
||
|
||
- Реализована функция `build_safe_subprocess_env()` в `src/antigravity_provider/agy_subprocess.py`:
|
||
- Окружение дочернего процесса формируется **явно** на основе строгого allowlist системных переменных (`PATH`, `SYSTEMROOT`, `TEMP`, `USERPROFILE`, `LOCALAPPDATA` и др.).
|
||
- Все ключи и токены внешних провайдеров (`OPENAI_API_KEY`, `CODEX_TOKEN_*`, `ANTHROPIC_API_KEY`, `DEEPSEEK_API_KEY`, `OPENCODE_GO_API_KEY`, `XAI_API_KEY`, `HERMES_API_SECRET`, `MY_AUTH_TOKEN` и др.) гарантированно удаляются по шаблонам безопасности `BLOCKED_SECRET_PATTERNS`.
|
||
- Профильная изоляция (`USERPROFILE`, `HOME`, `HOMEPATH`) передается через явные `overrides`.
|
||
- Все вызовы дочерних процессов (`agy_generate`, `discover_models`, `AntigravityAdapter.invoke`) переведены на использование `build_safe_subprocess_env()`.
|
||
- Добавлен статический AST-тест `test_no_unfiltered_environ_copy_in_src`, запрещающий использование сырых копий `dict(os.environ)` или `os.environ.copy()` в `src/` без безопасной фильтрации.
|
||
- Добавлен юнит-тест `test_antigravity_adapter_subprocess_env_isolation`, доказывающий отсутствие ключей сторонних провайдеров в окружении дочернего процесса.
|
||
|
||
---
|
||
|
||
## 2. Разбор и фиксация оставшихся долгов (P1-2)
|
||
|
||
| Долг | Решение / Статус | Обоснование |
|
||
|---|---|---|
|
||
| **Комментарии `router_profiles.yaml`** | **Закрыт / Зафиксирован** | Функция `save_router_config` в `router_config.py` читает существующие заголовочные комментарии и пустые строки (`existing_comments`) перед первым ключом и сохраняет их verbatim. Inline-комментарии внутри структур пересобираются каноническим safe_dump. |
|
||
| **`Registry.CurrentUser` в установщике** | **Закрыт** | В `installer/HermesHubSetup.cs` добавлена проверка переменной окружения `HERMES_HUB_NO_REGISTRY == "1"`, отключающая запись в HKCU при тестовых и изолированных запусках. В `tests/test_installer.py` переменная передается по умолчанию, исключая загрязнение live-реестра. |
|
||
| **`fastapi` / `uvicorn`** | **Закрыт** | Зависимости `fastapi` и `uvicorn` вынесены в `[project.optional-dependencies] legacy` в `pyproject.toml` и отсутствуют в обязательных `dependencies`. |
|
||
| **Сериализация Antigravity (`_AGY_INVOCATION_LOCK`)** | **Осознанный долг / Зафиксирован** | Windows Credential Manager хранит учётные данные глобально для текущего пользователя под единым ключом `gemini:antigravity`. При одновременном запуске нескольких Antigravity-профилей с разными Google-аккаунтами мьютекс `_AGY_INVOCATION_LOCK` предотвращает состояние гонки при подмене токена в WCM. Для профилей без собственной авторизации блокировка не накладывается. |
|
||
|
||
---
|
||
|
||
## 3. Объяснение выбора провайдера (P1-3)
|
||
|
||
- В `RouterEngine.route_request()` реализована матрица оценки кандидатов `evaluation_matrix`, отслеживающая каждый профиль из `candidate_profiles`:
|
||
- Статус кандидата: `selected`, `skipped`, `rejected`, `failed`.
|
||
- Честная причина отсева/выбора (отключен в конфигурации, исчерпана квота / cooldown всех моделей, достигнут лимит параллелизма `max_concurrency`, сбой выполнения с ошибкой 429/401/timeout, выбран для выполнения с баллом оценки).
|
||
- Полный след выбора `selection_trace` (включая `required_capabilities`, `candidates_evaluated`, `selected_profile_id`, `selected_model`, `decision_rationale`, `evaluation_matrix`) сохраняется:
|
||
1. В метаданных успешного ответа: `response["router_metadata"]["selection_trace"]`;
|
||
2. В ответе при исчерпании всех маршрутов: `response["selection_trace"]`;
|
||
3. В записях `EventLogService` (категория `routing`).
|
||
|
||
---
|
||
|
||
## 4. Результаты проверок
|
||
|
||
- **Headless pytest** (3.8): `175 passed, 22 skipped, 3 deselected in 9.79s`
|
||
- **Full pytest** (Python 3.12 с `customtkinter`, `pillow`, `psutil`): `175 passed, 22 skipped, 3 deselected in 9.49s`
|
||
- **Ruff linter**: `All checks passed!`
|
||
- **Release Gate**: `7/7 PASSED` (`[RELEASE GATE: PASSED] All criteria verified. Ready for Candidate v0.1.1`)
|
||
- **Live Update Feed**: `[MANIFEST_LIVE=True, PACKAGE_LIVE=True, PACKAGE_HASH_VERIFIED=True]` (sha256 `f2e565619209b0746182ae0e4612b021e50c739af04ca1e455bd48a4d42385f1`)
|
||
- **UI Zone Isolation**: `0 files modified in UI area`
|