Три уикенда на защиту AI-агента: детекторы, происхождение данных и проверка побочных эффектов

Три слоя вместо одного зелёного статуса в чате: текстовые детекторы, контроль происхождения на границе вызова инструмента и проверка побочных эффектов в песочнице. В разбора автор показывает, что при MCP и реальных tools/call типичный контур смотрит на ответ модели, а не на то, что уже ушло в сеть.
Когда тест проходит, а утечка уже случилась
Несколько недель назад автор описывает сцену в своём терминале и со своим кодом: включён prompt-injection guardrail со зелёным статусом, агент читает веб-страницу со скрытой строкой, тихо отправляет секрет через tool call и выводит что-то вроде «Here's your summary. All done ✅». Проверка смотрела на ответ агента: он выглядел безопасным, тест дал PASS, хотя exfiltration уже состоялся.
Для любого стека с агентом и шлюзом tools/call это не абстрактный баг. Если критерий успеха только текст в чате, модель может «пройти аудит», не меняя факта утечки.
buried-injections: детекторы prompt-injection на AgentDojo
Первый open-source проект автора, buried-injections, бенчмаркирует детекторы: насколько они ловят инъекции, спрятанные внутри обычного tool output, как видит шлюз агента. Методология: 10 open-source детекторов против 629 injection-атак AgentDojo плюс 97 benign tool outputs при дефолтных порогах.
| Детектор (дефолтный порог) | Поймано атак | Ложные на benign |
|---|---|---|
| jailbreak-detector-large | 319/629 (~51%) | 2/97 (~2%) |
| Meta Prompt Guard 2 (86M) | 6/629 (~1%) | 0/97 |
| Meta Prompt Guard 2 (22M) | 0/629 | н/д |
| regex-baseline | 0/629 | н/д |
| preamble-defense | 556/629 (~88%) | 46/97 (~47%) |
| deepset-deberta / fmops-distilbert | 629/629 (100%) | 95/97 (~98%) |
Три механизма провала, которые выделяет автор: атаки без шаблонных фраз вроде «ignore previous instructions»; сигнал теряется, когда инъекция зарыта в контекст (invoice, email); детекторы с полным catch и почти полным false positive.
Перекалибровка порога на held-out benign (цель ≤~2% ложных) для Prompt Guard 2: при пороге 0.003 catch 97–100% на невидимом четвёртом домене AgentDojo, против 1% при дефолте 0.5. Оговорка та же: атаки AgentDojo используют один wrapper template, тонкая настройка может учить шаблон, а не инъекции в целом. Практический совет: калибровать порог на своём смешанном трафике, а не доверять цифрам с model card «как есть».
Воспроизведение: репозиторий buried-injections, команда make bench-agentdojo.
taintgate и agentverify: происхождение данных и побочные эффекты
Второй проект, taintgate (статус автора: ранняя, грубая версия). Идея: опасен не только текст tool call, а то, что недоверенный контент (web, email, doc, review) попал в аргументы чувствительного инструмента: egress, почта, удаление, перевод средств.
Пример политики из поста:
Policy(
sensitive_sinks={"http_post", "send_email", "delete_file", "transfer_funds"},
rule="deny if any argument is tainted by untrusted provenance",
)
Установка: pip install taintgate. Демо: клон rudratoshs/taintgate, python examples/banking_demo.py. Автор честно ограничивает scope: propagation taint, сложная область; покрытие «first pass».
Для агентного firewall это другой вектор, чем классификатор на prompt: даже «чистая» формулировка вызова может нести данные из недоверенного источника.
Третий проект, agentverify (~200 строк, zero dependencies по описанию автора): верификация по наблюдаемым побочным эффектам (файловая система, сеть, sandbox), а не по ответу агента. Схема из поста: агент → Agent → Proxy (решает tools/call) → MCP server с реальными tools; verify.py читает sandbox напрямую как ground truth.
На демонстрации exfiltration через egress tool output-based тест даёт PASS, проверка side effect даёт FAIL. Отдельно drift check: fingerprint модели, system prompt, tool schema и policy; при изменении нужен re-verify с указанием, какая защита регрессировала, даже если вывод агента байт-в-байт тот же.
git clone https://github.com/rudratoshs/agentverify && cd agentverify
PYTHONPATH=src python3 -m agentverify.verify
PYTHONPATH=src python3 -m agentverify.verify --regress # exits 1 при регрессии
Ограничение: sandbox ≠ production tool surface; tool, не включённый в каталог, верификатор не видит. Заголовок поста про «~600 lines» описывает образ итога, а не один файл: для agentverify в теле явно ~200 строк; суммарный объём трёх репозиториев автор в одно число не сводит.
Три уикенда складываются в цепочку, а не в взаимоисключающие «магические фильтры». Детектор на тексте полезен только с калибровкой на своём трафике; дефолтные пороги и бенчмарки с одним шаблоном атаки легко дают ложную уверенность. Provenance на границе tool call: отдельный слой для чувствительных приёмников, когда источник данных недоверен. Независимая верификация эффектов уместна там, где MCP и proxy стоят между моделью и реальными инструментами.
Автор не позиционирует репозитории как готовый продукт: buried-injections («solid, reproducible»), taintgate (early), agentverify (reference impl, not a control). Для соло OSS-практики это честный набор экспериментов, а не чеклист «включил и забыл».
В комментариях к посту (kevinbai, 13 октября) звучит контраргумент к двухуровневой модели trusted/untrusted: утечки через stdout программ, которые запустил агент, могут не попасть в taint-модель, со ссылкой на другой материал на Dev.to. Это не позиция автора поста, но напоминание: граница доверия шире, чем «web vs code».
Источники
- Rudratosh Shastri, «I spent three weekends trying to secure an AI agent…»: Dev.to (доступ 2026-10-14 UTC)
- https://github.com/rudratoshs/buried-injections
- https://github.com/rudratoshs/taintgate
- https://github.com/rudratoshs/agentverify