Voltar às atualizações
New releaseAug 21, 2026

mcp-stdio-shellguard v0.1.2

Pacote de defesa em profundidade para servidores MCP stdio: wrappers guardExec/guardSpawn de substituição, CLI de auditoria AST, servidor MCP de referência. Fecha a classe de RCE via stdio de 200k servidores do Ox-Security (CVE-2025-69256 do LiteLLM). MIT, TypeScript, Node >= 20.

Compartilhar

Parte do StudioMeyer MCP Stack — Construído em Mallorca 🌴 · ⭐ se você usa

mcp-stdio-shellguard

npm version npm downloads License Last commit GitHub stars

Pacote de defesa em profundidade para servidores MCP stdio. Encapsula child_process.exec/spawn com lista de permissões + sandbox + detecção de replay, além de uma CLI de auditoria AST (mcp-shellguard-audit) que escaneia fontes de servidores MCP em busca de chamadas de shell não sanitizadas. Fecha a classe Ox-Security MCP stdio-RCE (200k servidores vulneráveis, divulgação de maio de 2026).

  • Especificação MCP: 2025-06-18
  • SDK: @modelcontextprotocol/sdk ^1.29.0
  • Node: >= 20
  • Licença: MIT
  • Autor: Matthias Meyer (StudioMeyer)

Instalação

npm install mcp-stdio-shellguard

Ou execute a CLI de auditoria diretamente sem instalar:

npx -y -p mcp-stdio-shellguard mcp-shellguard-audit scan ./src

O que ele oferece

