
SecureAI-Scan é uma ferramenta CLI que analisa bases de código TypeScript e JavaScript em busca de problemas de segurança específicos de aplicativos com tecnologia de IA — injeção de prompt, abuso de ferramentas MCP, envenenamento de dados RAG, violações de confiança de agentes e muito mais.
A maioria dos scanners neste espaço faz correspondência de padrões com uma palavra-chave e chama isso de descoberta. O SecureAI-Scan rastreia o caminho real de origem → fluxo → destino através de código real, resolvido por importação — e uma varredura padrão mostra apenas o que ele pode comprovar. Sem conta, sem upload para a nuvem, nada sai da sua máquina.
Cobre o OWASP Top 10 para Aplicações LLM 2026 oficial, o Top 10 para Aplicações Agênticas (2026) e o MCP Top 10 desde a semana de lançamento.
npx --yes [email protected] scan .
Sem conta, upload para a nuvem, interpretador Python ou configuração necessários. TypeScript, JavaScript, Python, configs MCP e pacotes de Agent Skill são detetados automaticamente.
**Candidato a lançamento `0.9.0` medido:** 136/136 testes · 88,08% de cobertura de declarações · 12.676 ficheiros em 9 repositórios públicos · 0 novas impressões digitais de nível predefinido face à linha de base revista. [Evidência](https://github.com/akanthed/secureai-scan/blob/main/docs/benchmarks/v0.9.0.json) · [metodologia e limites](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.
Isto é para você? O SecureAI-Scan é deliberadamente escopado para riscos de LLM, MCP e RAG/agentes — injeção de prompt, envenenamento de ferramentas, tratamento inseguro de saída, controle de acesso a vector stores, envenenamento de habilidades de agentes. Não é um scanner SAST geral ou de segredos, e não tenta ser um; um pacote conhecidamente malicioso sem payload em formato LLM (por exemplo, um endereço de exfiltração hardcoded em uma chamada de API de e-mail) é capturado pela lista de avisos offline (DEP003), não por uma regra de padrão. Se seu código fala com um LLM, um servidor MCP, um vector store ou envia Agent Skills, isto foi feito para você.
Novo: varredura estática de configuração para LiteLLM Proxy (config.yaml) — segredos hardcoded, endpoints de provedor em texto puro, guardrails ausentes. Veja Regras (LLC001–LLC003).
provado (dataflow rastreado ou fato de configuração analisado), provável (sink resolvido, um salto heurístico) ou heurístico. Uma varredura padrão mostra apenas provado + provável. Heurísticas são opcionais via --paranoid.openai, @anthropic-ai/sdk, ai, @google/genai, LangChain, Bedrock, …). Seu cliente do Google Maps nunca mais será sinalizado como LLM.npm run regression varre repositórios públicos reais (OpenAI/Anthropic/Vercel AI SDKs, servidores MCP oficiais, LlamaIndex) contra uma baseline commitada e revisada manualmente e falha em qualquer novo achado provado/provável. Veja Testes e benchmarking para os números reais de antes/depois, ou O que encontramos varrendo repositórios reais para a história por trás deles — uma taxa de captura de 6/6 em um corpus rotulado de skills maliciosas, e por que não estamos chamando llama_index de "vulnerável" por um achado honesto em nível de biblioteca. Write-up da discussão →--output report.sarif coloca achados inline em pull requests e na aba Security.secureai-scan bom . constrói um inventário derivado de sintaxe de SDKs, IDs de modelo, vector stores, frameworks de agentes e servidores MCP, mapeado para as necessidades de documentação do OWASP LLM Top 10 / EU AI Act..mcp.json, claude_desktop_config.json, .cursor/mcp.json: servidores npx -y sem pin, segredos inline, transportes HTTP em texto puro.command/args do transporte stdio MCP construídos a partir de dados de requisição — o padrão por trás da divulgação de RCE no MCP STDIO de 2026.SKILL.md — Agent Skills são carregados integralmente no contexto, então uma skill envenenada é uma descrição de ferramenta envenenada por outro nome.SKILL.md, e cada verificação de conteúdo roda contra variantes deofuscadas do texto. Isso visa as técnicas publicadas — homóglifos, divisão por zero-width, payloads armazenados em .git/ ou build/, exfiltração escondida em um arquivo *.test.ts — que contornaram >90% dos nove scanners pesquisados em Cloak and Detonate (arXiv:2607.02357). Veja Resistência a evasão.scripts/sync-advisories.js. Roda offline em cada varredura, sem necessidade de flag. Um CVE só dispara quando sua versão fixada está provadamente dentro do intervalo afetado; um pacote documentado como malicioso dispara mesmo em um intervalo ambíguo, porque instalar um backdoor é irrecuperável.O SecureAI-Scan não é um substituto para uma ferramenta SAST geral ou um scanner de contêiner/IaC — execute-o junto com uma, não no lugar de uma. Ele é feito sob medida para a superfície de ataque LLM/MCP/RAG e enfatiza evidência de dataflow em vez de achados planos por palavra-chave.
| SecureAI-Scan | Semgrep (regras OSS) | Trivy | GitHub Advanced Security | |
|---|---|---|---|---|
| Injeção de prompt (source→sink rastreado) | ✅ dataflow resolvido por importação | ⚠️ apenas regras de padrão, mantidas pela comunidade | ❌ | ⚠️ CodeQL pode, mas sem ruleset específico de IA |
| Envenenamento de ferramentas MCP / risco de configuração | ✅ MCP007–010, scanner de configuração | ❌ | ❌ | ❌ |
Envenenamento de Agent Skills (SKILL.md) | ✅ resistente a evasão, ciente de bundle | ❌ | ❌ | ❌ |
| Configuração incorreta de RAG / vector-store | ✅ VEC001–004 | ❌ | ❌ | ❌ |
| Avisos de pacotes de IA conhecidamente maliciosos | ✅ DEP003, offline, ciente de versão | ❌ | ⚠️ feed CVE geral, não específico de IA | ⚠️ Dependabot, feed CVE geral |
| SAST geral (SQLi, XSS, path traversal) | ❌ fora do escopo por design | ✅ | ❌ | ✅ |
| Varredura de contêiner / IaC | ❌ | ❌ | ✅ | ⚠️ via CodeQL/Actions |
| Níveis de evidência (provado/provável/heurístico) | ✅ | ❌ achados são planos | ❌ | ⚠️ CodeQL tem alguns, não ajustados para IA |
| Saída SARIF (code scanning do GitHub) | ✅ | ✅ | ✅ | nativo |
| Roda offline, sem conta | ✅ | ✅ (regras OSS) | ✅ | ❌ requer GitHub |
Se você já roda Semgrep ou GHAS, mantenha-os — adicione o SecureAI-Scan para a superfície de risco que eles não modelam de forma alguma.
Prefere fazer perguntas primeiro? Experimente o SecureAI-Scan AI Security Advisor no ChatGPT gratuito.
Prestes a executar um servidor MCP que encontrou no GitHub ou Twitter? Cole a descrição da ferramenta no MCP X-Ray primeiro — verifica Unicode oculto, instruções injetadas e pacotes conhecidamente maliciosos no seu navegador, sem instalação.
secureai-scan scan . de ponta a ponta, saída real contra um arquivo real (pequeno, deliberadamente vulnerável) — fonte:

Formatos de ataque que o scanner rastreia de ponta a ponta:
| Dataflow de envenenamento de ferramentas MCP | Dataflow de injeção de contexto RAG |
|---|---|
![]() | ![]() |
O que você precisa 95% das vezes:```bash secureai-scan scan .
Tudo o resto está lá quando precisar. `secureai-scan scan . --help` mostra tudo isto no terminal, agrupado da mesma forma:
**Uso diário**
| Flag | O que faz |
|------|---------------|
| *(nenhuma)* | resultados `proven` + `likely` — o padrão, sem necessidade de flags |
| `--paranoid` | inclui também resultados de nível `heuristic` |
| `-s, --severity <level>` | mostra apenas resultados em/abaixo de `low`\|`medium`\|`high`\|`critical` |
| `--output <file>` | escreve um relatório completo — `.sarif` (code scanning do GitHub), `.json`, `.md` ou `.html` |
**Âmbito das regras que são executadas**
| Flag | O que faz |
|------|---------------|
| `-r, --rules <list>` | executa apenas estes IDs de regras, ex.: `AI001,MCP007` |
| `--only-ai` / `--only-mcp` / `--only-vec` / `--only-skl` | executa apenas uma categoria de regras |
| `--check-dependencies` | verifica também `package.json`/`requirements.txt` contra o registo npm/PyPI para erros de digitação e pacotes alucinados (`DEP001`/`DEP002`). Ativado automaticamente se selecionar essas regras diretamente via `-r` — nunca precisa de se lembrar de passar ambas. Não é necessário para `DEP003` (pacotes conhecidos como maliciosos), que é sempre executado offline |
**CI / fluxo de trabalho**
| Flag | O que faz |
|------|---------------|
| `--fail-on <severity>` | sai com código `1` se existirem resultados em/abaixo desta gravidade |
| `--baseline <file>` | monitoriza apenas problemas novos/alterados em relação a uma baseline guardada |
| `--policy <file>` | carrega limites, caminhos ignorados e regras bloqueadas de um `.secureai-policy.json` (deteção automática se presente — `secureai-scan init` cria um) |
**Avançado**
| Flag | O que faz |
|------|---------------|
| `--min-confidence <0-1>` | mais granular que `--paranoid`: oculta resultados abaixo de uma pontuação de confiança exata (`0.9` proven / `0.65` likely / `0.35` heuristic) |
| `--limit <n>` | número máximo de grupos de regras mostrados no terminal (padrão `10`) — o detalhe completo vai sempre para `--output` |
| `--debug` | imprime cada ficheiro analisado e quais regras foram executadas |
**Analise antes de instalar — sem clone, sem configuração:**```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 e mcp buscam o alvo e o analisam, depois eliminam a cópia obtida (--keep para inspecioná-la em vez disso). Nada do que é obtido é alguma vez executado: um alvo npm é descarregado com npm pack — apenas o tarball, sem install, sem scripts de ciclo de vida — e um alvo git é um simples git clone --depth 1. Este é o momento que mais importa: antes de uma skill chegar a ~/.claude/skills/ ou de um servidor chegar a .mcp.json, não depois.
Outros comandos:```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
Suprimir uma descoberta revisada no código:```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
As descobertas aparecem como anotações inline no PR e na aba de Segurança do repositório. (`secureai-scan init` gera um workflow equivalente usando a CLI diretamente.)
A varredura está limpa? Adicione o badge ao seu próprio README:```md
[](https://github.com/akanthed/SecureAI-Scan)
Prefere capturar descobertas antes de serem enviadas? Adicione este repositório como fonte de hook do pre-commit em vez de, ou juntamente com, a GitHub Action:```yaml repos:
O hook verifica todo o projeto a cada commit (não apenas os arquivos alterados — um rastreamento de fluxo de dados no arquivo A pode depender do arquivo B, o que uma verificação parcial deixaria passar) e bloqueia o commit em descobertas de severidade `high`+ por padrão. Substitua o limite na sua própria configuração:```yaml
- id: secureai-scan
args: ["--fail-on", "critical"]
42 regras, mapeadas para o OWASP Top 10 oficial para Aplicações de LLM (2026) — além, quando aplicável, do OWASP Top 10 para Aplicações de Agentes (2026, ASI), do OWASP MCP Top 10 (2025) e de um artigo da Lei de IA da UE. Consulte a cobertura e limites da versão 2026; threat-model renderiza a matriz para cada projeto verificado.
| Regra | O que ela comprova | OWASP |
|---|---|---|
| AI001 | Entrada do usuário flui para um prompt de sistema/desenvolvedor (fonte rastreada → sumidouro, inclusive entre limites de arquivo/função) | LLM01 |
| AI002 | Conteúdo do prompt ou segredos gravados em logs (em arquivos que usam um SDK de LLM) | LLM02 |
| AI003 | Chamada de LLM em um manipulador de requisição sem verificação de autenticação antes dela | LLM06 |
| AI004 | Objeto inteiro de usuário/sessão serializado em um prompt (a seleção de campos não é sinalizada) | LLM02 |
| AI005 | Saída do LLM alcança sumidouros de eval/exec/SQL/HTML | LLM10 |
| AI006 | Ferramentas de alto impacto (delete, pay, deploy, …) expostas sem uma etapa de aprovação | LLM03 |
| AI007 | Conteúdo de RAG recuperado interpolado em prompts privilegiados | LLM01 |
| AI008 | Segredos incorporados no texto do prompt de sistema | LLM08 |
| AI009 | Entrada de usuário sem limites / limites de token ausentes | LLM06 |
| AI010 | Conteúdo externo buscado flui para prompts | LLM01 |
| AI011 | Saída do agente elevada ao papel de sistema em chamadas posteriores | LLM03 |
| AI012 | Saída do LLM analisada sem validação de esquema | LLM10 |
| MCP001 | Metadados de ferramenta MCP alcançam o prompt de sistema sem validação | LLM01 |
| MCP002 | URL do servidor MCP construída a partir de entrada do usuário | LLM04 |
| MCP003 | Resultados de ferramenta MCP elevados ao papel de sistema | LLM10 |
| MCP004 | Servidor MCP iniciado como um pacote npx -y sem fixação de versão | LLM04 |
| MCP005 | Segredo embutido em uma configuração MCP versionada | LLM02 |
| MCP006 | Servidor MCP sobre HTTP sem criptografia | LLM04 |
| MCP007 | Unicode invisível/bidi oculto em nomes ou descrições de ferramentas MCP | LLM01 · MCP03 |
| MCP008 | Frases de injeção direcionadas a agentes em descrições de ferramentas MCP | LLM01 · MCP03 |
secureai-scan explain <RULE_ID> fornece o passo a passo da exploração e um exemplo de código antes/depois para qualquer regra.
Três superfícies de varredura independentes alimentam uma única lista de achados mesclada e deduplicada:``` ┌─────────────────────┐ *.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)
Every AST rule only calls a function an "LLM call" if it resolves through real imports to a known SDK — never by name-matching alone. See [`docs/Architecture.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Architecture.md) for the full breakdown of each surface, and [`docs/DetectionEngine.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/DetectionEngine.md) for how the evidence-tier contract works.
## Servidor MCP (use-o a partir do Claude)
O pacote inclui um servidor MCP que expõe `scan_repository`, `explain_rule`, `generate_bom` e `scan_untrusted_target` (busca e analisa uma skill ou servidor MCP antes de o Claude recomendar a instalação — o mesmo comportamento de busca-sem-execução dos comandos CLI `skill`/`mcp`):```json
{
"mcpServers": {
"secureai-scan": {
"command": "node",
"args": ["/path/to/secureai-scan/mcp-server/index.js"]
}
}
}
Para utilizadores de Claude Code / Claude.ai, o skills/secureai-scan/SKILL.md ensina o Claude quando executar uma análise (ao rever código de IA/LLM, ou ao verificar um servidor MCP/Skill de Agente antes de o instalar) e como ler os resultados — sem necessidade de um processo separado, ao contrário do servidor MCP acima. Copie o diretório skills/secureai-scan/ para o seu .claude/skills/ para o utilizar.
Em julho de 2026, duas investigações públicas demonstraram que os scanners de Skills de Agente são trivialmente contornados.
Cloak and Detonate (Ji, Xu, Li, Gao, Wei, Wang & Cheung — HKUST, arXiv:2607.02357) realizou um estudo adversarial contra nove scanners de skills publicados, utilizando 1.613 skills maliciosas recolhidas em ambiente real. Duas famílias de ataques, ambas preservando exatamente o payload:
.git/, com um SKILL.md de cobertura benigno que o reconstrói na primeira execução. ≥90% de evasão em todos os scanners testados, ≥96% na maioria. A taxa de deteção de um scanner híbrido caiu de ~99% para ~10%.Separadamente, a Gecko Security (VentureBeat) distribuiu um payload num ficheiro *.test.ts. Todos os scanners públicos o ignoraram — mas o Jest e o Vitest detetam e executam automaticamente esses ficheiros no próximo npm test.
O SecureAI-Scan trata cada técnica publicada. Cada linha abaixo tem um teste correspondente em test/deobfuscate.test.js ou uma fixture em test-fixtures/vulnerable/skills/:
| Técnica (nome do artigo) | Exemplo | Como é tratada |
|---|---|---|
| Inserção de largura zero | Ignore all previous… | O texto é comparado com variantes sem caracteres invisíveis |
| Glifos confundíveis | сurl (с cirílico) | Dobragem de homóglifos para ASCII antes da correspondência |
| Inversão de maiúsculas | EvAl | Todos os padrões de conteúdo são insensíveis a maiúsculas/minúsculas |
| Divisão de variáveis | 'cu' + 'rl' | Literais de string adjacentes são unidos antes da correspondência |
| Divisão por linha | ev⏎al(x) | Quebras de linha intra-palavra unidas como variante extra |
| Extração por ficheiro | payload num helper importado | Todo o pacote é analisado, não apenas SKILL.md |
| Relocalização de diretório | scripts/x.sh → build/x.sh | Sem lista de exclusão de diretórios dentro de um pacote |
| Renomeação de extensão | x.sh → x.txt | Os ficheiros são analisados por conteúdo, não por extensão |
| Preenchimento de tamanho | preencher além do limite do scanner | Ficheiros sobredimensionados são lidos no início, nunca ignorados |
| Empacotamento SFS | payload em .git/skillpack.dat | Qualquer ficheiro não-git sob o .git/ de um pacote é provado (SKL004) |
| Encenação em ficheiros de teste | payload em *.test.ts | A análise de pacotes deliberadamente não rebaixa caminhos de teste (SKL005) |
A desofuscação é normalmente um risco de precisão — mais correspondências, mais ruído. Aqui a lógica é invertida: uma correspondência que só aparece após a desofuscação é promovida a provado, não rebaixada. Documentação comum não contém um unidor de largura zero dentro de "ignore previous instructions", nem um с cirílico dentro de curl. A ocultação é, por si só, evidência afirmativa de intenção.
A comparação é feita contra o conjunto de correspondências brutas, não apenas "a correspondência bruta existiu ou não" — caso contrário, um atacante poderia mascarar o sinal deixando uma frase inócua em texto claro.
As duas novas regras de pacote disparam apenas em conjunções, nunca numa palavra-chave isolada:
tar -x ao lado de um ativo binário não relacionado não é suficiente. Arquivos reais (gzip/zip/png/pdf/wasm — verificados por magic bytes, não por extensão) nunca são "opacos" em primeiro lugar, independentemente de como são comprimidos.~/.aws/credentials, não a palavra "token") ou enumeração em massa de variáveis de ambiente (os.environ.items(), não os.environ["API_KEY"]) — e egresso para um host não-local codificado, dentro de 25 linhas um do outro no mesmo ficheiro, ou uma obtenção remota que é executada após ser reatribuída através de uma ou mais renomeações. Um helper de publicação que lê ~/.npmrc numa função e chama um registo quarenta linhas depois permanece limpo, e ler uma variável de ambiente nomeada para uma chamada de API nunca é sinalizado — ambas as formas estão fixadas como fixtures seguras.Validado contra dois corpora do mundo real, não apenas fixtures que escrevemos nós próprios: 0 descobertas em todos os 18 pacotes de skills reais em anthropics/skills e todos os 14 em vercel/ai, e 6/6 corretos no corpus de avaliação rotulado de cisco-ai-defense/skill-scanner (20 skills, cada uma com um veredito _expected.json) com zero falsos positivos em qualquer coisa rotulada como segura. Consulte Testes e benchmarking.
Limitação honesta: a conclusão do artigo é que a detonação em tempo de execução supera a análise estática, e isso está correto. Um adversário adaptativo que conhece estas regras pode escrever uma transformação que elas não cobrem. O que muda aqui é o custo da evasão — as técnicas publicadas e atualmente em circulação deixam de funcionar, e a ofuscação necessária para as derrotar agora eleva ela própria a gravidade da descoberta. A análise estática é um filtro, não uma fronteira de segurança. Trate uma skill não confiável como código não confiável, independentemente do que qualquer scanner diga.
prepublishOnly.Este é um projeto de mantenedor único, sem SLA contratual ou certificação independente. Os controlos acima reduzem o risco; não transformam uma análise estática em prova de segurança.
Falsos positivos matam scanners. O motor de regras do SecureAI-Scan segue três regras rígidas:
test-fixtures/safe/ contém os padrões que costumavam causar falsos positivos (payloads de PII redigidos, clientes do Google Maps, chaves de API em variáveis de ambiente junto a clientes de LLM, registo de respostas comum, campos de metadados OAuth, chunks de respostas em streaming, texto de prompt de ficção/narrativa). Qualquer descoberta aí falha a suíte.Três camadas, porque uma só não é suficiente para confiar nas afirmações de um scanner — precisão e recall são modos de falha diferentes, e ambos são verificados.
1. Corpus de fixtures — precisão + recall, executado em cada build.```bash npm test
[`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) e [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe) são analisados em conjunto: cada fixture vulnerável deve acionar a regra esperada com evidência `proven`/`likely` (recall), cada fixture seguro deve produzir **zero** achados `proven`/`likely` (precisão). Rápido e determinístico — mas apenas prova que o scanner se comporta em código escrito especificamente para testá-lo.
**2. Benchmark de regressão no mundo real — contra repositórios públicos que não escrevemos.**```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 clona um conjunto curado e diversificado de repositórios públicos reais (OpenAI/Anthropic/Vercel AI SDKs, os servidores MCP oficiais e o TypeScript SDK, LlamaIndex, além de anthropics/skills e cisco-ai-defense/skill-scanner para cobertura de pacotes de skills — abrangendo TS e Python, código de exemplo de consumidores de SDK e código-fonte de autores de SDK) e escaneia cada um com o CLI compilado.
Ele sai com código não-zero em qualquer achado proven/likely que já não esteja em test/regression-baseline.json — um registro revisado manualmente de achados já lidos em relação à sua linha de origem. As impressões digitais são repo|rule|file, não números de linha, então mudanças comuns a montante não geram ruído. Uma nova impressão digital é uma afirmação que o scanner precisa justificar: se não for um problema genuíno, é um bug de regra, corrigido na causa raiz e travado como um novo fixture test-fixtures/safe/. Incluir na baseline um achado que você não leu anula todo o mecanismo.
A cobertura de pacotes de skills tem sua própria linha porque o corpus evals/ do cisco-ai-defense/skill-scanner é rotulado — cada um de seus 20 fixtures vem com um veredito _expected.json e fica sob um diretório literalmente chamado malicious/ ou safe/, então ele funciona também como uma verificação de recall, não apenas de precisão: 6/6 fixtures maliciosos no escopo disparam, 0 achados em qualquer coisa rotulada como safe, e 0 achados em todos os 18 pacotes reais em anthropics/skills e todos os 14 em vercel/ai. (As categorias restantes da Cisco — injeção de SQL, path traversal, exaustão de recursos, eval() genérico de um argumento de função, um payload deliberadamente dividido entre quatro arquivos — estão fora do escopo documentado de LLM/MCP/RAG ou além da análise de conjunção em arquivo único; veja a entrada do changelog 0.6.0 para o raciocínio específico de cada uma.)
Antes/depois histórico da execução que motivou as correções originais de precisão (achados no nível de evidência padrão, sem --paranoid):
| Repo | Antes | Depois | O que estava errado |
|---|---|---|---|
| vercel/ai | 773 | 1 | examples/, tests/ de nível superior e diretórios com hífen no estilo ecosystem-tests/ não eram reconhecidos como caminhos de menor confiança; chunks (uma variável comum de resposta em streaming) era tratado como evidência inequívoca de RAG |
| openai/openai-node | 47 | 0 | Mesma lacuna de detecção de caminho, aplicada aos próprios examples//ecosystem-tests/ do SDK |
| anthropics/anthropic-sdk-typescript | 2 | 0 | Mesma lacuna de detecção de caminho em um diretório tests/ de nível superior |
| modelcontextprotocol/typescript-sdk | 3 | 0 | Campos de metadados OAuth no estilo token_endpoint/tokenType sinalizados como segredos vazados |
| run-llama/llama_index | 18 | 15 | Uma verificação em Python sinalizava qualquer campo description= contendo "system prompt" como envenenamento proven de ferramenta MCP, independentemente do contexto. Os 15 restantes são hits VEC001 nas definições genéricas de retriever da própria biblioteca — escanear o código-fonte de um SDK de banco vetorial, não código de aplicação, então um filtro não pode existir para ser verificado; um limite honesto e inerente, não um bug |
Execução atual (2026-08-06) — evidência versionada está registrada em docs/benchmarks/v0.9.0.json:
| Repo | Achados | Regras | Status |
|---|---|---|---|
| openai-node, anthropic-sdk-typescript, anthropic-sdk-python, modelcontextprotocol/typescript-sdk, modelcontextprotocol/servers | 0 | — | limpo |
| anthropics/skills (18 pacotes de skills reais) | 0 | — | limpo — verificação pura de precisão para SKL001–005 |
| vercel/ai (5.691 arquivos) | 0 | — | era 40 (AI001, AI003, AI005, AI010, MCP002) antes da triagem — cada um revisado manualmente contra a fonte e confirmado como falso positivo, rastreado até 3 bugs independentes de causa raiz (veja abaixo), corrigido e re-confirmado limpo em um re-escaneamento completo |
| run-llama/llama_index | 46 | VEC001 | limite inerente, não um bug — as definições genéricas de retriever da própria biblioteca, onde nenhum filtro de tenant pode existir para ser encontrado |
| cisco-ai-defense/skill-scanner | 7 | SKL001, SKL002, SKL005 | todos em fixtures rotulados como malicious/ — 6/6 no escopo, 0 em qualquer coisa rotulada como safe/ |
A triagem do vercel/ai encontrou três bugs reais com causa raiz identificada — nenhum específico das regras de skills da v0.6.0, todos em lógica compartilhada usada em muitas regras:
resolveLlmSink tratava qualquer chamada resolvida para um módulo de SDK de LLM como uma invocação de modelo, independentemente do nome do método — sinalizando isToolUIPart (um type guard que o pacote ai exporta ao lado de generateText) como uma chamada de LLM. Isso sozinho causou 3 dos 5 grupos de achados (AI001, AI003, AI010).DANGEROUS_CALLEES em AI005 inclui "query" para sinks no estilo injeção de SQL, mas "query" também é um verbo legítimo de invocação de LLM/agente — claudeSdk.query({ prompt, options }), a chamada de modelo do próprio Claude Agent SDK, foi sinalizado como "saída de LLM passada para um sink perigoso" puramente por causa do nome de método compartilhado.REQUEST_SOURCES (duplicado de forma idêntica em MCP002, MCP010, VEC003) correspondia a um "params." simples — qualquer parâmetro de função convencionalmente chamado params, não necessariamente dados de requisição HTTP. Um validador de esquema de URL (assertOpenLinkParams(params: unknown)) foi sinalizado como "URL de servidor MCP de entrada do usuário".Todos os três corrigidos na causa raiz (não no call site específico) e fixados como fixtures permanentes em test-fixtures/. Detalhes completos em CHANGELOG.md.
3. Validação vulnerável-vs-corrigido — prova recall, não apenas precisão.
As duas camadas acima apenas verificam que o scanner permanece silencioso em código seguro. As verificações de advisory do DEP003 são validadas no sentido oposto: fixe um pacote em uma versão documentada como vulnerável e confirme que ele é sinalizado, depois fixe-o na versão corrigida e confirme que não é.```bash
node --test test/dependency-guard.test.js
covers: `[email protected]` (CVE-2025-6514, vulnerável) sinalizado / `[email protected]` (corrigido) limpo; `[email protected]` (antes do backdoor) limpo / `[email protected]` (depois — não existe patch legítimo para um pacote malicioso) ainda sinalizado; `llama-cpp-python==0.2.71` (CVE-2024-34359, do conjunto gerado por OSV) sinalizado / `==0.2.72` (corrigido) limpo, inclusive sob a normalização de nomes do PyPI (`llama_cpp_python`); e especificadores não fixados do tipo `langchain>=0.1.0` produzindo **zero** achados no relatório padrão. Construir este teste revelou uma lacuna real: o `DEP003` costumava corresponder avisos apenas pelo nome do pacote, sem nunca comparar de fato a versão declarada com o intervalo afetado do aviso — corrigido em [`src/scanner/semver.ts`](https://github.com/akanthed/secureai-scan/blob/main/src/scanner/semver.ts).
A ambiguidade é resolvida de forma diferente por tipo de aviso, deliberadamente. Um pacote **malicioso** dispara mesmo quando a versão declarada não pode ser resolvida — instalar um backdoor é irreversível, então ele falha em direção à sinalização. Uma **CVE** dispara em `proven` apenas quando a versão declarada é um pin exato comprovadamente dentro do intervalo afetado; versões não fixadas mas possivelmente afetadas caem para `heuristic` (somente com `--paranoid`). Aplicar a regra do tipo malicioso a um snapshot de CVE com 162 entradas colocaria um achado crítico em todo repositório que declara `langchain>=0.1.0` — ruído inacionável em escala.
## Roadmap
Consulte [`ROADMAP.md`](https://github.com/akanthed/secureai-scan/blob/main/ROADMAP.md) para saber o que foi lançado e o que está planejado. Ambos os mecanismos de linguagem são baseados em AST: ts-morph para TypeScript/JavaScript e Tree-sitter para Python. Imports, chamadas, atribuições, decoradores, escopos, argumentos nomeados, campos de dicionário e strings em Python são nós de sintaxe; o código-alvo nunca é importado ou executado, e nenhum interpretador Python é necessário. A lacuna restante do Python é a profundidade limitada de taint entre funções/arquivos, não a análise. O desempenho da varredura e os limites conhecidos estão documentados em [`docs/Performance.md`](https://github.com/akanthed/secureai-scan/blob/main/docs/Performance.md).
## Contribuindo
Contribuições são bem-vindas — consulte [`CONTRIBUTING.md`](https://github.com/akanthed/secureai-scan/blob/main/CONTRIBUTING.md) para o fluxo de trabalho, e [`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) para saber como adicionar uma regra de detecção que atenda ao padrão de precisão acima. Cada nova regra precisa de um fixture tanto em [`test-fixtures/vulnerable/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/vulnerable) quanto em [`test-fixtures/safe/`](https://github.com/akanthed/secureai-scan/blob/main/test-fixtures/safe), uma entrada em `src/scanner/catalog.ts` e um caso em `test/corpus.test.js` — `npm test` aplica os três.
## Licença
MIT © Akshay Kanthed
| MCP009 |
| Uma descrição de ferramenta que direciona chamadas para uma ferramenta diferente (sombreamento) |
| LLM01 · MCP03 |
| MCP010 | Comando/argumentos do servidor MCP stdio construídos a partir de entrada do usuário (RCE) | LLM04 · MCP05 |
| SKL001 | Unicode invisível/bidi em qualquer lugar em um pacote de Skill de Agente | LLM01 |
| SKL002 | Fraseado de injeção direcionado a agentes na descrição ou no corpo de uma skill (correspondido por meio de ofuscação) | LLM01 |
| SKL003 | O conteúdo de uma skill direciona quando/como uma skill diferente é usada (sombreamento) | LLM01 |
| SKL004 | Payload encenado/autoextraível: blob opaco + instruções para decodificá-lo e executá-lo | LLM04 · MCP04 |
| SKL005 | Leitura de credenciais + egresso externo codificado em um arquivo complementar do pacote | LLM02 · MCP04 |
| SKL006 | Execução de comando em tempo de carregamento via sintaxe de injeção de contexto dinâmico do Claude Code (!`cmd`/```!), antes de qualquer etapa de permissão de ferramenta | LLM04 · MCP05 |
| SKL007 | Concessão de Bash sem escopo no frontmatter allowed-tools de uma skill | LLM03 |
| SKL008 | Skill busca instruções de uma URL externa e direciona o agente a segui-las ("Circus of Skills") | LLM04 |
| SKL009 | Skill persiste uma backdoor gravando em outro arquivo de contexto (MEMORY.md/SOUL.md/AGENTS.md/CLAUDE.md) | LLM05 |
| SKL010 | Tag insegura de desserialização YAML/JSON no frontmatter de uma skill ou em um arquivo de configuração empacotado | LLM04 |
| VEC001 | Busca vetorial sem filtro de locatário/usuário | LLM09 |
| VEC002 | Limite de busca sem limites ou controlado pelo usuário | LLM06 |
| VEC003 | Conteúdo do usuário ingerido em um armazenamento vetorial compartilhado | LLM05 |
| VEC004 | Ingestão sem marcação de locatário/namespace | LLM09 |
| DEP001 | Nome de dependência não encontrado no registro (opt-in --check-dependencies) | LLM04 |
| DEP002 | Nome de dependência a uma edição de distância de um pacote popular (opt-in) | LLM04 |
| DEP003 | Dependência com lançamento malicioso documentado ou CVE crítico — verificado offline em cada varredura, ciente de faixa de versão (postmark-mcp, mcp-remote CVE-2025-6514, …) | LLM04 · MCP04 |
| LLC001 | Segredo codificado em um config.yaml do proxy LiteLLM | LLM02 |
| LLC002 | api_base do proxy LiteLLM acessível sobre HTTP sem criptografia | LLM04 |
| LLC003 | Configuração do proxy LiteLLM sem seção guardrails: (heurística, somente --paranoid) | LLM03 |