skill-audit
Входящий гейт: чужой код ко мне. Любой сторонний скилл или плагин, скачанный
с GitHub, попадает в авто-загружаемый каталог ~/.claude/ без единой проверки —
а его SKILL.md уходит в контекст модели, его хуки выполняются на каждом вызове
инструмента, его .mcp.json может поднять чужой сервер с твоими правами. Этот скан —
барьер перед тем, как чужой код станет частью твоего окружения.
Граница с leak-scan
Две противоположные стороны одной двери, их легко перепутать:
|
Направление |
Вопрос |
Навык |
| Сюда |
чужое ко мне |
«безопасно ли ставить этот скачанный скилл?» |
skill-audit (этот) |
| Отсюда |
моё наружу |
«не утечёт ли что-то личное, если я это опубликую?» |
leak-scan |
Публикуешь свой пак — это leak-scan. Ставишь чужой пак — это сюда. Проверки
независимы: скилл может быть безупречно обезличен и при этом нести инъекцию, и наоборот.
Где лежит скрипт
python ~/.claude/skills/leak-scan/scripts/skill_injection_scan.py <цель> [опции]
Скрипт физически живёт в skills/leak-scan/scripts/ — там же, где его выходной
близнец leak_scan.py. Оба сканера написаны одним заходом и делят внутренности,
поэтому этот навык — инструкция к соседнему скрипту, а не отдельная программа.
Зови полным путём выше, не относительным от каталога этого навыка.
Нет каталога leak-scan — навык не работает: везти их надо парой.
Когда запускать
- Скачал скилл/плагин/агента с GitHub и до копирования в
~/.claude/skills|plugins|agents.
- Оцениваешь чужой репозиторий, из которого хочешь забрать код себе.
- Ревизия уже установленного стороннего пакета («а что оно вообще делает при загрузке?»).
Использование
<цель> — каталог скилла (со SKILL.md), каталог плагина (с plugin.json), каталог
агентов/команд, сборник (skills/+agents/+hooks/), или одиночный .md.
Тип определяется автоматически.
--min-severity CRITICAL|WARN|INFO — порог печати (по умолчанию INFO).
--allow RULE_ID — заглушить правило (повторяемо), напр. --allow hooks.registered
для заведомо доверенного пакета с хуком.
--include-vendor — сканировать и node_modules/dist/build (по умолчанию помечаются
как не проверенные, а не молча пропускаются).
--json — машинный вывод. --list-rules — список всех правил.
Коды выхода: 0 чисто · 1 есть находки (CRITICAL/WARN) · 2 скан не завершён
достоверно (симлинк-цель, превышен лимит).
# Гейт перед установкой (пайп теряет код возврата — смотри печатное CRITICAL/ЧИСТО)
python ~/.claude/skills/leak-scan/scripts/skill_injection_scan.py ./downloaded-skill
Скрипт — чистый stdlib Python, UTF-8 stdout (Windows-совместимо), ничего не меняет на диске.
Ключей и оплаты не требует: всё считается локально, сеть не используется.
Что ловит (35 правил, severity CRITICAL/WARN/INFO)
| Группа |
Правила |
Сигнал |
| Невидимый Unicode |
unicode.invisible |
ZWSP/ZWNJ/ZWJ/word-joiner/BOM, bidi-override (trojan source), теговый блок U+E0000–E007F. Emoji-ZWJ (👨💻) не флагуется |
| Prompt-injection |
prompt.ignore_previous (RU+EN), disregard_system, role_reassignment, fake_system_prefix, system_tag, chat_template_tag (<|im_start|>, [INST]), tool_call_tag, covert_directive, autonomy_grab, memory_write |
фразы-перехваты, поддельные system:/<system>, разделители чат-шаблонов, приказ действовать скрытно/дописать себя в CLAUDE.md |
| Скрытое в Markdown |
md.hidden_directive, md.hidden_style |
инструкция в HTML-комментарии или под display:none/белым шрифтом |
| Фронтматтер-захват |
frontmatter.allowed_tools, model_invocation_enabled, permission_mode |
скилл сам себе выдаёт Bash(*)/*, включает автовызов, понижает режим разрешений |
| Эксфильтрация |
tool.exfiltration_shape, net.suspicious_sink, code.credential_exfil_chain, content.base64_blob |
секрет по сети на литеральный посторонний хост; webhook.site/ngrok/paste-сервисы; чтение кред + отправка в одном файле; длинный base64-блоб |
| Опасный код |
code.remote_exec, dynamic_eval, detached_spawn, destructive, claude_config_write |
curl|sh, исполнение декодированного base64, фоновый отвязанный процесс, rm -rf ~, запись в твой конфиг Claude Code |
| Конфиг |
config.permission_widening |
пакет расширяет permissions в settings.json — тихо добавляет себе разрешения |
| Хуки (нет у источника) |
hooks.registered, hooks.dangerous_command |
плагин регистрирует хук, что выполнится на каждом вызове инструмента (matcher *) |
| MCP (нет у источника) |
mcp.server_declared, mcp.arbitrary_command, mcp.remote_server |
.mcp.json поднимает сервер с произвольной командой (bash -c …) или удалённый хост, куда уходит сессия |
| npm lifecycle (нет у источника) |
npm.lifecycle_script, npm.obfuscated_lifecycle |
postinstall/preinstall, исполняющий код при npm install; вскрывает и локальный скрипт, который тот вызывает (реальный кейс: postinstall маскировал spawn) |
| Файловая система |
fs.symlink, fs.binary_artifact |
симлинк (содержимое не проверено, может указывать наружу); исполняемый бинарник без исходников |
Точный список идентификаторов — всегда --list-rules, а не эта таблица: она пересказ.
Как читать результат
- CRITICAL — не устанавливать, пока не понятно, зачем это в пакете.
- WARN — бывает легитимно (хук, MCP-сервер, объявленный
allowed-tools), но должно
быть заявлено в README пакета; сверь, что ожидаемо.
- Правила ловят форму, а не смысл: текст про безопасность/LLM может совпасть (напр.
статья, объясняющая инъекции). Всегда смотри строку в контексте —
> excerpt
печатается рядом.
- Этот навык ловит сам себя. Таблица правил выше цитирует
<|im_start|>, system:
и webhook/paste-хосты, поэтому скан каталога скиллов даёт на skill-audit/SKILL.md
находки уровня CRITICAL (prompt.chat_template_tag, prompt.system_tag,
net.suspicious_sink). Это тот самый случай «форма, а не смысл» — не находка.
Полезный побочный эффект: так проверяется, что сканер вообще работает.
- ЧИСТО — не гарантия: прочитай
SKILL.md и скрипты глазами. Скан — первый барьер,
не последний.
Расширение правил
Правила инлайн в ~/.claude/skills/leak-scan/scripts/skill_injection_scan.py:
текстовые — _TEXT_RULES, кодовые — _CODE_RULES, структурные (JSON) — функции
_scan_hooks / _scan_mcp / _scan_package_json / _scan_json_file.
Полный список id — --list-rules. Для разового доверия — --allow RULE_ID,
не редактируй файл.
Происхождение
Идея и часть правил — из virgiliojr94/book-to-skill (tools/scan_generated_skill.py),
адаптировано и расширено проверками hooks / MCP / npm-lifecycle, которых у источника нет.
1---2name: skill-audit3description: Проверка ЧУЖОГО скилла/плагина до установки: prompt-injection, хуки, MCP, npm (35 правил). Триггеры: «безопасно ли ставить», «скачал с гитхаба скилл». Своё наружу→leak-scan.4---56# skill-audit78Входящий гейт: **чужой код ко мне**. Любой сторонний скилл или плагин, скачанный9с GitHub, попадает в авто-загружаемый каталог `~/.claude/` **без единой проверки** —10а его SKILL.md уходит в контекст модели, его хуки выполняются на каждом вызове11инструмента, его `.mcp.json` может поднять чужой сервер с твоими правами. Этот скан —12барьер перед тем, как чужой код станет частью твоего окружения.1314## Граница с leak-scan1516Две противоположные стороны одной двери, их легко перепутать:1718| | Направление | Вопрос | Навык |19|---|---|---|---|20| **Сюда** | чужое ко мне | «безопасно ли ставить этот скачанный скилл?» | **skill-audit** (этот) |21| **Отсюда** | моё наружу | «не утечёт ли что-то личное, если я это опубликую?» | `leak-scan` |2223Публикуешь свой пак — это `leak-scan`. Ставишь чужой пак — это сюда. Проверки24независимы: скилл может быть безупречно обезличен и при этом нести инъекцию, и наоборот.2526## Где лежит скрипт2728```bash29python ~/.claude/skills/leak-scan/scripts/skill_injection_scan.py <цель> [опции]30```3132Скрипт физически живёт в `skills/leak-scan/scripts/` — там же, где его выходной33близнец `leak_scan.py`. Оба сканера написаны одним заходом и делят внутренности,34поэтому этот навык — инструкция к соседнему скрипту, а не отдельная программа.35Зови **полным путём выше**, не относительным от каталога этого навыка.36Нет каталога `leak-scan` — навык не работает: везти их надо парой.3738## Когда запускать3940- Скачал скилл/плагин/агента с GitHub и **до** копирования в `~/.claude/skills|plugins|agents`.41- Оцениваешь чужой репозиторий, из которого хочешь забрать код себе.42- Ревизия уже установленного стороннего пакета («а что оно вообще делает при загрузке?»).4344## Использование4546- `<цель>` — каталог скилла (со `SKILL.md`), каталог плагина (с `plugin.json`), каталог47 агентов/команд, сборник (`skills/`+`agents/`+`hooks/`), **или** одиночный `.md`.48 Тип определяется автоматически.49- `--min-severity CRITICAL|WARN|INFO` — порог печати (по умолчанию INFO).50- `--allow RULE_ID` — заглушить правило (повторяемо), напр. `--allow hooks.registered`51 для заведомо доверенного пакета с хуком.52- `--include-vendor` — сканировать и `node_modules/dist/build` (по умолчанию помечаются53 как **не проверенные**, а не молча пропускаются).54- `--json` — машинный вывод. `--list-rules` — список всех правил.5556**Коды выхода:** `0` чисто · `1` есть находки (CRITICAL/WARN) · `2` скан не завершён57достоверно (симлинк-цель, превышен лимит).5859```bash60# Гейт перед установкой (пайп теряет код возврата — смотри печатное CRITICAL/ЧИСТО)61python ~/.claude/skills/leak-scan/scripts/skill_injection_scan.py ./downloaded-skill62```6364Скрипт — чистый stdlib Python, UTF-8 stdout (Windows-совместимо), **ничего не меняет на диске**.65Ключей и оплаты не требует: всё считается локально, сеть не используется.6667## Что ловит (35 правил, severity CRITICAL/WARN/INFO)6869| Группа | Правила | Сигнал |70|--------|---------|--------|71| Невидимый Unicode | `unicode.invisible` | ZWSP/ZWNJ/ZWJ/word-joiner/BOM, bidi-override (trojan source), теговый блок U+E0000–E007F. Emoji-ZWJ (👨💻) не флагуется |72| Prompt-injection | `prompt.ignore_previous` (RU+EN), `disregard_system`, `role_reassignment`, `fake_system_prefix`, `system_tag`, `chat_template_tag` (`<\|im_start\|>`, `[INST]`), `tool_call_tag`, `covert_directive`, `autonomy_grab`, `memory_write` | фразы-перехваты, поддельные `system:`/`<system>`, разделители чат-шаблонов, приказ действовать скрытно/дописать себя в CLAUDE.md |73| Скрытое в Markdown | `md.hidden_directive`, `md.hidden_style` | инструкция в HTML-комментарии или под `display:none`/белым шрифтом |74| Фронтматтер-захват | `frontmatter.allowed_tools`, `model_invocation_enabled`, `permission_mode` | скилл сам себе выдаёт `Bash(*)`/`*`, включает автовызов, понижает режим разрешений |75| Эксфильтрация | `tool.exfiltration_shape`, `net.suspicious_sink`, `code.credential_exfil_chain`, `content.base64_blob` | секрет по сети на литеральный посторонний хост; webhook.site/ngrok/paste-сервисы; чтение кред + отправка в одном файле; длинный base64-блоб |76| Опасный код | `code.remote_exec`, `dynamic_eval`, `detached_spawn`, `destructive`, `claude_config_write` | `curl\|sh`, исполнение декодированного base64, фоновый отвязанный процесс, `rm -rf ~`, запись в твой конфиг Claude Code |77| Конфиг | `config.permission_widening` | пакет расширяет `permissions` в settings.json — тихо добавляет себе разрешения |78| **Хуки** (нет у источника) | `hooks.registered`, `hooks.dangerous_command` | плагин регистрирует хук, что выполнится **на каждом** вызове инструмента (matcher `*`) |79| **MCP** (нет у источника) | `mcp.server_declared`, `mcp.arbitrary_command`, `mcp.remote_server` | `.mcp.json` поднимает сервер с произвольной командой (`bash -c …`) или удалённый хост, куда уходит сессия |80| **npm lifecycle** (нет у источника) | `npm.lifecycle_script`, `npm.obfuscated_lifecycle` | `postinstall/preinstall`, исполняющий код при `npm install`; вскрывает и локальный скрипт, который тот вызывает (реальный кейс: postinstall маскировал `spawn`) |81| Файловая система | `fs.symlink`, `fs.binary_artifact` | симлинк (содержимое не проверено, может указывать наружу); исполняемый бинарник без исходников |8283Точный список идентификаторов — всегда `--list-rules`, а не эта таблица: она пересказ.8485## Как читать результат8687- **CRITICAL** — не устанавливать, пока не понятно, зачем это в пакете.88- **WARN** — бывает легитимно (хук, MCP-сервер, объявленный `allowed-tools`), но должно89 быть заявлено в README пакета; сверь, что ожидаемо.90- Правила ловят **форму, а не смысл**: текст про безопасность/LLM может совпасть (напр.91 статья, объясняющая инъекции). Всегда смотри строку в контексте — `> excerpt`92 печатается рядом.93- **Этот навык ловит сам себя.** Таблица правил выше цитирует `<|im_start|>`, `system:`94 и webhook/paste-хосты, поэтому скан каталога скиллов даёт на `skill-audit/SKILL.md`95 находки уровня CRITICAL (`prompt.chat_template_tag`, `prompt.system_tag`,96 `net.suspicious_sink`). Это тот самый случай «форма, а не смысл» — не находка.97 Полезный побочный эффект: так проверяется, что сканер вообще работает.98- ЧИСТО — не гарантия: прочитай `SKILL.md` и скрипты глазами. Скан — первый барьер,99 не последний.100101## Расширение правил102103Правила инлайн в `~/.claude/skills/leak-scan/scripts/skill_injection_scan.py`:104текстовые — `_TEXT_RULES`, кодовые — `_CODE_RULES`, структурные (JSON) — функции105`_scan_hooks` / `_scan_mcp` / `_scan_package_json` / `_scan_json_file`.106Полный список id — `--list-rules`. Для разового доверия — `--allow RULE_ID`,107не редактируй файл.108109## Происхождение110111Идея и часть правил — из `virgiliojr94/book-to-skill` (`tools/scan_generated_skill.py`),112адаптировано и расширено проверками hooks / MCP / npm-lifecycle, которых у источника нет.