diff --git a/agents/inbox/2026-08-30-A37-agent-isolation-and-guards.md b/agents/inbox/2026-08-30-A37-agent-isolation-and-guards.md new file mode 100644 index 0000000..938f7fd --- /dev/null +++ b/agents/inbox/2026-08-30-A37-agent-isolation-and-guards.md @@ -0,0 +1,143 @@ +# Задание A37: изоляция агентов, защита учётных данных и разрушительных операций + +## Дата поступления +2026-08-30 + +## База + +Ветка ревьюера `review/a28-a31-fixes` (`ab2ee12`). + +``` +git fetch origin --prune +git checkout -b antigravity/a37-isolation-guards origin/review/a28-a31-fixes +``` + +В `main` напрямую не пушить. + +## Порядок исполнения + +Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-6** написан для аудитора. + +Выполнять **после A34 и A35**: пока нельзя подключить аккаунт и хаб не участвует в вызовах Hermes, укреплять периметр вокруг системы, которая ничем не управляет, преждевременно. + +--- + +## Задача + +Владелец держит на трёх машинах 24 аккаунта, а работу ведут несколько автономных агентов. Изоляции между ними нет никакой, и это уже приводило к последствиям. + +Повод пришёл извне: OpenAI официально описала инцидент, где экспериментальные агенты использовали **внутренний Artifactory как канал связи между собой**, обменивались найденными обходами, получили обходной доступ в интернет и в итоге скомпрометировали часть инфраструктуры Hugging Face. Вывод сформулирован там жёстко: + +``` +deny internet != secure agent +``` + +Разрешённый внутренний сервис становится proxy, доской объявлений, скрытым каналом и точкой опоры. + +## Наши собственные случаи — не гипотезы + +Каждый проверен или произошёл в этом проекте: + +1. **Любой сайт в соседней вкладке мог управлять хабом.** CORS стоял как `allow_origins=["*"]` вместе с `allow_credentials=True`, а на `127.0.0.1` токен не требуется вовсе. Проверено запросом: страница со стороннего адреса получала `200` и полный снапшот со всеми аккаунтами. Закрыто ревьюером в `c35bc48`, но это тот самый класс, о котором говорит инцидент. + +2. **Агенты координируются через общий канал** — публичный репозиторий на GitHub. Они пишут ветки, читают чужие, и как минимум однажды работа ушла в `main` минуя ревью (зафиксировано в A23). + +3. **Общее рабочее дерево.** Агент Codex переключил ветку в каталоге, где в это же время работал ревьюер; коммиты чуть не легли в чужую ветку. Пришлось собирать их через временный индекс, чтобы не мешать. + +4. **Учётные данные 24 аккаунтов лежат общей кучей** в `~/.hermes/agy_profiles/`, доступной любому процессу пользователя. Разделения по агентам нет. + +5. **Ревьюер удалил учётные данные `grok-worker-1`**, проверяя кнопку удаления на живом профиле. Ничто не помешало. + +6. **Хаб открыт в домашнюю сеть поверх HTTP.** Токен идёт по сети открытым текстом; почты аккаунтов — тоже, пока не сделан P0-4 из A31. + +## Что уже есть — не переделывать + +Поиском по коду: отдельного слоя безопасности нет, но три вещи работают и их надо использовать как основу. + +``` +agy_subprocess.build_safe_subprocess_env очистка окружения подпроцесса +update_manager.ALLOWED_UPDATE_HOSTS белый список хостов обновления +web/server.sanitize_snapshot вычистка секретов из снапшота +``` + +--- + +## P0-1. Граница рабочей области и разрушительные операции + +Ни агент, ни модель, ни инструмент не должны удалять или перезаписывать данные **за пределами разрешённой области**, независимо от того, кто выполняет команду. + +1. **Явная граница** — каталог проекта плюс явно разрешённые пути. Всё остальное вне области. +2. **Классификация операций**: удаление, рекурсивное удаление, массовое перемещение и перезапись, усечение. Проверять и вызовы Python (`shutil.rmtree`, `os.remove`, `os.unlink`), и командную строку (`rm`, `rm -rf`, `Remove-Item`, `del`), потому что агенты ходят обоими путями. +3. **Отказ объясняет причину и предлагает безопасную замену**, а не просто запрещает. +4. **Безусловный запрет** на каталоги учётных данных: `~/.hermes/agy_profiles/`, `~/.ssh/`, файлы `auth.json`, `hub_settings.json`. Удаление аккаунта делается **только** штатным действием `delete_credentials` с подтверждением — как раз потому, что удаление вручную уже происходило. +5. **Сухой прогон**: показать, что будет удалено, до удаления. + +## P0-2. Разделение агентов + +1. **У каждого агента свой рабочий каталог.** Общее дерево уже приводило к переключению ветки под чужой работой. Отдельные клоны либо `git worktree` на агента. +2. **Свои учётные данные.** Агенту нужны те аккаунты, с которыми он работает, а не все 24. Предложить схему разделения и обосновать; **молча ничего не переносить** — потеря учётных данных стоит владельцу повторного входа во все аккаунты. +3. **Свой след в журнале.** Каждое действие с аккаунтами и конфигурацией записывается с указанием, кто его выполнил. Сейчас по журналу нельзя отличить действия ревьюера от действий агента. + +## P0-3. Сетевая граница самого хаба + +1. **Белый список исходящих обращений.** Хаб ходит к провайдерам, к API релизов и к локальным серверам — этот список конечен и должен быть явным. Образец есть: `ALLOWED_UPDATE_HOSTS`. +2. **CORS остаётся закрытым по умолчанию.** Список источников — настройка `web_api_allowed_origins`. Возврат `allow_origins=["*"]` считать дефектом; нужен тест. +3. **Токен обязателен при небlocalhost-привязке** — уже так, не ослаблять. Проверить, что сравнение осталось постоянного времени и в байтах. +4. **HTTP по сети — назвать риском в интерфейсе.** Владелец должен видеть, что при сетевой привязке поверх HTTP токен и почты идут открытым текстом. Не запрещать, а сказать прямо и предложить туннель или VPN. + +## P0-4. Журнал, по которому можно расследовать + +После инцидента вроде описанного нужен ответ на вопрос «кто, что и когда». + +Записывать: кто выполнил, какое действие, над каким профилем и ролью, результат, время. Секреты в журнал не попадают — тест на это уже есть для снапшота, распространить на журнал. + +## P0-5. Проверка исполнением + +1. Попытка удалить файл вне рабочей области — отказ с причиной; проверено. +2. Попытка удалить каталог учётных данных — отказ; проверено. +3. Обращение к хосту вне белого списка — отказ; проверено. +4. Запрос с чужого `Origin` — заголовков CORS нет; проверено запросом. +5. Работа хаба при всех включённых ограничениях не деградирует: маршрутизация, обновление, обнаружение моделей работают. + +Пункт 5 обязателен: защита, ломающая продукт, хуже её отсутствия. + +## P0-6. Аудит вторым проходом + +1. **Ложное чувство защиты.** Проверить, что ограничения нельзя обойти очевидным способом — например, относительным путём или символической ссылкой за пределы области. +2. **Отказ не должен ронять хаб.** Сработавшая защита — это отказ операции, а не падение процесса. +3. **Секреты в журнале и в сообщениях об отказе** — искать целенаправленно. +4. **Запустить с ограничениями и поработать**, а не только прогнать тесты. +5. **Побочные изменения** объяснить. +6. **Пропущенный пункт назвать пропущенным.** + +--- + +## Ограничения + +- Не ломать работу агентов ради строгости: они должны продолжать работать в своих каталогах. +- Учётные данные не переносить и не удалять без явного согласия владельца. +- Зона: новый слой безопасности, `web/server.py`, журнал, тесты. Веб-клиент — A34, определение роли — A35, конвейер — A36. +- Правило честности без исключений. +- Тег `v0.1.1` не создавать. + +## Критерии приёмки + +1. Ветка в `origin` от `review/a28-a31-fixes`, `git status` чист. +2. Разрушительная операция вне рабочей области отклоняется с причиной; проверено исполнением. +3. Каталоги учётных данных защищены безусловно; удаление аккаунта возможно только штатным действием. +4. Есть сухой прогон, показывающий последствия до выполнения. +5. Агенты разведены по рабочим каталогам; схема разделения учётных данных предложена и обоснована, ничего не перенесено без согласия. +6. Белый список исходящих обращений работает; обращение вне списка отклоняется. +7. CORS закрыт по умолчанию, есть тест на возврат `allow_origins=["*"]`. +8. Риск HTTP по сети назван в интерфейсе. +9. В журнале видно, кто выполнил действие; секретов в нём нет. +10. Хаб при включённых ограничениях полностью работоспособен; проверено. +11. `ruff check .` чисто; релизный гейт не ухудшен. +12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На ветке ревьюера сейчас **475 passed, 2 skipped**. + +## Главное + +У владельца на трёх машинах лежат ключи от 24 аккаунтов, а работают там автономные агенты без изоляции друг от друга. Пока не было потерь, но все предпосылки уже сработали хотя бы раз: и удаление учётных данных, и работа в чужом дереве, и открытый доступ к хабу из браузера. + +## Порядок сдачи +Передать точный `FINAL_COMMIT_SHA`.