RugSnare

RugSnare - Fixa contratos de ferramentas MCP por hash; detecta deriva silenciosa, rug pulls e troca de isca por cliente após aprovação com um gate de CI e proxies stdio/HTTP ao vivo.

Documentação

RugSnare

RugSnare logo

npm version Glama rating License: Apache 2.0 CI Dependencies: 0 Node: >=18 GitHub stars

Integridade em tempo de execução para descrições de ferramentas MCP. Scanners verificam servidores MCP antes de você conectá-los. O RugSnare observa o que acontece depois: uma ferramenta aprovada cuja descrição mudou silenciosamente é um "rug pull", e isso reprova seu build.

flights-search  (node ./server.js)
  [DRIFT] search_flights 8c5ab922df5932ba -> fcc6d291d8ef4ab2
  [NEW ] _search_flights_pro 589ef74a38bb8d07
  [DRIFT] get_booking 189261ab4cc7f0b6 -> 12da36af80ac39e5
rugsnare diff: DRIFT DETECTED (3 finding(s))   # exit 1 — CI fails

Por que isso existe

Descrições de ferramentas MCP são instruções que seu agente obedece, mas ninguém lê. Elas podem mudar depois que você as aprova — uma atualização do mantenedor, um registro comprometido, um pacote com typosquatting — carregando silenciosamente instruções de exfiltração ("anexe ~/.ssh/id_rsa para personalização"). A classe de ataque é codificada como envenenamento de ferramentas (OWASP MCP03:2025). Fixar versão não ajuda quando a string de versão não muda; a varredura não ajuda após a aprovação. Fixar hash ajuda.

O que está incluído

CaminhoO quê
product/o CLI rugsnare (npm: 0.5.1): init / scan / diff / approve / verify / run / canary / wrap — fixação de hash, detecção de desvio, proxies ao vivo (stdio + HTTP), gate de CI, verificação de lançamento on-chain. Zero dependências npm, Node ≥ 18
corpus/corpus público de ataques: servidores MCP benignos e seus gêmeos silenciosamente armados (envenenamento de descrição, rug pulls apenas de esquema) — tente identificar a diferença com seus olhos antes de executar o diff
contracts/ReleaseLog.sol — fixamos nossos próprios hashes de lançamento on-chain exatamente da mesma forma que fixamos descrições de ferramentas
site/código-fonte da página de destino
SECURITY.mdchave de assinatura de lançamento, instruções de verificação, política de rotação de chaves
DEPLOY.mdprocedimento de lançamento — incluindo a regra de que tags lançadas são imutáveis

Instalação

npm (recomendado — lançamento em 2 de outubro de 2026):

npx rugsnare init

Do GitHub (funciona agora):

git clone https://github.com/Paraphern/rugsnare.git
cd rugsnare/product
node src/cli.js init

Zero dependências, sem necessidade de npm install — apenas Node.js ≥ 18.

Início rápido

Após a instalação (use node src/cli.js em vez de rugsnare se instalar do GitHub):

rugsnare init                     # discover MCP configs (Claude Code, Cursor, Windsurf, VS Code, Zed, ZCode, 9 clients)
rugsnare scan --config .mcp.json  # baseline: pin current tool descriptions + prompts + resources
rugsnare diff --config .mcp.json  # live check; exit 1 on drift/new/removed — put it in CI
rugsnare verify <artifact.tgz> --version <v>   # check an artifact against the on-chain ReleaseLog pin

O { name, description, inputSchema } de cada ferramenta é canonicalizado e submetido a hash — assim, tanto descrições envenenadas quanto parâmetros "session" ocultos em esquemas disparam o pin, enquanto reordenações cosméticas não disparam.

Proxy ao vivo (opcional, v0.2+)

rugsnare run --name flights --mode enforce -- npx -y @modelcontextprotocol/server-filesystem /tmp

