
SecureAI-Scan — это CLI-инструмент, который сканирует кодовые базы TypeScript и JavaScript на предмет проблем безопасности, специфичных для приложений на базе ИИ: инъекции в промпты, злоупотребление MCP-инструментами, отравление данных RAG, нарушения доверия агентов и другое.
Большинство сканеров в этой области сопоставляют ключевое слово по шаблону и называют это находкой. SecureAI-Scan прослеживает фактический путь источник → поток → приёмник через реальный код с разрешением импортов — и сканирование по умолчанию показывает только то, что может доказать. Без аккаунта, без загрузки в облако, ничего не покидает вашу машину.
Охватывает официальные OWASP Top 10 для LLM-приложений 2026, Top 10 для агентных приложений (2026) и MCP Top 10 с первой недели запуска.
npx --yes [email protected] scan .
Нет необходимости в аккаунте, облачной загрузке, интерпретаторе Python или настройке. TypeScript, JavaScript, Python, MCP-конфигурации и пакеты Agent Skill определяются автоматически.
**Измеренный кандидат в релиз `0.9.0`:** 136/136 тестов · 88.08% покрытие операторов · 12 676 файлов в 9 публичных репозиториях · 0 новых отпечатков уровня по умолчанию относительно проверенного базового уровня. [Доказательства](https://github.com/akanthed/secureai-scan/blob/main/docs/benchmarks/v0.9.0.json) · [методология и ограничения](https://github.com/akanthed/secureai-scan/blob/main/docs/ReleaseAssurance.md)```
▌ HIGH AI001 Prompt injection via user input
PROVEN LLM01:2026 Prompt Injection
source src/chat.ts:8 request data `req.body.input`
flow src/chat.ts:13 passed as `systemPrompt`
sink src/chat.ts:10 openai.chat.completions.create — system role (OpenAI)
fix Keep system prompts static; pass user input as a user-role message.
Это для вас? SecureAI-Scan намеренно ограничен рисками LLM, MCP и RAG/агентов — промпт-инъекциями, отравлением инструментов, небезопасной обработкой вывода, контролем доступа к векторным хранилищам, отравлением навыков агентов. Это не универсальный SAST- или секрет-сканер, и он не пытается им быть; известный вредоносный пакет без полезной нагрузки в форме LLM (например, жёстко заданный адрес эксфильтрации в вызове email API) обнаруживается офлайн-списком рекомендаций (DEP003), а не правилом-паттерном. Если ваша кодовая база взаимодействует с LLM, MCP-сервером, векторным хранилищем или поставляет Agent Skills — этот инструмент создан для вас.
Новое: статическое сканирование конфигурации для LiteLLM Proxy (config.yaml) — жёстко заданные секреты, открытые текстом конечные точки провайдера, отсутствующие защитные механизмы. См. Правила (LLC001–LLC003).
proven (прослеженный поток данных или установленный факт из конфига), likely (разрешённый приёмник, один эвристический шаг) или heuristic. Стандартное сканирование показывает только proven + likely. Эвристики включаются опционально через --paranoid.openai, @anthropic-ai/sdk, ai, @google/genai, LangChain, Bedrock, …). Ваш клиент Google Maps больше никогда не будет помечен как LLM.npm run regression сканирует реальные публичные репозитории (OpenAI/Anthropic/Vercel AI SDKs, официальные MCP-серверы, LlamaIndex) против зафиксированного, вручную проверенного базового уровня и падает при любой новой находке proven/likely. См. Тестирование и бенчмаркинг для фактических цифр до/после, или Что мы нашли при сканировании реальных репозиториев для истории за ними — 6/6 уровень обнаружения на размеченном корпусе вредоносных навыков, и почему мы не называем llama_index «уязвимым» из-за честной находки на уровне библиотеки. Обсуждение →--output report.sarif размещает находки прямо в pull request и во вкладке Security.secureai-scan bom . строит инвентарь на основе синтаксиса: SDK, ID моделей, векторные хранилища, агентные фреймворки и MCP-серверы, сопоставленные с потребностями документации OWASP LLM Top 10 / EU AI Act..mcp.json, claude_desktop_config.json, .cursor/mcp.json: незакреплённые npx -y серверы, встроенные секреты, HTTP-транспорты открытым текстом.command/args stdio-транспорта MCP, построенные из данных запроса — паттерн, стоящий за раскрытием RCE в MCP STDIO 2026 года.SKILL.md — Agent Skills загружаются в контекст целиком, поэтому отравленный навык — это отравленное описание инструмента под другим именем.SKILL.md, и каждая проверка содержимого выполняется против деобфусцированных вариантов текста. Это нацелено на опубликованные техники — гомоглифы, разбиение нулевой ширины, полезные нагрузки в .git/ или build/, эксфильтрация, скрытая в файле *.test.ts, — которые обошли >90% из девяти сканеров, исследованных в Cloak and Detonate (arXiv:2607.02357). См. Устойчивость к обходу.scripts/sync-advisories.js. Работает офлайн при каждом сканировании, без необходимости флага. CVE срабатывает только когда ваша закреплённая версия доказуемо находится в затронутом диапазоне; задокументированный вредоносный пакет срабатывает даже при неоднозначном диапазоне, потому что установка бэкдора необратима.SecureAI-Scan не заменяет универсальный SAST-инструмент или сканер контейнеров/IaC — запускайте его вместе с ними, а не вместо. Он создан специально для поверхности атаки LLM/MCP/RAG и делает акцент на доказательности потока данных, а не на плоских находках по ключевым словам.
| SecureAI-Scan | Semgrep (OSS rules) | Trivy | GitHub Advanced Security | |
|---|---|---|---|---|
| Промпт-инъекция (источник→приёмник прослежен) | ✅ поток данных с разрешением импортов | ⚠️ только правила-паттерны, поддерживаются сообществом | ❌ | ⚠️ CodeQL может, но нет набора правил для ИИ |
| Отравление MCP-инструментов / риск конфигурации | ✅ MCP007–010, сканер конфигурации | ❌ | ❌ | ❌ |
Отравление Agent Skills (SKILL.md) | ✅ устойчиво к обходу, с учётом пакетов | ❌ | ❌ | ❌ |
| Неправильная настройка RAG / векторных хранилищ | ✅ VEC001–004 | ❌ | ❌ | ❌ |
| Рекомендации по известным вредоносным ИИ-пакетам | ✅ DEP003, офлайн, с учётом версий | ❌ | ⚠️ общий поток CVE, не для ИИ | ⚠️ Dependabot, общий поток CVE |
| Общий SAST (SQLi, XSS, path traversal) | ❌ вне области действия по дизайну | ✅ | ❌ | ✅ |
| Сканирование контейнеров / IaC | ❌ | ❌ | ✅ | ⚠️ через CodeQL/Actions |
| Уровни доказательности (proven/likely/heuristic) | ✅ | ❌ находки плоские | ❌ | ⚠️ CodeQL имеет некоторые, не настроены под ИИ |
| Вывод SARIF (GitHub code scanning) | ✅ | ✅ | ✅ | нативный |
| Работает офлайн, без аккаунта | ✅ | ✅ (OSS rules) | ✅ | ❌ требует GitHub |
Если вы уже используете Semgrep или GHAS, оставьте их — добавьте SecureAI-Scan для той поверхности риска, которую они вообще не моделируют.
Предпочитаете сначала задать вопросы? Попробуйте бесплатного AI-консультанта по безопасности SecureAI-Scan на ChatGPT.
Собираетесь запустить MCP-сервер, найденный на GitHub или в Twitter? Сначала вставьте его описание инструмента в MCP X-Ray — он проверит его на скрытый Unicode, внедрённые инструкции и известные вредоносные пакеты в вашем браузере, без установки.
secureai-scan scan . от начала до конца, реальный вывод на реальном (маленьком, намеренно уязвимом) файле — исходник:

Формы атак, которые сканер прослеживает от начала до конца:
| Поток данных отравления MCP-инструментов | Поток данных инъекции в контекст RAG |
|---|---|
![]() | ![]() |
Та, что нужна вам в 95% случаев:```bash secureai-scan scan .
Все остальное доступно, когда понадобится. `secureai-scan scan . --help` показывает всё это в терминале, сгруппированным так же:
**Повседневное использование**
| Флаг | Что делает |
|------|---------------|
| *(нет)* | результаты `proven` + `likely` — по умолчанию, флаги не нужны |
| `--paranoid` | также включает результаты уровня `heuristic` |
| `-s, --severity <уровень>` | показывать только результаты на уровне `low`\|`medium`\|`high`\|`critical` и выше |
| `--output <файл>` | записать полный отчёт — `.sarif` (code scanning GitHub), `.json`, `.md` или `.html` |
**Область применения: какие правила запускать**
| Флаг | Что делает |
|------|---------------|
| `-r, --rules <список>` | запускать только эти ID правил, например `AI001,MCP007` |
| `--only-ai` / `--only-mcp` / `--only-vec` / `--only-skl` | запускать только одну категорию правил |
| `--check-dependencies` | также проверять `package.json`/`requirements.txt` по реестрам npm/PyPI на опечатки и выдуманные пакеты (`DEP001`/`DEP002`). Автоматически включается, если выбрать эти правила напрямую через `-r` — не нужно помнить о передаче обоих флагов. Не требуется для `DEP003` (заведомо вредоносные пакеты), который всегда работает офлайн |
**CI / рабочий процесс**
| Флаг | Что делает |
|------|---------------|
| `--fail-on <серьёзность>` | выход с кодом `1`, если существуют результаты на этом уровне серьёзности и выше |
| `--baseline <файл>` | отслеживать только новые/изменённые проблемы относительно сохранённого базового уровня |
| `--policy <файл>` | загрузить пороги, пропускаемые пути и заблокированные правила из `.secureai-policy.json` (определяется автоматически, если присутствует — `secureai-scan init` создаёт такой файл) |
**Расширенные возможности**
| Флаг | Что делает |
|------|---------------|
| `--min-confidence <0-1>` | более точный, чем `--paranoid`: скрывать результаты ниже точного порога уверенности (`0.9` proven / `0.65` likely / `0.35` heuristic) |
| `--limit <n>` | максимальное количество групп правил, отображаемых в терминале (по умолчанию `10`) — полные детали всегда попадают в `--output` |
| `--debug` | выводить каждый просканированный файл и какие правила выполнялись |
**Сканируйте перед установкой — без клонирования, без настройки:**```bash
secureai-scan skill anthropics/skills # a GitHub "owner/repo" shorthand
secureai-scan skill https://github.com/… # or a full git URL
secureai-scan skill ./some/local/skill-dir # or a local path
secureai-scan mcp some-mcp-server-package # a bare npm package name
secureai-scan mcp owner/mcp-server-repo # or git, same as `skill`
skill и mcp загружают цель и сканируют её, после чего удаляют загруженную копию (--keep, чтобы вместо этого изучить её). Ничто из загруженного никогда не выполняется: npm-цель загружается с помощью npm pack — только tarball, без install, без скриптов жизненного цикла — а git-цель представляет собой обычный git clone --depth 1. Это самый важный момент: до того, как навык попадёт в ~/.claude/skills/ или сервер — в .mcp.json, а не после.
Другие команды:```bash secureai-scan bom . --output AI_BOM.md # AI Bill of Materials secureai-scan explain AI001 # why + exploit + fix example, for any rule secureai-scan threat-model . # THREAT_MODEL.md with the OWASP coverage matrix — example: docs/examples/THREAT_MODEL.example.md secureai-scan init # policy file + CI workflow, one-time setup
Подавить проверенное замечание в коде:```ts
// secureai-ignore AI001: reviewed, input sanitized via allowlist
name: SecureAI-Scan on: [pull_request] permissions: contents: read security-events: write jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: akanthed/[email protected] with: scanner-version: 0.10.0 fail-on: high
Находки отображаются в виде встроенных аннотаций в PR и на вкладке Security репозитория. (`secureai-scan init` генерирует эквивалентный workflow с использованием CLI напрямую.)
Сканирование чистое? Добавьте бейдж в свой собственный README:```md
[](https://github.com/akanthed/SecureAI-Scan)
Предпочитаете выявлять находки до того, как они будут отправлены? Добавьте этот репозиторий как источник хука pre-commit вместо GitHub Action или вместе с ним:```yaml repos:
The hook scans the whole project on every commit (not just changed files — a dataflow trace into file A can depend on file B, which a partial scan would miss) and blocks the commit on `high`+ severity findings by default. Override the threshold in your own config:```yaml
- id: secureai-scan
args: ["--fail-on", "critical"]
42 правила, сопоставленные с официальным OWASP Top 10 для LLM-приложений (2026) — а также, где применимо, с OWASP Top 10 для агентных приложений (2026, ASI), OWASP MCP Top 10 (2025) и статьёй EU AI Act. См. версионированное покрытие и ограничения 2026; threat-model выводит матрицу для каждого сканируемого проекта.
| Правило | Что оно доказывает | OWASP |
|---|---|---|
| AI001 | Пользовательский ввод попадает в системный/разработческий промпт (прослеженный путь от источника к приёмнику, включая границы функций/файлов) | LLM01 |
| AI002 | Содержимое промпта или секреты записываются в логи (в файлах, использующих LLM SDK) | LLM02 |
| AI003 | Вызов LLM в обработчике запроса без проверки аутентификации перед ним | LLM06 |
| AI004 | Весь объект пользователя/сессии сериализуется в промпт (выбор отдельных полей не помечается) | LLM02 |
| AI005 | Вывод LLM достигает приёмников eval/exec/SQL/HTML | LLM10 |
| AI006 | Высокорисковые инструменты (delete, pay, deploy, …) доступны без шлюза одобрения | LLM03 |
| AI007 | Извлечённый RAG-контент интерполируется в привилегированные промпты | LLM01 |
| AI008 | Секреты, встроенные в текст системного промпта | LLM08 |
| AI009 | Неограниченный пользовательский ввод / отсутствие лимитов токенов | LLM06 |
| AI010 | Полученный извне контент попадает в промпты | LLM01 |
| AI011 | Вывод агента повышается до системной роли в последующих вызовах | LLM03 |
| AI012 | Вывод LLM разбирается без проверки схемы | LLM10 |
| MCP001 | Метаданные MCP-инструмента достигают системного промпта без валидации | LLM01 |
| MCP002 | URL MCP-сервера формируется из пользовательского ввода | LLM04 |
| MCP003 | Результаты MCP-инструмента повышаются до системной роли | LLM10 |
| MCP004 | MCP-сервер запускается как незакреплённый пакет npx -y | LLM04 |
| MCP005 | Секрет, встроенный в закоммиченную MCP-конфигурацию | LLM02 |
| MCP006 | MCP-сервер по незашифрованному HTTP | LLM04 |
| MCP007 | Невидимый/двунаправленный Unicode, скрытый в именах или описаниях MCP-инструментов | LLM01 · MCP03 |
| MCP008 | Фразы инъекций, направляющих агента, в описаниях MCP-инструментов | LLM01 · MCP03 |
| MCP009 | Описание инструмента, перенаправляющее вызовы на другой инструмент (затенение) |
secureai-scan explain <RULE_ID> предоставляет описание эксплуатации и пример кода до/после для любого правила.
Три независимые поверхности сканирования питают один объединённый список находок с дедупликацией:``` ┌─────────────────────┐ *.ts / *.js ───▶ │ ts-morph AST rules │───┐ │ (import-resolved │ │ │ sinks + dataflow) │ │ └─────────────────────┘ │ │ ┌─────────────────────┐ │ ┌──────────────┐ ┌─────────────────┐ *.py ───▶ │ tree-sitter AST + │───┼───▶ │ scan.ts │───▶ │ evidence filter │ │ local taint flow │ │ │ merge/dedupe│ │ → confidence │ └─────────────────────┘ │ │ + suppress │ │ → severity │ │ │ (// secure- │ │ → baseline diff │ .mcp.json, ┌─────────────────────┐ │ │ ai-ignore) │ │ → report │ SKILL.md ───▶ │ Config/bundle scan │──┘ └──────────────┘ └─────────────────┘ │ (off-disk, evasion- │ │ │ resistant) │ ▼ └─────────────────────┘ terminal · sarif · json · md · html
package.json, requirements.txt ─▶ dependency-guard.ts (advisories.ts, offline, version-aware)
Каждое правило AST вызывает функцию «LLM-вызовом» только в том случае, если она через реальные импорты разрешается в известный SDK — никогда только по совпадению имени. Полную разбивку каждой поверхности см. в [`docs/Architecture.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Architecture.md), а о том, как работает контракт уровней доказательств, — в [`docs/DetectionEngine.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/DetectionEngine.md).
## MCP-сервер (используйте его из Claude)
Пакет поставляется с MCP-сервером, предоставляющим `scan_repository`, `explain_rule`, `generate_bom` и `scan_untrusted_target` (получение и сканирование навыка или MCP-сервера, прежде чем Claude порекомендует его установить — то же поведение «получение без выполнения», что и у CLI-команд `skill`/`mcp`):```json
{
"mcpServers": {
"secureai-scan": {
"command": "node",
"args": ["/path/to/secureai-scan/mcp-server/index.js"]
}
}
}
Для пользователей Claude Code / Claude.ai, skills/secureai-scan/SKILL.md обучает Claude, когда запускать сканирование (при проверке кода AI/LLM или при проверке MCP-сервера/Agent Skill перед его установкой) и как читать результаты — в отличие от MCP-сервера выше, здесь не нужен отдельный процесс. Скопируйте каталог skills/secureai-scan/ в свой .claude/skills/, чтобы использовать его.
В июле 2026 года два публичных исследования показали, что сканеры Agent Skill тривиально обходятся.
Cloak and Detonate (Ji, Xu, Li, Gao, Wei, Wang & Cheung — HKUST, arXiv:2607.02357) провели состязательное исследование против девяти опубликованных сканеров навыков, используя 1 613 вредоносных навыков из реального мира. Два семейства атак, оба из которых сохраняют полезную нагрузку в точности:
.git/, с безобидным прикрытием SKILL.md, который пересобирает её при первом запуске. ≥90% обход на каждом протестированном сканере, ≥96% на большинстве. У одного гибридного сканера уровень обнаружения упал с ~99% до ~10%.Отдельно Gecko Security (VentureBeat) разместили полезную нагрузку в файле *.test.ts. Каждый публичный сканер пропустил его — но Jest и Vitest автоматически обнаруживают и выполняют эти файлы при следующем npm test.
SecureAI-Scan обрабатывает каждую опубликованную технику. Каждой строке ниже соответствует тест в test/deobfuscate.test.js или фикстура в test-fixtures/vulnerable/skills/:
| Техника (название из статьи) | Пример | Как обрабатывается |
|---|---|---|
| Вставка нулевой ширины | Ignore all previous… | Текст сопоставляется с вариантами без невидимых символов |
| Схожие глифы | сurl (кириллическая с) | Свёртка гомоглифов в ASCII перед сопоставлением |
| Смена регистра | EvAl | Все шаблоны содержимого нечувствительны к регистру |
| Склейка переменных | 'cu' + 'rl' | Смежные строковые литералы объединяются перед сопоставлением |
| Разбиение по строкам | ev⏎al(x) | Разрывы строк внутри слова объединяются как дополнительный вариант |
| Извлечение по файлам | полезная нагрузка во вспомогательном файле | Сканируется весь пакет, а не только SKILL.md |
| Перемещение каталога | scripts/x.sh → build/x.sh | Нет списка пропускаемых каталогов внутри пакета |
| Переименование расширения | x.sh → x.txt | Файлы сканируются по содержимому, а не по расширению |
| Дополнение размера | дополнение сверх лимита сканера | Чрезмерно большие файлы читаются с начала, никогда не пропускаются |
| SFS packing | полезная нагрузка в .git/skillpack.dat | Любой не-git файл в .git/ пакета помечается как proven (SKL004) |
| Размещение в тестовых файлах | полезная нагрузка в *.test.ts | Сканирование пакета намеренно не понижает тестовые пути (SKL005) |
Деобфускация обычно является риском для точности — больше совпадений, больше шума. Здесь логика инвертирована: совпадение, которое появляется только после деобфускации, повышается до proven, а не понижается. Обычная документация не содержит соединителя нулевой ширины внутри «ignore previous instructions» и кириллической с внутри curl. Само сокрытие является утвердительным доказательством намерения.
Сравнение проводится с набором сырых совпадений, а не просто с вопросом «совпал ли сырой текст вообще» — иначе атакующий мог бы замаскировать сигнал, оставив одну безобидную фразу в открытом виде.
Два новых правила пакета срабатывают только на конъюнкции, никогда на отдельное ключевое слово:
tar -x в README рядом с несвязанным бинарным ресурсом недостаточно. Настоящие архивы (gzip/zip/png/pdf/wasm — проверяются по магическим байтам, а не по расширению) вообще никогда не являются «непрозрачными», как бы они ни были сжаты.~/.aws/credentials, а не слово «token») или массовое перечисление переменных окружения (os.environ.items(), а не os.environ["API_KEY"]) — и исходящий трафик на жёстко заданный нелокальный хост, в пределах 25 строк друг от друга в одном файле, или удалённую загрузку, которая выполняется после переназначения через одно или несколько переименований. Хелпер публикации, который читает ~/.npmrc в одной функции и вызывает реестр сорока строками позже, остаётся чистым, а чтение одной именованной переменной окружения для вызова API никогда не помечается — обе формы закреплены как безопасные фикстуры.Проверено на двух реальных корпусах, а не только на фикстурах, написанных нами самими: 0 находок во всех 18 реальных пакетах навыков в anthropics/skills и во всех 14 в vercel/ai, и 6/6 правильных на размеченном оценочном корпусе cisco-ai-defense/skill-scanner (20 навыков, каждый с вердиктом _expected.json) с нулевым количеством ложных срабатываний на всём, что помечено как безопасное. См. Тестирование и бенчмаркинг.
Честное ограничение: вывод статьи состоит в том, что детонация во время выполнения превосходит статический анализ, и это верно. Адаптивный противник, знающий эти правила, может написать преобразование, которое они не покрывают. Что здесь меняется — это стоимость обхода: опубликованные, ныне циркулирующие техники больше не работают, а обфускация, необходимая для их преодоления, теперь сама повышает серьёзность находки. Статическое сканирование — это фильтр, а не граница безопасности. Относитесь к непроверенному навыку как к непроверенному коду, независимо от того, что говорит любой сканер.
prepublishOnly.Это проект с одним мейнтейнером, без контрактного SLA или независимой сертификации. Указанные выше меры контроля снижают риск; они не превращают статическое сканирование в доказательство безопасности.
Ложные срабатывания убивают сканеры. Механизм правил SecureAI-Scan следует трём жёстким правилам:
test-fixtures/safe/ содержит шаблоны, которые раньше вызывали ложные срабатывания (редактированные полезные нагрузки PII, клиенты Google Maps, ключи API из переменных окружения рядом с LLM-клиентами, обычное логирование ответов, поля метаданных OAuth, chunks потоковых ответов, художественный/повествовательный текст промптов). Любая находка там приводит к провалу набора тестов.Три уровня, потому что одного недостаточно, чтобы доверять заявлениям сканера — точность и полнота — это разные режимы отказа, и проверяются оба.
1. Корпус фикстур — точность + полнота, запускается при каждой сборке.```bash npm test
[`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) и [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe) сканируются вместе: каждый уязвимый фикстур должен срабатывать на ожидаемое правило с доказательством `proven`/`likely` (полнота), каждый безопасный фикстур должен давать **ноль** результатов `proven`/`likely` (точность). Быстро и детерминированно — но это лишь доказывает, что сканер ведёт себя корректно на коде, написанном специально для его тестирования.
**2. Регрессионный бенчмарк на реальных проектах — против публичных репозиториев, которые мы не писали.**```bash
npm run regression # scan the full curated repo set
npm run regression -- --fresh # re-clone everything first
npm run regression -- openai-node # scan just one repo by name
npm run regression -- --update-baseline # accept the current findings
scripts/regression-scan.js клонирует подобранный, разнообразный набор реальных публичных репозиториев (OpenAI/Anthropic/Vercel AI SDK, официальные MCP-серверы и TypeScript SDK, LlamaIndex, а также anthropics/skills и cisco-ai-defense/skill-scanner для покрытия skill-бандлов — охватывая TS и Python, примеры кода потребителей SDK и исходники авторов SDK) и сканирует каждый из них с помощью собранного CLI.
Он завершается с ненулевым кодом при любом proven/likely срабатывании, отсутствующем в test/regression-baseline.json — вручную проверенной записи срабатываний, уже сверенных с их исходной строкой. Отпечатки имеют формат repo|rule|file, а не номера строк, поэтому обычные изменения в вышестоящих репозиториях не создают шума. Новый отпечаток — это утверждение, которое сканер должен обосновать: если это не реальная проблема, значит, это баг правила, исправленный в корне и закреплённый как новый фикстур в test-fixtures/safe/. Внесение в базовую линию срабатывания, которое вы не прочитали, сводит на нет весь механизм.
Покрытие skill-бандлов выделено отдельной строкой, потому что корпус evals/ из cisco-ai-defense/skill-scanner размечен — каждый из его 20 фикстуров поставляется с вердиктом _expected.json и находится в каталоге, буквально названном malicious/ или safe/, поэтому он служит двойной проверкой полноты, а не только точности: 6/6 вредоносных фикстуров в области действия срабатывают, 0 срабатываний на всём, что помечено как safe, и 0 срабатываний на всех 18 реальных бандлах в anthropics/skills и всех 14 в vercel/ai. (Остальные категории Cisco — SQL-инъекции, обход пути, исчерпание ресурсов, обобщённый eval() аргумента функции, полезная нагрузка, намеренно разбитая на четыре файла — либо выходят за документированную область LLM/MCP/RAG, либо за пределы анализа конъюнкции в пределах одного файла; см. запись в журнале изменений 0.6.0 с конкретным обоснованием по каждой.)
Исторические данные «до/после» из прогона, который привёл к исходным исправлениям точности (срабатывания на уровне доказательств по умолчанию, без --paranoid):
| Репозиторий | До | После | Что было не так |
|---|---|---|---|
| vercel/ai | 773 | 1 | Каталоги examples/, корневые tests/ и каталоги в стиле ecosystem-tests/ с дефисами не распознавались как пути с более низким уровнем доверия; chunks (распространённая переменная потокового ответа) рассматривалась как однозначное свидетельство RAG |
| openai/openai-node | 47 | 0 | Тот же пробел в определении путей, применённый к собственным examples//ecosystem-tests/ SDK |
| anthropics/anthropic-sdk-typescript | 2 | 0 | Тот же пробел в определении путей для корневого каталога tests/ |
| modelcontextprotocol/typescript-sdk | 3 | 0 | Поля метаданных OAuth в стиле token_endpoint/tokenType помечались как утёкшие секреты |
| run-llama/llama_index | 18 | 15 | Проверка Python помечала любое поле description=, содержащее «system prompt», как proven отравление MCP-инструмента, независимо от контекста. Оставшиеся 15 — это срабатывания VEC001 на собственных обобщённых определениях ретриверов библиотеки — сканирование исходников SDK векторной БД, а не кода приложения, поэтому фильтр для проверки существовать не может; честное, внутренне присущее ограничение, а не баг |
Текущий прогон (2026-08-06) — версионированные свидетельства записаны в docs/benchmarks/v0.9.0.json:
| Репозиторий | Срабатывания | Правила | Статус |
|---|---|---|---|
| openai-node, anthropic-sdk-typescript, anthropic-sdk-python, modelcontextprotocol/typescript-sdk, modelcontextprotocol/servers | 0 | — | чисто |
| anthropics/skills (18 реальных skill-бандлов) | 0 | — | чисто — чистая проверка точности для SKL001–005 |
| vercel/ai (5 691 файл) | 0 | — | было 40 (AI001, AI003, AI005, AI010, MCP002) до триажа — каждое вручную сверено с исходником и подтверждено как ложное срабатывание, прослежено до 3 независимых багов в корневой причине (см. ниже), исправлено и повторно подтверждено как чистое при полном повторном сканировании |
| run-llama/llama_index | 46 | VEC001 | внутренне присущее ограничение, а не баг — собственные обобщённые определения ретриверов библиотеки, где фильтр арендатора для поиска существовать не может |
| cisco-ai-defense/skill-scanner | 7 | SKL001, SKL002, SKL005 | все на фикстурах, помеченных как malicious/ — 6/6 в области действия, 0 на всём, что помечено как safe/ |
Триаж vercel/ai выявил три реальных бага в корневой причине — ни один не специфичен для правил навыков v0.6.0, все находятся в общей логике, используемой многими правилами:
resolveLlmSink рассматривал любой вызов, разрешённый в модуль LLM SDK, как вызов модели, независимо от имени метода — помечая isToolUIPart (защитник типа, который пакет ai экспортирует прямо рядом с generateText) как вызов LLM. Только это вызвало 3 из 5 групп срабатываний (AI001, AI003, AI010).DANGEROUS_CALLEES в AI005 включает "query" для приёмников в стиле SQL-инъекций, но "query" также является допустимым глаголом вызова LLM/агента — claudeSdk.query({ prompt, options }), собственный вызов модели Claude Agent SDK, помечался как «вывод LLM, переданный в опасный приёмник» исключительно из-за общего имени метода.REQUEST_SOURCES (идентично продублированный в MCP002, MCP010, VEC003) сопоставлялся с голым "params." — любой параметр функции, традиционно названный params, не обязательно данные HTTP-запроса. Валидатор схемы URL (assertOpenLinkParams(params: unknown)) помечался как «URL MCP-сервера из пользовательского ввода».Все три исправлены в корневой причине (а не в конкретном месте вызова) и закреплены как постоянные фикстуры в test-fixtures/. Полные подробности в CHANGELOG.md.
3. Проверка «уязвимая vs исправленная версия» — доказывает полноту, а не только точность.
Два описанных выше уровня проверяют только то, что сканер остаётся спокойным на безопасном коде. Проверки рекомендаций DEP003 валидируются в обратную сторону: закрепите пакет на документально уязвимой версии и подтвердите, что он помечается, затем закрепите его на исправленной версии и подтвердите, что он не помечается.```bash
node --test test/dependency-guard.test.js
covers: `[email protected]` (CVE-2025-6514, уязвим) помечен / `[email protected]` (исправлен) чист; `[email protected]` (до бэкдора) чист / `[email protected]` (после — для вредоносного пакета легитимного патча не существует) всё ещё помечен; `llama-cpp-python==0.2.71` (CVE-2024-34359, из набора, сгенерированного OSV) помечен / `==0.2.72` (исправлен) чист, в том числе с учётом нормализации имён в PyPI (`llama_cpp_python`); и спецификаторы без пиннинга вида `langchain>=0.1.0`, дающие **ноль** находок в отчёте по умолчанию. Сборка этого теста выявила реальный пробел: `DEP003` раньше сопоставлял уведомления только по имени пакета, никогда фактически не сравнивая заявленную версию с затронутым диапазоном из уведомления — исправлено в [`src/scanner/semver.ts`](https://github.com/akanthed/secureai-scan/blob/main/src/scanner/semver.ts).
Неоднозначность разрешается по-разному для каждого вида уведомлений — намеренно. **Вредоносный** пакет срабатывает, даже если заявленную версию не удаётся разрешить — установка бэкдора необратима, поэтому проверка завершается пометкой. **CVE** срабатывает на уровне `proven` только тогда, когда заявленная версия является точным пином, заведомо попадающим в затронутый диапазон; вариант без пиннинга, но возможно затронутый, понижается до `heuristic` (только с `--paranoid`). Применение правила для вредоносных пакетов к снимку CVE из 162 записей поставило бы критическую находку в каждом репозитории, где объявлен `langchain>=0.1.0` — непригодный к действию шум в масштабе.
## Roadmap
См. [`ROADMAP.md`](https://github.com/akanthed/secureai-scan/blob/main/ROADMAP.md) — что уже выпущено и что запланировано. Оба языковых движка основаны на AST: ts-morph для TypeScript/JavaScript и Tree-sitter для Python. Импорты, вызовы, присваивания, декораторы, области видимости, именованные аргументы, поля словарей и строки в Python являются узлами синтаксиса; целевой код никогда не импортируется и не выполняется, и интерпретатор Python не требуется. Оставшийся пробел Python ограничен глубиной межфункционального/межфайлового потока данных, а не парсингом. Производительность сканирования и известные ограничения задокументированы в [`docs/Performance.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Performance.md).
## Участие
Вклад приветствуется — см. [`CONTRIBUTING.md`](https://github.com/akanthed/secureai-scan/blob/main/CONTRIBUTING.md) о рабочем процессе и [`docs/WritingRules.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/WritingRules.md) / [`docs/RuleDevelopment.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/RuleDevelopment.md) о том, как добавить правило обнаружения, отвечающее указанной выше планке точности. Каждому новому правилу нужен фикстур как в [`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable), так и в [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe), запись в `src/scanner/catalog.ts` и кейс в `test/corpus.test.js` — `npm test` обеспечивает соблюдение всех трёх.
## Лицензия
MIT © Akshay Kanthed
| LLM01 · MCP03 |
| MCP010 | Команда/аргументы MCP stdio-сервера, сформированные из пользовательского ввода (RCE) | LLM04 · MCP05 |
| SKL001 | Невидимый/двунаправленный Unicode где-либо в пакете Agent Skill | LLM01 |
| SKL002 | Фразы инъекций, направляющих агента, в описании или теле навыка (обнаруживаются через обфускацию) | LLM01 |
| SKL003 | Содержимое навыка управляет тем, когда/как используется другой навык (затенение) | LLM01 |
| SKL004 | Многоэтапная/самораспаковывающаяся полезная нагрузка: непрозрачный блоб + инструкции по его декодированию и запуску | LLM04 · MCP04 |
| SKL005 | Чтение учётных данных + жёстко заданный внешний исходящий канал в сопутствующем файле пакета | LLM02 · MCP04 |
| SKL006 | Выполнение команд во время загрузки через синтаксис динамической инъекции контекста Claude Code (!`cmd`/```!), до любого шлюза разрешений инструментов | LLM04 · MCP05 |
| SKL007 | Неограниченное разрешение Bash во frontmatter allowed-tools навыка | LLM03 |
| SKL008 | Навык получает инструкции с внешнего URL и направляет агента следовать им («Circus of Skills») | LLM04 |
| SKL009 | Навык сохраняет бэкдор, записывая в другой файл контекста (MEMORY.md/SOUL.md/AGENTS.md/CLAUDE.md) | LLM05 |
| SKL010 | Небезопасный тег десериализации YAML/JSON во frontmatter навыка или в прилагаемом конфигурационном файле | LLM04 |
| VEC001 | Векторный поиск без фильтра арендатора/пользователя | LLM09 |
| VEC002 | Неограниченный или управляемый пользователем лимит поиска | LLM06 |
| VEC003 | Пользовательский контент, загружаемый в общее векторное хранилище | LLM05 |
| VEC004 | Загрузка без тегирования арендатора/пространства имён | LLM09 |
| DEP001 | Имя зависимости не найдено в реестре (опционально --check-dependencies) | LLM04 |
| DEP002 | Имя зависимости на одно изменение от популярного пакета (опционально) | LLM04 |
| DEP003 | Зависимость с задокументированным вредоносным релизом или критической CVE — проверяется офлайн при каждом сканировании, с учётом диапазона версий (postmark-mcp, mcp-remote CVE-2025-6514, …) | LLM04 · MCP04 |
| LLC001 | Жёстко заданный секрет в config.yaml прокси LiteLLM | LLM02 |
| LLC002 | api_base прокси LiteLLM доступен по незашифрованному HTTP | LLM04 |
| LLC003 | В конфигурации прокси LiteLLM нет секции guardrails: (эвристика, только --paranoid) | LLM03 |