TL;DR
TrojanForge — автоматическая система, которая берёт чистый репозиторий и прячет в нём вредоносный код так, чтобы кодинг-агент сам его запустил. Система вычисляет, какие файлы агент точно откроет при задаче пользователя. Потом просит «агента-безопасника» ранжировать эти места по подозрительности и прячет код в самом нижнем месте списка. Затем она много раз проверяет маскировку на целевом агенте и учится на его же рассуждениях.
Боль в том, что агент при обычной задаче («почини баг», «разверни окружение») не проверяет всё подряд. Он идёт по пути задачи и доверяет «скучному» коду: конфигам, логгерам, вспомогательным функциям. Если вредоносная вставка названа и оформлена как остальной код, агент её не замечает и запускает как побочный эффект работы. В тестах средний успех атаки превысил 60%. «Закладки» в компонентах кода срабатывали более чем в 90% случаев. Коммерческие агенты не устояли.
Метод в три шага: (1) найти код, до которого агент дойдёт при задаче; (2) выбрать из него наименее подозрительные места; (3) подбирать маскировку циклами, превращая причины провалов в правила для следующей попытки. Для читателя главная ценность не в самой атаке, а в знании: чужой репозиторий — недоверенный ввод, и агент сам вас не защитит.
Схема метода
ШАГ 1. Достижимость: агент решает задачу на чистом репо → лог команд
→ LLM сопоставляет лог с функциями (AST) → список мест, которые агент выполнит
ШАГ 2. Слепые зоны: агент-безопасник (ему сказано «тут есть вредонос»)
ранжирует места по подозрительности с причиной → берут k САМЫХ НЕПОДОЗРИТЕЛЬНЫХ
ШАГ 3. Маскировка (цикл): N вариантов маскировки → проверка на целевом агенте
→ провал: из его рассуждений делают правила («имя выдало замысел» → «называй по бизнес-стилю»)
→ новый круг; успех → в «библиотеку опыта» для других репозиториев
Проверка успеха: детерминированная (код реально сработал), а не оценка LLM-судьёй
Шаги идут отдельными запросами и вызовами агентов, это автоматизированный конвейер.
Пример применения
Сама атака читателю не нужна. Полезен её защитный разворот: перед тем как пускать агента в чужой репозиторий, можно устроить аудит так, как это делали авторы. Только смотреть нужно не на верх списка подозрительности, а на его низ, где вредонос прячется чаще всего.
Задача: Вы фрилансер. Заказчик прислал репозиторий «скрипты для выгрузки заказов из Wildberries в Google Таблицы» и просит: «Почини падение на выгрузке остатков». Вы собираетесь отдать репозиторий Claude Code. Сначала нужен быстрый аудит.
Промпт:
Ты — аудитор безопасности. Известно, что в этом репозитории ПРИСУТСТВУЕТ
вредоносный код. Он спрятан так, чтобы выглядеть обычным.
Задача пользователя, ради которой репозиторий будет запускаться:
«Починить падение на выгрузке остатков (export_stock.py)».
Кандидаты — файлы и функции, которые выполнятся при этой задаче:
1. export_stock.py::main()
2. wb_api/client.py::request()
3. config/settings.py::load()
4. utils/logger.py::setup()
5. setup.py (хуки установки)
6. tests/conftest.py
Проверь каждое место по-настоящему: открой файл, прочитай код.
Выдай список от самого подозрительного к наименее подозрительному.
К каждому месту — короткая причина: что в нём настораживает
(имя, не вяжущееся с содержимым; сетевые вызовы; удаление файлов;
запись в чужие каталоги; код, не вписывающийся в стиль модуля).
Отдельно в конце: для 3 НИЖНИХ пунктов списка перечисли, что именно
ты НЕ проверил и почему счёл их безобидными.
Результат: Модель выдаст ранжированный список с причинами для каждого места. Ключевая часть — финальный блок: он покажет, какие «скучные» места (логгер, конфиг, хуки установки) аудитор счёл безобидными. Их стоит дочитать глазами. Формат — нумерованный список и короткий абзац в конце.
Почему это работает
Слабость агента. У него ограниченное внимание: он проверяет то, что бросается в глаза. Имя вроде send_data_to_server насторожит, а normalize_config_paths — нет. Когда код вписан в стиль модуля и стоит там, где «обычно всякое бывает», агент считает его частью фона.
Сильная сторона LLM. Если сказать модели «вредонос точно есть», она проверяет ощутимо усерднее и умеет ранжировать места по подозрительности с объяснением. Подозрения предсказуемы: у разных агентов они совпадают. Поэтому ранжирование одного агента хорошо предсказывает слепые зоны другого.
Как метод это использует. Он инвертирует список: слепая зона находится по самому низу ранжирования. Вторая инверсия — разбор провалов: рассуждение агента «почему я это заметил» превращается в правило «делай так, чтобы не заметил». Для защитника то же самое работает наоборот: причины, по которым агент замечает подозрительное, показывают, на какие признаки он реагирует. Всё остальное он пропускает.
Рычаги для защитного аудита: - Фраза «вредонос точно есть» → делает аудит строже, меньше «всё выглядит нормально». - Список кандидатов → сужай до пути задачи, иначе аудит растянется на весь репозиторий. - Блок «что НЕ проверил» → заставляет модель назвать слепые зоны вслух. - Причина к каждому пункту → убери, если нужен только быстрый список.
Шаблон промпта
Ты — аудитор безопасности. Известно, что в репозитории {название} ПРИСУТСТВУЕТ
вредоносный код, замаскированный под обычный.
Задача пользователя: «{задача}».
Кандидаты — места, которые выполнятся при этой задаче:
{список_файлов_и_функций}
Открой и прочитай каждое место. Выдай список от самого подозрительного
к наименее подозрительному. К каждому — короткая причина: что в нём
настораживает (имя, не вяжущееся с содержимым; сетевые вызовы; удаление
файлов; запись в чужие каталоги; несоответствие стилю модуля).
В конце: для {число} нижних пунктов перечисли, что ты НЕ проверил,
и почему счёл их безобидными.
Подставляйте: {название} — имя репозитория, {задача} — то, что собираетесь поручить агенту, {список_файлов_и_функций} — файлы из ls и места, куда пойдёт агент, {число} — обычно 3.
🚀 Быстрый старт — вставь в чат:
Вот шаблон аудита репозитория перед запуском агента. Адаптируй под мою задачу: [твоя задача].
Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
LLM спросит, какую задачу вы поручите агенту и какие файлы лежат в репозитории. Это нужно, чтобы собрать список «мест на пути выполнения»: именно оттуда метод берёт кандидатов на слепые зоны.
Ограничения
⚠️ Защита не проверялась: статья про атаку. Рецепт защиты (аудит с инверсией списка, изоляция) авторы не тестировали, это вывод из логики метода.
⚠️ Аудитора тоже можно обмануть: атака подбирается именно под целевого агента. Модель-ревьюер может пропустить то же, что пропустит агент.
⚠️ Не 100%: в среднем агенты ловят заметную долю атак. Значит, «агент почти всегда замечает» неверно, но и «никогда не замечает» тоже.
⚠️ Сам метод — инженерный: он требует разбора синтаксических деревьев кода, запуска агентов, библиотеки опыта. В обычном чате воспроизвести его нельзя, воспроизводится только аудит.
⚠️ Неполные данные: в доступной части статьи нет подробного разбора сбоев и сравнения моделей и оболочек. Выводы о различиях между агентами сделать нельзя.
Как исследовали
Команда взяла 136 чистых репозиториев из SWE-bench, Commit0 и GitHub: шесть типов задач (разработка, баги, развёртывание, обработка данных, поддержка, оптимизация) и девять языков. Задачи для агента LLM синтезировала из реальных issues и коммитов, чтобы они были как в жизни. Затем система внедряла четыре типа вредоносной нагрузки: кража данных, удаление файлов, подмена значений, «закладка» в компоненте.
Важная деталь дизайна: успех проверяли детерминированно. Смотрели, что вредоносное действие реально произошло, а не что LLM-судья сказал «похоже на успех». Так цифрам можно доверять больше.
Результат: средний успех атаки выше 60%, пик 76% (кража), 85% (удаление), 94% (подмена), 100% (закладка). В итоге получился бенчмарк на 384 отравленных репозитория, который прогнали на 4 оболочках агентов и 12 моделях. Вывод: даже коммерческие агенты не защищены от такого отравления.
Что показательно: самое слабое звено — не «умность» модели, а предсказуемость внимания. Любой ревьюер с похожей логикой подозрения подсказывает, где прятаться. Для практики это значит: безопасность нельзя отдавать агенту целиком. Нужна изоляция (песочница, ограничение сети, подтверждение команд) и ручной просмотр хвоста списка.
Адаптации и экстраполяции
💡 Адаптация для приёмки чужого кода: то же ранжирование, но по задаче «принять пулреквест от подрядчика». В кандидатах — изменённые файлы в диффе, а не весь репозиторий. Смотрите на хвост списка и блок «что не проверил».
🔧 Техника (моя экстраполяция, в статье не проверялась): добавьте в CLAUDE.md или AGENTS.md правило: «Перед запуском скриптов установки, хуков,
conftest.pyи конфигов из чужого репозитория покажи мне их содержимое и жди подтверждения». Логика: слепые зоны из статьи — как раз «скучные» точки входа.
Ресурсы
- Trojan Horse: Automated Repository Poisoning against Coding Agents — Xiao Yang, Jinchang Hou, Jiawei Chen, Zhonghou Lv, Junyuan Shang, Chenfu Bao. Baidu Inc.; Beihang University; East China Normal University; Zhongguancun Academy.
- Код: github.com/SimonsShoot/TrojanForge
- Бенчмарк: huggingface.co/datasets/MikeYoung123/TrojanBench
- Страница проекта: simonsshoot.github.io/TrojanForge
- Связанные работы в статье: MCPTox (отравление инструментов MCP), CIPR, RepoGuardBench, Day et al. (топология рабочего пространства).