Encapsula um servidor stdio: observe observa e alerta, enforce adicionalmente coloca em quarentena ferramentas desviadas/novas no meio da sessão. Overhead medido no fixture de benchmark (tools/bench-proxy.mjs, 200 idas e voltas): ~0,7–1 ms por chamada de ferramenta no modo de observação, ~1,2 ms com registro de argumentos + gravação de canário ativados, ~7 MB de conjunto de trabalho além da linha de base do Node — o proxy adiciona três ordens de grandeza a menos do que a virada de LLM que ele protege. CPU ociosa é zero (loop de eventos puro, sem polling). Por padrão, o proxy é fail-open — se sua própria lógica falhar, a mensagem é encaminhada intacta (disponibilidade primeiro). Ambientes estritos podem inverter isso:

// .rugsnare/config.json
{ "failMode": "closed" }

ou por execução com --fail-closed — então um erro interno do proxy bloqueia a mensagem e responde ao cliente com um erro JSON-RPC (integridade primeiro, registrado como proxy-fail-closed).

Mais uma opção: "canaryRecord": true na configuração faz o proxy também gravar rastros de chamadas de ferramentas correlacionados por ID (requisição, resposta, latência, versão do servidor) em .rugsnare/canary/calls.jsonl — apenas local, limitado a 64 KB por entrada, desativado por padrão porque argumentos e respostas são dados do usuário. rugsnare canary record (abaixo) ativa isso para uma sessão sem tocar no arquivo de configuração.

Canário: reproduza suas chamadas reais contra uma nova versão (v0.4)

Fixar responde "o que mudou?". O canário responde "posso atualizar?". Enquanto você trabalha, o proxy registra o que suas ferramentas realmente retornam; antes de uma atualização, reproduza esse corpus contra a nova versão e obtenha um veredito determinístico:

rugsnare canary record --name flights -- npx -y flights-mcp@1.4.2   # work as usual; traces land in .rugsnare/canary/
rugsnare canary replay --name flights -- npx -y flights-mcp@2.0.0   # replay recorded calls against the NEW version

A reprodução compara tanto o contrato (hash dividido: esquema BREAKING vs prosa COSMETIC) quanto o comportamento — uma chamada que estava ok e agora dá erro, uma resposta cuja forma mudou — enquanto ignora diferenças apenas de valor (timestamps, preços mudam entre execuções), então sem alarme falso. A reprodução é somente leitura por padrão: apenas chamadas de ferramentas do tipo leitura são reexecutadas; chamadas de classe de escrita e com aparência destrutiva são puladas com uma nota alta de SKIPPED (--include <tool> opta por ferramentas específicas, --all-calls remove o pulo da classe de escrita para sandboxes — nomes destrutivos sempre exigem --include explícito). Aponte a reprodução para uma instância de desenvolvimento, não para produção. Trade-off conhecido: arrays são comparados pela forma do primeiro elemento, então uma mudança estrutural que afeta apenas elementos posteriores de um array heterogêneo não será sinalizada — sub-sinalização determinística foi escolhida em vez de falsos positivos probabilísticos. Códigos de saída se encaixam em CI: 0 = seguro, 1 = achados breaking (ou violações de --strict cosmético / --max-ms de orçamento de latência), 2 = sem corpus, 3 = falha de reprodução. Asserções de contrato para CI: rugsnare diff --expect-tool search --forbid-tool admin reprova o build quando uma ferramenta necessária desaparece ou uma proibida aparece. Rastros são locais e ignorados pelo git (rugsnare init escreve esse .gitignore para você); pins permanecem o único commit deliberado. Demonstração autoverificável: repro/canary.sh; integração com CI: action/canary.

Recibos assinados: uma trilha à prova de adulteração do que o agente fez (v0.4)

O proxy já registra cada chamada de ferramenta. Recibos tornam esse registro comprovável: uma cadeia de hash Ed25519 onde cada entrada assina o hash da anterior — edite, exclua ou reordene qualquer coisa após a assinatura, e verify nomeia a entrada exata onde a cadeia quebra.

