3,583 papers
arXiv:2610.10612 75 7 окт. 2026 г. FREE

PyCache Trap: подложенный байткод-кэш обходит сканеры навыков агентов

КЛЮЧЕВАЯ СУТЬ
Сканер написал «чисто», а Python запустил совсем другой код. Рядом с чистым исходником .py лежит подложенный байткод-кэш (.pyc), и при запуске Python берёт именно его. Метод EAV позволяет проверить навык агента (например, для Claude Code) до установки: не по описанию, а по тому, что реально выполнится. Против семи сканеров ловушка прошла в 94–100% случаев. EAV пересобирает исходник доверенным интерпретатором и сравнивает с кэшем. Не совпало или исходника нет, значит блок. Переписыванием текста это не обмануть.
Адаптировать под запрос
⚡

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.
НЕ РАЗОБРАНО никогда не считается безопасным.



Таблица «файл — эффект — доказательство», затем результат трёх проверок,
затем вердикт. Отдельная строка на каждый .pyc.

Что подставлять: {название_навыка} и {путь} — что проверяешь; {назначение} — что навык обещает делать; {версия_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

📋 Дайджест исследования

Ключевая суть

Сканер написал «чисто», а Python запустил совсем другой код. Рядом с чистым исходником .py лежит подложенный байткод-кэш (.pyc), и при запуске Python берёт именно его. Метод EAV позволяет проверить навык агента (например, для Claude Code) до установки: не по описанию, а по тому, что реально выполнится. Против семи сканеров ловушка прошла в 94–100% случаев. EAV пересобирает исходник доверенным интерпретатором и сравнивает с кэшем. Не совпало или исходника нет, значит блок. Переписыванием текста это не обмануть.

Принцип работы

Процесс из трёх шагов: исходник → пересборка → сравнение с кэшем. Сначала берёшь видимый .py. Потом сам компилируешь его в пустой папке. Потом сверяешь строки, адреса и команды с тем, что лежит в .pyc. Верить можно только тому, что проверил сам, а не тому, что пакет сообщает о себе. Это как сверять товар с накладной. Накладную пишет продавец, поэтому смотреть надо в коробку. Третий вердикт «не разобрано» нужен, чтобы всё непонятное никогда не считалось безопасным.

Почему работает

Проверка «кэш подходит к исходнику» смотрит только на заголовок: время изменения или хеш исходника. Заголовок пишет сам автор пакета. Он подделывается за секунду и ничего не доказывает. Сканеры на основе большой языковой модели оценивают, насколько подозрительно звучит описание. Поэтому правки вроде «смягчить язык» или «вплести в сюжет» гасят предупреждения. А вредный код сидит в байтах, которые модель не читала. Жесть: ни один из семи сканеров не назвал, что спрятано в кэше, а пропустили почти все пакеты. В реальном агенте вредное поведение выполнилось примерно у трёх четвертей навыков. EAV решает это детерминированной сверкой: компиляция и сравнение, а модель только читает исходник и называет эффекты с цитатами. Критично: у самой EAV около 10% ложных тревог. Около 7% настоящих атак она всё же пропустила (смешанная оценка по пяти семействам).

Когда применять

Безопасность агентов → проверка сторонних навыков с публичных каталогов, особенно когда в папке лежит __pycache__ или .pyc без явной причины. Подходит, если навык получит доступ к токенам, cookie или файлам .env. НЕ подходит для навыков не на Python: авторы другие форматы не проверяли. НЕ заменяет сверку, если у агента нет доступа к командам. «В голове» у модели пересборку делать нельзя.

Мини-рецепт

1. Заведи агента с командами: ему нужны просмотр файлов и компиляция в пустой временной папке. Без этого сверка не получится.
2. Запрети запуск самого навыка: только читать и пересобирать.
3. Узнай версию Python: кэш привязан к версии, пересобирать надо тем же интерпретатором.
4. Построй карту связей: инструкции → скрипты → импорты → .pyc, бинарники, картинки.
5. Требуй цитату на каждый эффект: чтение, запись, сеть, запуск команд. Нет строки в файле — это «гипотеза», в вердикт она не идёт.
6. Прогони три проверки: достижимость (секреты → сеть или запуск), запрещённые комбинации, происхождение кэша.
7. Каждый .pyc проверь отдельно: есть ли исходник, совпали ли строки, адреса и команды после пересборки.
8. Прими вердикт: ВРЕДНО, БЕЗВРЕДНО или НЕ РАЗОБРАНО. Последнее — это не «безопасно».
9. Добавь свои запретные связки: например, «чтение .env → любой POST-запрос».

Примеры

[ПЛОХО]: `Проверь навык metrika-direct-report на безопасность, посмотри SKILL.md и analyze.py` [ХОРОШО]: `Ты аудитор безопасности навыков агентов. Ничего из навыка не запускай. Построй граф: SKILL.md → скрипты → импорты → все .pyc рядом. Для каждого эффекта (чтение, запись, сеть, запуск) дай файл и цитату, иначе помечай «гипотеза». Для КАЖДОГО .pyc: найди парный исходник, пересобери его в пустой временной папке, сравни строки, адреса и команды. Нет исходника или не совпало — нарушение. Заявленное назначение навыка это не отменяет. Вердикт: ВРЕДНО / БЕЗВРЕДНО / НЕ РАЗОБРАНО (не считается безопасным). Отдельная строка на каждый .pyc.` [ПЛОХО]: `Навык прошёл сканер, значит можно ставить` [ХОРОШО]: `Сканер молчит. Проверь, есть ли в папке __pycache__ без парного исходника, и пересобери код тем же Python, что стоит у меня (версия 3.11). Заголовок кэша не считай доказательством.`
Источник: PyCache Trap: The Inspection-Execution Gap in Agent Skill Scanners
ArXiv ID: 2610.10612 | Сгенерировано: 2026-10-09 05:20

Проблемы LLM

ПроблемаСутьКак обойти
Модель-проверяющий судит по тому, что видно, а не по тому, что исполняетсяПросишь проверить пакет, документ или код на безопасность. Модель читает описание и видимый исходник. Если реально работает другое (скомпилированный файл, подгружаемый ресурс, вложение), она этого не видит. Ответ: «чисто». Дальше хуже. Модель реагирует на тон и формулировки. Автор смягчает текст, и предупреждения исчезают. Суть при этом не меняется. Проверка по тексту ничего не гарантируетНе проси «оцени, подозрительно ли выглядит». Проси: «перечисли всё, что может исполниться вместо видимого». Каждый пункт подтверждай цитатой или строкой. То, что не удалось прочитать, помечай «не разобрано». Сверку делай командой, а не «в голове» модели. Модель читает и описывает. Сравнивает и считает код или инструмент

Методы

МетодСуть
Доказательства и третий исход вместо «чисто/не чисто»Разреши модели три вердикта: ВРЕДНО, БЕЗВРЕДНО, НЕ РАЗОБРАНО. Добавь два правила. Первое: «Каждый вывод — с цитатой или строкой из файла. Нет цитаты — пометь "гипотеза" и не включай в вердикт». Второе: «НЕ РАЗОБРАНО никогда не считается безопасным». Почему работает: при выборе из двух вариантов модель угадывает и чаще выбирает «чисто». Третий исход даёт честный выход при нехватке данных. Требование цитаты отсекает выдумки и догадки. Когда да: аудит кода, проверка договоров, ревью конфигов, проверка фактов. То есть любая задача, где пропуск опаснее ложной тревоги. Когда нет: творческие задачи и мнения без проверяемого источника. Рычаг: строгая цитата даёт меньше выдумок, но больше «не разобрано». Для вердикта «безопасно» оставляй строгое правило
📖 Простыми словами

PyCache Trap: The Inspection-Execution Gap inAgentSkill Scanners

arXiv: 2610.10612

Сканеры безопасности для AI-агентов тупо читают текст, а исполняет код совсем другая система. Когда ты ставишь плагин или «скилл» для условного Claude Code, проверяющий софт радостно парсит понятный человеку исходник .py и восторженное описание. Но сам Python — ленивая скотина: если в папке лежит скомпилированный кэш .pyc, он молча исполнит именно его, даже не заглядывая в исходный код. Получается критический разрыв между проверкой и исполнением: сканер одобряет безобидный текст, а машина запускает цифровой яд.

Это как пройти фейсконтроль в элитный клуб с идеальным паспортом прилежного студента, внутри которого вклеена чужая фотография. Охранник вежливо пощупал корочку, поставил штамп «проверен», а внутри ты уже творишь полнейший беспредел. Сканеры навыков ведут себя как слепые вахтёры: они ведутся на красивую витрину, пока под капотом выполняется откровенная малварь.

Сама атака называется PyCache Trap, и работает она до смешного просто. Злоумышленник кладёт в публичный репозиторий кристально чистый файл analyze.py, а рядом прячет скомпилированный вредонос в папке __pycache__. Всю валидацию ломает то, что хеш и таймстемп в заголовке кэша генерирует сам автор пакета. Защитный софт видит, что даты формально сходятся, решает, что всё совпадает, и пропускает пакет без реальной декомпиляции байткода.

Исследовали ловушку на Claude Code Skills, но эта дыра разносит вообще любые агентские экосистемы на Python. Ты качаешь из публичного хаба безобидный навык вроде выгрузки отчётов из Метрики и Директа в Excel, думаешь, что нейросеть тебя прикрыла, а на деле сливаешь токены и базы данных. Любая платформа, скачивающая чужие автономные навыки, сейчас уязвима перед этим трюком.

Короче: проверка только исходного кода сегодня — полная фикция. Любой агентский софт обязан либо тупо сносить __pycache__ при установке и форсить флаг PYTHONDONTWRITEBYTECODE, либо лезть внутрь скомпилированного бинарника. Пока разработчики сканеров этого не поняли, ставить чужие скиллы в агентов — русская рулетка для твоих приватных данных.

Работа с исследованием

Адаптируйте исследование под ваши задачи или создайте готовый промпт на основе техник из исследования.

0 / 2000
~0.5-2 N-токенов ~10-30с
~0.3-1 N-токенов ~5-15с