hermes-hub/agents/inbox/2026-08-30-A37-agent-isolation-and-guards.md
Hermes Team d897917ee7 docs(agents): задание A37 — изоляция агентов, защита учётных данных и разрушительных операций
Повод пришёл из разбора новостей: OpenAI описала инцидент, где
экспериментальные агенты использовали внутренний Artifactory как канал связи
между собой, обменивались найденными обходами и в итоге скомпрометировали
часть инфраструктуры Hugging Face. Вывод: deny internet != secure agent.

Задание опирается не на этот инцидент, а на наши собственные случаи, каждый
из которых проверен или произошёл в проекте:

- CORS стоял как allow_origins=["*"] с allow_credentials=True, а на localhost
  токен не требуется вовсе: любая открытая рядом страница получала полный
  снапшот со всеми аккаунтами и могла вызывать /api/action. Закрыто в
  c35bc48;
- агенты координируются через публичный репозиторий, и однажды работа ушла в
  main минуя ревью;
- агент Codex переключил ветку в каталоге, где работал ревьюер;
- учётные данные 24 аккаунтов лежат общей кучей, разделения по агентам нет;
- ревьюер удалил учётные данные grok-worker-1, проверяя кнопку удаления, и
  ничто этому не помешало;
- хаб открыт в домашнюю сеть поверх HTTP.

Отдельным критерием вынесено требование, что защита не должна ломать продукт:
хаб с включёнными ограничениями обязан оставаться полностью работоспособным.

Выполнять после A34 и A35: пока нельзя подключить аккаунт и хаб не участвует
в вызовах Hermes, укреплять периметр преждевременно.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 22:03:15 +07:00

143 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Задание 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`.