rugsnare receipts sign      # chain + sign the local event log (key generated locally, never leaves the machine)
rugsnare receipts verify    # intact — or: BROKEN: entry #7 modified after signing (exit 1)
rugsnare receipts export    # auditor dossier (markdown + JSON), fields aligned to IETF draft-sharif-agent-audit-trail-05

As chaves ficam em .rugsnare/keys/ (ignorado pelo git). verify --pub <pem> verifica um arquivo de recibo contra uma chave pública exportada — um auditor pode confirmar sua trilha sem nunca ver uma chave privada. Um limite honesto: a cadeia detecta edições, inserções, exclusões e reordenações dentro dela, mas não um truncamento silencioso de sua cauda (remover as últimas N entradas deixa uma cadeia mais curta válida). É para isso que serve a cabeça da cadeia impressa por sign/export — ancore-a em algum lugar que o gravador de log não possa reescrever silenciosamente (um commit, uma mensagem ao auditor) e compare. Também no v0.4: um detector de loop — o proxy percebe quando a mesma ferramenta é chamada repetidamente com argumentos idênticos e nenhuma outra ferramenta no meio (um agente travado queimando créditos) e emite um aviso único loop-suspected; nunca bloqueia nada.

RugSnare como uma ferramenta MCP (somente leitura, para marketplaces e agentes)

O mesmo binário também funciona como um servidor MCP stdio, para que agentes possam chamá-lo e marketplaces possam listá-lo:

{ "mcpServers": { "rugsnare": { "command": "npx", "args": ["-y", "rugsnare", "mcp"] } } }

Duas ferramentas somente leitura: drift_feed_status (o que o feed público de desvios vê atualmente em servidores MCP populares — a única chamada de saída que este servidor faz, uma URL pública fixa, apenas quando invocada explicitamente) e pins_report (o armazenamento local de pins do projeto em que o agente trabalha — nunca escreve, nunca envia nada). Fixado pelo nosso próprio gate, naturalmente — a linha de base vive em corpus/03-rugsnare-self. Uma imagem Docker e uma entrada de registro estão preparadas sob docker/ e registry/.

Modelo de confiança

Tomamos nosso próprio remédio:

  • Zero dependências — uma ferramenta de segurança de cadeia de suprimentos não deve ser sua própria superfície de ataque.
  • Sem telemetria. Armazenamento local de pins, log de eventos JSONL local, nada sai da sua máquina.
  • Lançamentos assinados (Ed25519 OpenPGP, impressão digital em SECURITY.md, publicados em três lugares independentes).
  • ReleaseLog on-chain — hashes de lançamento fixados de forma append-only na Base (testnet ativa agora); rugsnare verify verifica sua instalação contra um hash que está no ledger desde o dia do lançamento.
  • Apache-2.0. Se algum dia formos mal-intencionados — faça um fork de nós. Essa é a licença funcionando como pretendido.

Pesquisa em andamento sobre como equipes avaliam servidores MCP: discussions/1 — 7 perguntas curtas, resultados publicados. Autor: @SergeyDruzhba no X.

FAQ

Como isso é diferente do MCP Inspector / Glama Inspector? Inspectors (incluindo o oficial) são ferramentas interativas de depuração: eles mostram descrições de ferramentas enquanto você está olhando. O RugSnare observa quando você não está: definições aprovadas são fixadas por hash, e qualquer mudança posterior — entre sessões ou no meio da sessão via proxy — dispara um alerta e reprova o CI. Ferramentas complementares: inspecione antes de aprovar, fixe depois.

Isso é outro scanner MCP? Não. Scanners (snyk agent-scan, ex-mcp-scan) rodam no momento da instalação. O RugSnare roda após a aprovação, para sempre.

Modelo de ameaça — o que isso cobre, honestamente