Três camadas, opt-in por partes:

  1. API de BibliotecaguardExec / guardSpawn de substituição que você chama do seu próprio servidor MCP. Lista de permissões com negação padrão, perfis de sandbox, janela de replay.
  2. CLI de Auditoriamcp-shellguard-audit scan <caminho> percorre a AST, relata 12 anti-padrões de LOW (no timeout) a CRITICAL (exec(\...${userInput}...`)`).
  3. Servidor MCP de referênciamcp-stdio-shellguard-demo expõe 8 ferramentas para que o MCP Inspector / Claude Desktop possam controlar o pacote diretamente.

Ferramentas (servidor de referência)

FerramentaTipoPropósito
guard_execdestrutivachild_process.exec defendido. Força vetor args[], lista de permissões + sandbox + replay. Retorna stdout, stderr, exitCode, canonicalHash, isReplay, trustTier.
guard_spawndestrutivachild_process.spawn defendido. Retorna hashes SHA-256 de stdout/stderr em vez dos corpos completos. Rejeita shell:true rigidamente.
register_allowlistmutávelRegistra um nome de ferramenta com executável + regex de args. Sem registro, a negação padrão se aplica.
audit_sourcesomente leituraEscaneia um caminho TS/JS em busca de anti-padrões de injeção de shell. Retorna AuditFinding[] + resumo.
audit_reportsomente leituraFormata um resultado de auditoria como markdown / json / SARIF 2.1.0.
replay_checksomente leituraCalcula o hash SHA-256 canônico para uma invocação e informa se já está na janela de replay.
sandbox_statussomente leituraInforma o perfil de sandbox ativo + limites concretos + flag de atividade cgroup-v2.
trust_tiersomente leituraDeriva nível LOW/MEDIUM/HIGH/CRITICAL para uma ferramenta registrada mais dicas de melhoria.

Perfis de sandbox

PerfilTimeoutmáx. stdoutmáx. stderrOrçamento FDcgroup-v2
strict5 s1 MB256 KB32sim (cpu/memory)
standard (padrão)30 s10 MB1 MB256sim
permissive5 min100 MB10 MB1024não

O chamador pode restringir via timeoutMs / fdBudget por chamada. O chamador não pode ampliar além do perfil.

Níveis de confiança

NívelCondição
LOWferramenta não registrada (negação padrão)
MEDIUMregistrada mas argsPatterns vazio (qualquer argumento permitido)
HIGHargsPatterns definido mas sandbox ou rastreador de replay inativo
CRITICALargsPatterns + sandbox + replay todos ativos

Eleve LOW → CRITICAL registrando a ferramenta + definindo argsPatterns + executando via guardExec/guardSpawn (que sempre ativam sandbox + replay).

Início rápido da biblioteca

import {
  AllowlistRegistry,
  ReplayWindow,
  guardExec,
} from "mcp-stdio-shellguard";

const registry = new AllowlistRegistry();
const replay = new ReplayWindow();

registry.register({
  toolName: "git-log",
  executable: "/usr/bin/git",
  argsPatterns: ["^log$", "^--oneline$", "^-n$", "^\\d+$"],
  sandboxProfile: "strict",
});

const result = await guardExec(
  {
    toolName: "git-log",
    command: "/usr/bin/git",
    args: ["log", "--oneline", "-n", "10"],
  },
  { registry, replay },
);

console.log(result.stdout); // → commit lines
console.log(result.trustTier); // → "CRITICAL"
console.log(result.canonicalHash); // → 64-char SHA-256

CLI de auditoria

mcp-shellguard-audit scan ./src
mcp-shellguard-audit scan ./src --format sarif --output audit.sarif
mcp-shellguard-audit scan ./src --severity-floor HIGH    # CI gate

Códigos de saída:

  • 0 limpo (nenhuma descoberta no ou acima do piso)
  • 1 descobertas presentes
  • 2 erros de análise / E/S

Biblioteca de anti-padrões (12 regras)

IDSeveridadeAciona em
exec_template_literal_with_inputCRITICALchild_process.exec(\ls ${x}`)`
exec_dynamic_stringCRITICALchild_process.exec(cmd)
exec_sync_dynamic_stringCRITICALchild_process.execSync(cmd)
eval_near_child_processCRITICALeval(...)
function_constructor_near_child_processCRITICALnew Function(...)
spawn_dynamic_file_argsHIGHspawn(bin, userArgs)
exec_file_dynamicHIGHexecFile(bin, ...)
shell_true_optionHIGH{ shell: true }
os_system_equivalentHIGHDeno.run / Bun.spawn
spawn_literal_dynamic_argsMEDIUMspawn('git', userArgs)
unbounded_bufferLOWexec sem maxBuffer
missing_timeoutLOWexec/spawn sem timeout

O scanner resolve renomeações de ligações child_process antes da correspondência, então as formas perigosas abaixo são capturadas mesmo quando a chamada passa por um alias em vez de um child_process.exec literal:

  • const execAsync = promisify(exec); execAsync(...${x})
  • import cp from "node:child_process"; cp.exec(...${x})
  • const { exec: sh } = require("child_process"); sh(...${x})
  • import { exec as run } from "node:child_process"; run(...)

Variantes síncronas (spawnSync, execFileSync) compartilham suas regras assíncronas, e shell_true_option também dispara em um shell string ({ shell: "/bin/sh" }) ou um valor de shell dinâmico — não apenas o literal { shell: true }. Um promisify de uma função não child_process, uma desestruturação de outro módulo e { shell: false } permanecem limpos (sem falsos positivos).

Pragmas

  • // shellguard:ignore-next-line — suprime uma descoberta
  • // shellguard:ignore-file — suprime o arquivo inteiro (raro; prefira por linha)

Por que isso existe

A Ox-Security divulgou (2026-05) que 200k+ servidores MCP stdio encapsulam child_process.exec com template literals carregando entrada do usuário diretamente dos args de ferramentas LLM. LiteLLM v1.83.6 foi o exemplo canônico (CVE corrigido em 1.83.7). Este pacote é a contraparte de segurança defensiva: um guarda + scanner de substituição que fecha a classe. Inspirado por AWS Linux seccomp + níveis de sandbox do Chromium.

Veja também

Licença

MIT — Copyright (c) 2026 Matthias Meyer (StudioMeyer)

Categorias