
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.
Parte do StudioMeyer MCP Stack — Construído em Mallorca 🌴 · ⭐ se você usa
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).
@modelcontextprotocol/sdk ^1.29.0npm install mcp-stdio-shellguard
Ou execute a CLI de auditoria diretamente sem instalar:
npx -y -p mcp-stdio-shellguard mcp-shellguard-audit scan ./src
Três camadas, opt-in por partes:
guardExec / 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.mcp-shellguard-audit scan <caminho> percorre a AST, relata 12 anti-padrões de LOW (no timeout) a CRITICAL (exec(\...${userInput}...`)`).mcp-stdio-shellguard-demo expõe 8 ferramentas para que o MCP Inspector / Claude Desktop possam controlar o pacote diretamente.O chamador pode restringir via timeoutMs / fdBudget por chamada. O chamador não pode ampliar além do perfil.
| Nível | Condição |
|---|---|
| LOW | ferramenta não registrada (negação padrão) |
| MEDIUM | registrada mas argsPatterns vazio (qualquer argumento permitido) |
Eleve LOW → CRITICAL registrando a ferramenta + definindo argsPatterns + executando via guardExec/guardSpawn (que sempre ativam sandbox + replay).
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
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 presentes2 erros de análise / E/SO 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).
// shellguard:ignore-next-line — suprime uma descoberta// shellguard:ignore-file — suprime o arquivo inteiro (raro; prefira por linha)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.
HOOK_RECIPES.md — receitas de hook do Claude Code que bloqueiam automaticamente chamadas de ferramentas perigosasCHANGELOG.md — histórico de versõesMIT — Copyright (c) 2026 Matthias Meyer (StudioMeyer)
| Ferramenta | Tipo | Propósito |
|---|
guard_exec | destrutiva | child_process.exec defendido. Força vetor args[], lista de permissões + sandbox + replay. Retorna stdout, stderr, exitCode, canonicalHash, isReplay, trustTier. |
guard_spawn | destrutiva | child_process.spawn defendido. Retorna hashes SHA-256 de stdout/stderr em vez dos corpos completos. Rejeita shell:true rigidamente. |
register_allowlist | mutável | Registra um nome de ferramenta com executável + regex de args. Sem registro, a negação padrão se aplica. |
audit_source | somente leitura | Escaneia um caminho TS/JS em busca de anti-padrões de injeção de shell. Retorna AuditFinding[] + resumo. |
audit_report | somente leitura | Formata um resultado de auditoria como markdown / json / SARIF 2.1.0. |
replay_check | somente leitura | Calcula o hash SHA-256 canônico para uma invocação e informa se já está na janela de replay. |
sandbox_status | somente leitura | Informa o perfil de sandbox ativo + limites concretos + flag de atividade cgroup-v2. |
trust_tier | somente leitura | Deriva nível LOW/MEDIUM/HIGH/CRITICAL para uma ferramenta registrada mais dicas de melhoria. |
| Perfil | Timeout | máx. stdout | máx. stderr | Orçamento FD | cgroup-v2 |
|---|
strict | 5 s | 1 MB | 256 KB | 32 | sim (cpu/memory) |
standard (padrão) | 30 s | 10 MB | 1 MB | 256 | sim |
permissive | 5 min | 100 MB | 10 MB | 1024 | não |
| HIGH | argsPatterns definido mas sandbox ou rastreador de replay inativo |
| CRITICAL | argsPatterns + sandbox + replay todos ativos |
| ID | Severidade | Aciona em |
|---|
exec_template_literal_with_input | CRITICAL | child_process.exec(\ls ${x}`)` |
exec_dynamic_string | CRITICAL | child_process.exec(cmd) |
exec_sync_dynamic_string | CRITICAL | child_process.execSync(cmd) |
eval_near_child_process | CRITICAL | eval(...) |
function_constructor_near_child_process | CRITICAL | new Function(...) |
spawn_dynamic_file_args | HIGH | spawn(bin, userArgs) |
exec_file_dynamic | HIGH | execFile(bin, ...) |
shell_true_option | HIGH | { shell: true } |
os_system_equivalent | HIGH | Deno.run / Bun.spawn |
spawn_literal_dynamic_args | MEDIUM | spawn('git', userArgs) |
unbounded_buffer | LOW | exec sem maxBuffer |
missing_timeout | LOW | exec/spawn sem timeout |