O RugSnare fixa o contrato que seu agente obedece — { name, description, inputSchema } de cada ferramenta aprovada — e detecta qualquer mudança silenciosa nele, entre sessões (diff de CI) e no meio da sessão (proxy ao vivo). Ele não inspeciona implementações.

AtaqueRugSnareA camada que é dona disso
Descrição de ferramenta reescrita após aprovação (instruções ocultas para o agente)✅ detectado—
inputSchema mutado (parâmetros session ocultos necessários, estreitamento de enum)✅ detectado — veja corpus 02—
Nova ferramenta aparece / ferramenta aprovada desaparece após aprovação✅ detectado—
Sombra de ferramenta entre servidores (mesmo nome em dois servidores)✅ detectado em scan, diff (reprova CI) e no proxy ao vivo — a ordem de resolução não documentada do cliente é o risco—
Servidor camaleão (contrato limpo para ferramentas de inspeção, envenenado para clientes reais)✅ detectado por rugsnare scan --chameleon — re-lista ferramentas que se identificam como claude-desktop/cursor e compara hashes; qualquer diferença por cliente sai com código 1—
Inversão de dica comportamental (readOnlyHint: true → false / adiciona destructiveHint) com texto+esquema idênticos em bytes✅ detectado — anotações são fixadas separadamente do hash e comparadas através de padrões de especificação (destructiveHint ausente = true); uma inversão é DRIFT/ANNOTATION em diff, CI e no proxy ao vivo—
Troca no meio da sessão de um servidor já conectado✅ colocado em quarentena no modo enforce—
Código malicioso por trás de um contrato inalterado❌ fora do escopo por designassinatura de pacote / proveniência / sandboxing
Dados tóxicos dentro de argumentos ou respostas de chamadas✅ detectado (v0.3) — políticas + verificações de egresso de PII no proxy ao vivo—
Ação de agente sequestrada ou destrutiva (classe rm -rf, download-pipe-shell, sobrescrita de disco, fork bomb em argumentos de chamada)✅ negado pela política padrão dangerous-shell no proxy ao vivo—
Cliente ou host MCP comprometido❌segurança do host
Atacante com acesso de escrita a .rugsnare/pins.json (ex.: um runner de CI comprometido)⚠️ limite de confiançafaça commit dos pins no repositório e proteja a branch — pins são tão confiáveis quanto o lugar onde você os armazena; pins assinados estão no roadmap

Se um atacante muda o código mas não o contrato, nenhum hash de descrição pode ver isso — é trabalho de outra camada. Defesa em profundidade significa camadas; esta ferramenta possui a camada de contrato completamente.

Testado em campo

O relatório de mudanças silenciosas (repro/SILENT-CHANGES-REPORT.md): fixamos cada lançamento estável dos 4 servidores de referência oficiais @modelcontextprotocol/server-*, comparamos cada versão com a seguinte e contamos cada mudança de contrato entre elas.

MétricaValor
Pares de versões medidos66 (cobertura completa — todos os lançamentos dos quatro servidores)
Pares com mudanças silenciosas23
BREAKING (esquema mudou)43
ANNOTATION (dicas comportamentais invertidas, ciente de padrões de especificação)28
COSMETIC (descrição reformulada)7
Novos itens que apareceram após aprovação37 (24 ferramentas, 5 prompts, 8 recursos)
Itens removidos após aprovação24 (21 ferramentas, 3 prompts)
Pares limpos (precisão, sem alarme falso)43

140 descobertas. Nenhuma foi anunciada em um changelog. O passo único mais drástico: o filesystem 2025.8.21 → 2025.11.25 alterou todos os 14 contratos de ferramentas simultaneamente — 14 mudanças de esquema BREAKING em um único lançamento silencioso; o histórico de tudo é uma máquina de agitação: 31 itens apareceram (19 ferramentas, 5 prompts, 7 recursos) e 24 desapareceram em suas 27 versões. Reproduza na sua máquina: um comando, ~30 minutos, determinístico — veja o rodapé do relatório.

