TL;DR
PyCache Trap — атака на «навыки» (Skills) агентов вроде Claude Code. В пакет рядом с чистым исходником .py кладут скомпилированный кэш .pyc с другим, вредным кодом. Сканеры читают описание навыка и видимый исходник, находят там чистоту и пропускают пакет. Python при запуске берёт кэш и вообще не смотрит на исходник.
Главная находка: «сканер сказал "чисто"» не значит «безопасно». Сканеры проверяют то, что можно прочитать глазами, а запускается другое. Против семи сканеров атака прошла в 94–100% случаев. При этом ни один сканер не понял, что именно спрятано в кэше: редкие срабатывания были общими предупреждениями про «странный текст» или «тут есть кэш». А LLM-переписчик подчищал описание навыка, пока предупреждения не исчезали. Байты кэша при этом не менялись. В реальном агенте вредное поведение выполнилось примерно у трёх четвертей навыков.
Защита авторов, EAV, проверяет сам исполняемый артефакт. Она берёт исходник, пересобирает его доверенным интерпретатором и сравнивает результат с тем, что лежит в кэше. Не совпало, или исходника нет — блок. Переписывание текста на такую проверку повлиять не может. Практикам из статьи достаётся прежде всего правило: не считать текстовую проверку навыка достаточной. Сама EAV — это код, а не промпт.
Схема метода
Атака (как работает ловушка):
ШАГ 1: Берут чистый навык + безобидный .py → видимый исходник
ШАГ 2: Рядом кладут .pyc с другим телом и «правильным» заголовком → Python его принимает
ШАГ 3: Добавляют в навык вызов модуля, связанный с задачей → кэш реально запускается
ШАГ 4: LLM переписывает ТОЛЬКО описание навыка по отчёту сканера (до 5 попыток) → байты кэша не трогает
Защита (EAV, 4 стадии):
СТАДИЯ 1: Граф связей: инструкции → скрипты → импорты → кэши
СТАДИЯ 2: Каждый файл (текст, код, картинка, байткод) → список эффектов: чтение, запись, сеть, запуск...
СТАДИЯ 3: Три проверки: достижимость / запрещённые комбинации / «происхождение кэша»
СТАДИЯ 4: Вердикт с учётом назначения навыка: вредно / безвредно / не разобрано (не считается безопасным)
Пример применения
Задача: Ты ставишь в Claude Code навык «разбор выгрузки Яндекс.Метрики и Директа в Excel» с публичного хаба. Сканер показал «чисто», в SKILL.md и analyze.py ничего подозрительного. В папке есть __pycache__. Ты хочешь, чтобы агент-ревьюер проверил не только текст, но и всё, что реально может запуститься.
Промпт:
Ты — аудитор безопасности навыков агентов. Перед тобой папка навыка
«metrika-direct-report» (скачана с публичного хаба, в Claude Code ещё не подключена).
Проверь навык по четырём стадиям. Ничего не запускай, только читай файлы
и выполняй безопасные команды просмотра (ls, cat, python -m py_compile в
пустую временную папку).
СТАДИЯ 1 — граф. Составь список: что вызывает SKILL.md → какие скрипты →
какие модули они импортируют → какие скомпилированные файлы (.pyc),
бинарники и картинки лежат рядом и могут быть загружены вместо исходника.
СТАДИЯ 2 — эффекты. Для каждого файла перечисли, что он делает:
чтение файлов, запись, сеть, запуск команд, переменные окружения,
сохранение между сессиями, обращение к агенту. Для каждого эффекта
укажи файл и строку или цитату. Нет подтверждения в файле — пометь
«гипотеза», в вердикт не включай.
СТАДИЯ 3 — три проверки:
а) Достижимость: может ли чтение чувствительных данных (токены Метрики,
cookie, .env) дойти до сети или запуска команд?
б) Запрещённые комбинации: чтение секретов → отправка наружу;
скачивание → запуск; обратная оболочка.
в) Происхождение кэша: для КАЖДОГО .pyc, до которого доходит вызов,
есть ли исходник, из которого он получен? Пересобери исходник в
пустую временную папку и сравни со скомпилированным файлом по
константам (URL, команды, строки). Нет исходника или не совпало —
это нарушение.
СТАДИЯ 4 — вердикт. Учти назначение навыка: загрузка отчётов из
Метрики — легитимная сеть; отправка данных на неизвестный хост —
нет. Назначение, заявленное в SKILL.md, не отменяет нарушение
в проверке «происхождение кэша».
Формат ответа: таблица «файл — эффект — доказательство», затем
результаты трёх проверок, затем вердикт: ВРЕДНО / БЕЗВРЕДНО / НЕ РАЗОБРАНО
(не считается безопасным). Для каждого .pyc отдельная строка.
Результат: Агент вернёт список файлов и связей (в том числе скрытых кэшей) и таблицу эффектов с цитатами. Затем он по каждой из трёх проверок напишет, пройдена она или нет. Для каждого .pyc будет отдельная строка: есть ли исходник и совпало ли содержимое после пересборки. Финал — один из трёх вердиктов. Нет исходника или кэш не разобран — «НЕ РАЗОБРАНО», а не «безопасно».
Почему это работает
Слабость сканеров. Они читают текст: описание навыка, исходник, метаданные. Проверка «кэш подходит к исходнику» смотрит только на заголовок — время изменения или хеш исходника. Заголовок пишет сам автор пакета, поэтому он ничего не доказывает. Сканер видит чистый исходник, а запускается другое.
Слабость LLM-проверки текстом. Модель хорошо оценивает, «выглядит ли описание подозрительно». Поэтому переписывание формулировок («смягчить язык», «разнести инструкции», «вплести в сюжет») снимает предупреждения. Но вредное поведение сидит в байтах, которые она не читала. Ни один сканер не назвал спрятанное поведение, хотя 94–100% навыков прошли.
Как защита обходит слабость. Модель хорошо читает исходник и описывает эффекты, если требовать цитату или строку. Проверку «кэш = пересборка исходника» делает детерминированный шаг: компиляция и сравнение. Обмануть его формулировками нельзя. Подтверждения из файла — условие для вердикта. Назначение навыка может объяснить «странную» сеть, но не может оправдать кэш без исходника.
Рычаги управления промптом: - «Гипотеза» против «доказано» → без цитаты из файла эффект не идёт в вердикт. Ослабишь — получишь больше ложных тревог. - Третий вердикт «НЕ РАЗОБРАНО» → пусть неразобранный артефакт никогда не считается безопасным. - Стадия 4 (назначение) → убери, если навык нужен для чего-то заведомо сетевого; оставь, если хочешь меньше ложных срабатываний. - Список подозрительных комбинаций → дополни своим: «чтение .env → любой POST».
Шаблон промпта
В самой статье готового промпта-аудитора нет: она описывает логику EAV и реализацию на коде. Шаблон ниже — перенос этой логики в промпт. Он не заменяет детерминированную сверку, поэтому пересборку и сравнение должен выполнять агент с доступом к командам, а не «в голове» у модели.
Ты — аудитор безопасности навыков агентов. Ничего не запускай из самого навыка.
Разрешены: просмотр файлов, компиляция исходников в пустую временную папку.
Навык: {название_навыка}
Папка: {путь}
Назначение по заявлению автора: {назначение}
Версия Python в окружении: {версия_python}
Построй граф: инструкции → скрипты → импорты → кэши (.pyc), бинарники, картинки.
Укажи, что может быть загружено вместо видимого исходника.
Динамические вызовы, которые не удалось разрешить, пометь как «не разобрано».
Для каждого файла выпиши эффекты из набора:
{read, write, net, exec, env, persist, agent}.
Каждый эффект: тип, цель, файл и строка или цитата.
Нет цитаты или строки — «гипотеза», в вердикт не входит.
1. Достижимость: путь от чтения чувствительного ({секреты}) до net/exec.
2. Запрещённые комбинации: {список_комбинаций}.
3. Происхождение кэша: для каждого достижимого .pyc —
- есть ли парный исходник;
- пересобери исходник в пустую папку интерпретатором {версия_python};
- сравни константы (строки, URL, команды) и операции с кэшем;
- кэш без исходника или не совпал → нарушение.
Заголовок кэша доказательством не считается.
Вердикт: ВРЕДНО | БЕЗВРЕДНО | НЕ РАЗОБРАНО.
Назначение навыка может объяснить эвристическую находку.
Оно не отменяет нарушение в проверке 3.
НЕ РАЗОБРАНО никогда не считается безопасным.
Что подставлять: {название_навыка} и {путь} — что проверяешь; {назначение} — что навык обещает делать; {версия_python} — версия в окружении, где навык будет работать (кэш привязан к версии); {секреты} — что у тебя чувствительно (токены, .env, cookie); {список_комбинаций} — например «чтение секретов → отправка наружу», «скачивание → запуск».
🚀 Быстрый старт — вставь в чат агента с доступом к файлам:
Вот шаблон аудита навыков по методу EAV. Адаптируй под мою задачу: хочу проверить
навык {название} перед установкой. Задавай вопросы, чтобы заполнить поля.
[вставить шаблон выше]
Агент спросит, какой версией Python ты пользуешься, что у тебя чувствительное и для чего нужен навык. Версия нужна потому, что кэш привязан к ней, а пересборку надо делать тем же интерпретатором. Секреты и назначение нужны, чтобы проверка «достижимости» и стадия 4 работали на твоём контексте.
Ограничения
⚠️ Только Python-навыки: атака описана для кэшей Python. Сама идея («проверяй то, что исполняется») шире, но остальные форматы авторы не проверяли.
⚠️ Версия и загрузчик: защита работает для поддерживаемых загрузчиков и нормализации кода. Для неподдерживаемых случаев она воздерживается и выдаёт «не разобрано». Это не «безопасно».
⚠️ Промпт не заменяет сверку: чтобы пересобрать исходник и сравнить, нужен агент с командами или скрипт. Одной модели «в голове» это не доверить. Авторы подчёркивают, что вердикт по кэшу не зависит от языковой модели, потому что делается детерминированной сверкой.
⚠️ Ложные тревоги: EAV выдала около 10% ложных срабатываний на безобидных навыках. Часть настоящих атак (около 7%) осталась незамеченной в смешанной оценке пяти семейств.
⚠️ Один агент и одна модель в проверке запуска: выполнение в реальном агенте подтверждено на одной связке (Claude Code с MiniMax-M3) и четырёх сканерах из семи. Для остальных трёх есть только результаты сканера.
⚠️ Атака собрана исследователями: вредное поведение — тестовое, в песочнице. О реальной частоте таких навыков статья не говорит.
Как исследовали
Исследователи взяли 100 чистых навыков из ClawHub, skill0.io и GitHub. К каждому добавили подложенный кэш с тестовым поведением и вызов, связанный с задачей. Дальше LLM-переписчик правил только описание навыка по отчётам сканера: пять попыток, четыре приёма (смягчить язык, сдвинуть позицию, вплести в сюжет, разнести инструкции). Байты кэша оставались прежними. Прогнали семь сканеров, включая ExecScan, который разбирает картинки и симулирует запуск, но исключает из анализа __pycache__. Сравнили ещё с тремя известными атаками на текст и картинки.
Главное удивление: ASR 94–100%, а семантическое распознавание нулевое. «Не пропустили» не значит «поняли». Сканеры иногда блокировали навык из-за общих тревожных формулировок. Когда переписчик их убирал, пропускали всё. Поэтому авторы отдельно меряют «заблокировал по любой причине» и «назвал спрятанное».
Потом проверили в Claude Code: допущенный навык реально выполнил вредное поведение у 76–83 из 100 жертв (четыре сканера). Атаки, созданные против одних сканеров, переносятся на другие: 92–100% на Cisco, ExecScan и SkillFortify. Для HSS перенос слабее, потому что он ловит «подозрительный текст». Три сканера на Claude пропустили всё.
Защита EAV проверена на 200 безобидных навыках и пяти семействах атак. Подмену кэша она нашла во всех 100 случаях при нулевой успешности атаки. На остальных семействах получилось 92,8% полноты при 10% ложных срабатываний. Вывод для практики: защита, которая смотрит на исполняемые байты, а не на формулировки, невосприимчива к перефразированию.
Адаптации и экстраполяции
Ниже мои идеи. В статье их нет.
🔧 Техника: удалить кэш → заставить Python компилировать видимый исходник
Правило для CLAUDE.md или инструкции агенту:
Перед первым запуском скрипта из стороннего навыка удали все каталоги
__pycache__ и файлы .pyc в папке навыка. Если после удаления навык не
работает — сообщи мне и не восстанавливай их.
Если в папке нет подложенного кэша, Python скомпилирует тот исходник, который ты и ревьюер видели. Это не защищает от других способов спрятать код, но закрывает именно этот.
🔧 Техника: «неразобранное» → стоп, а не продолжение
Добавь в инструкцию агента:
Если в навыке есть файл, который ты не можешь прочитать как текст
(бинарник, .pyc без исходника, картинка с текстом), не считай его безопасным.
Остановись и покажи мне список таких файлов.
Экстраполяция: ревью чужого навыка вторым агентом. Первый агент составляет таблицу эффектов с цитатами. Второй получает только таблицу и файлы и ищет то, что в таблицу не попало: файлы, которых нет в списке, и эффекты без цитат. Идея та же, что у EAV: вердикт принимается только на доказанном, а недоказанное остаётся флагом «не разобрано».
Ресурсы
- PyCache Trap: The Inspection–Execution Gap in Agent Skill Scanners (Preprint)
- Авторы: Jie Liao, Simeng Qin, Wenqi Ren, Wei Zhou, Junhao Wen, Ranjie Duan, Yang Liu, Xiaojun Jia
- Университеты: Chongqing University, Northeastern University at Qinhuangdao, Sun Yat-sen University, Tencent, Nanyang Technological University
- Код: https://github.com/leo0481/PyCacheTrap
- Связанные работы: SkillScanner, SkillCamo, ExecScan (Jia et al., 2026b); Python cache poisoning (Ohm et al., 2026); PEP 552