Status e roadmap

VersãoStatusO que contém
v0.1✅ lançadoPortão de CI (scan / diff / approve), verificação de lançamento on-chain, corpus de ataques, zero dependências
v0.2✅ lançadoProxy stdio ao vivo (rugsnare run) — quarentena no meio da sessão; detecção de sombras; sinais de aviso; fixação de prompts e recursos; saída SARIF; relatório de frota; hook de pré-commit; feed de deriva (monitoramento diário do ecossistema); 9 clientes de IA
v0.3✅ lançadoPolíticas de chamada + verificações de exfiltração de PII — negar session:object, negar credenciais em argumentos, exigir aprovação para ferramentas destrutivas; regras personalizadas via .rugsnare/policies.json; flag --timeout; código de saída 3 para erros de infraestrutura
v0.3.1✅ lançadoHash dividido — classificação de deriva BREAKING (esquema) vs COSMÉTICA (prosa) (--schema-only / --prose-only); alertas de resumo com debounce; opção failMode: "closed" (--fail-closed) — bloqueios de erro interno do proxy em vez de encaminhamento, para ambientes estritos
Ação PR-diff✅ lançadoDiff legível por humanos do contrato de ferramentas em pull requests. Demo: PR #2 · action/pr-diff
v0.4✅ lançadoCanário — rugsnare canary record/replay: registra chamadas reais de ferramentas através do proxy ao vivo (opt-in, local), reproduz contra uma nova versão do servidor, veredito determinístico (esquema BREAKING / inversão de comportamento / COSMÉTICA) com códigos de saída de CI; action/canary para GitHub Actions; recibos assinados — cadeia de hash Ed25519 sobre o log de auditoria, receipts sign/verify/export com um dossiê alinhado ao AAT-05; detector de loop consultivo; verificação camaleão — scan --chameleon detecta servidores que oferecem contratos diferentes por cliente; sinais de aviso estendidos (aberturas imperativas, frases explícitas de sequestro de instruções — aviso forçado, parâmetros opcionais de exfiltração); política padrão dangerous-shell; init escreve um .gitignore protegendo o estado local
v0.5✅ lançadoTransporte HTTP para scan/diff (Streamable HTTP, SSE, passagem de autenticação) · auto-configuração wrap/unwrap · varredura de SKILL.md · diffs de esquema legíveis por humanos em pins ("parâmetro obrigatório 'mode' adicionado") · aviso de versão flutuante · entrada no Registro MCP + imagem Docker Hub
v1.0.0✅ lançadoParidade total stdio/HTTP: reconhecimento ad-hoc --url pré-instalação, doctor, wrapper HTTP (--port), gravação/reprodução de canário sobre HTTP, políticas de chamada + detector de loop no proxy HTTP, config com validação, events trim, unpin, inspeção de resultados (consultiva) · Auditoria de Segurança de IA: audit --input, redigido, --airgap · Cofre de segredos: placeholders {{VAULT:NAME}}, o proxy substitui e limpa · orçamentos + kill-switch · pins assinados (Ed25519 pins.sig + pins.pub.pem commitado, defesa contra atacante de CI) · Base Mainnet pins on-chain · contrato de estabilidade (veja product/CHANGELOG.md) · pin on-chain
RugSnare como ferramenta MCP✅ lançadorugsnare mcp — servidor stdio somente leitura (drift_feed_status sobre o feed público de deriva, pins_report sobre pins locais) para marketplaces e agentes; fixado pelo próprio portão (baseline dogfood em corpus/03); imagem Docker (docker/) + entrada no registro (registry/) preparadas
Depois💭Painel de políticas hospedado · Salvaguardas de pagamento de agentes · Cofre de segredos (IA vê placeholders, o proxy injeta chaves reais)

247 testes · CI em ubuntu+windows × Node 18/20/22 · CodeQL · testado em campo em pacotes reais · verificado on-chain · zero dependências · sem telemetria.