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.
<!-- studiomeyer-mcp-stack-banner:start -->
> **Parte do [StudioMeyer MCP Stack](https://studiomeyer.io)** — Construído em Mallorca 🌴 · ⭐ se você usar
<!-- studiomeyer-mcp-stack-banner:end -->
# mcp-stdio-shellguard
<!-- badges -->
[](https://www.npmjs.com/package/mcp-stdio-shellguard)
[](https://www.npmjs.com/package/mcp-stdio-shellguard)



<!-- /badges -->Pacote de defesa em profundidade para servidores MCP stdio. Envolve `child_process.exec/spawn`
com allowlist + sandbox + detecção de replay, além de uma CLI de auditoria AST (`mcp-shellguard-audit`)
que escaneia os códigos-fonte de servidores MCP em busca de chamadas de shell não sanitizadas. Fecha a classe
de RCE via stdio em MCP da Ox-Security (200 mil servidores vulneráveis, divulgação de maio de 2026).
- **MCP spec**: 2025-06-18
- **SDK**: `@modelcontextprotocol/sdk` ^1.29.0
- **Node**: >= 20
- **License**: MIT
- **Author**: Matthias Meyer (StudioMeyer)
## Install
```bash
npm install mcp-stdio-shellguard
```
Ou execute a CLI de auditoria diretamente sem instalar:
```bash
npx -y -p mcp-stdio-shellguard mcp-shellguard-audit scan ./src
```
## O que ele oferece
Três camadas, opcionais e ativáveis individualmente:
1. **API de biblioteca** — `guardExec` / `guardSpawn` prontos para uso, chamados a partir do seu
próprio servidor MCP. Allowlist com negação por padrão, perfis de sandbox, janela de replay.
2. **CLI de auditoria** — `mcp-shellguard-audit scan <path>` percorre a AST e reporta
12 anti-padrões, de LOW (`no timeout`) a CRITICAL (`exec(\`...${userInput}...\`)`).
3. **Servidor MCP de referência** — `mcp-stdio-shellguard-demo` expõe 8 ferramentas
para que o MCP Inspector / Claude Desktop possam operar o pacote diretamente.
## Ferramentas (servidor de referência)
| Ferramenta | Tipo | Finalidade |
|------|------|---------|
| `guard_exec` | destrutiva | `child_process.exec` protegido. Força vetor args[], allowlist + sandbox + replay. Retorna `stdout`, `stderr`, `exitCode`, `canonicalHash`, `isReplay`, `trustTier`. |
| `guard_spawn` | destrutiva | `child_process.spawn` protegido. Retorna hashes SHA-256 de stdout/stderr em vez dos corpos completos. Rejeita firmemente `shell:true`. |
| `register_allowlist` | mutante | Registra um nome de ferramenta com executável + regex de args. Sem registro, aplica-se a negação por padrão. |
| `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 de uma invocação e informa se ela já está na janela de replay. |
| `sandbox_status` | somente leitura | Reporta o perfil de sandbox ativo + limites concretos + flag de cgroup-v2 ativo. |
| `trust_tier` | somente leitura | Deriva o nível LOW/MEDIUM/HIGH/CRITICAL de uma ferramenta registrada, além de dicas de melhoria. |
## Perfis de sandbox
| Perfil | Timeout | stdout máx. | stderr máx. | Orçamento de 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 |
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ível | Condição |
|------|-----------|
| LOW | ferramenta não registrada (negação por padrão) |
| MEDIUM | registrada, mas `argsPatterns` vazio (quaisquer args permitidos) |
| HIGH | `argsPatterns` definido, mas sandbox ou rastreador de replay inativo |
| CRITICAL | argsPatterns + sandbox + replay todos ativos |
Eleve de LOW → CRITICAL registrando a ferramenta + definindo argsPatterns + executando
através de `guardExec`/`guardSpawn` (que sempre ativam sandbox + replay).
## Início rápido da biblioteca
```ts
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
```
## Audit CLI
```bash
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 (nenhum achado no nível mínimo ou acima)
- `1` achados presentes
- `2` erros de análise / E/S
`ignoreGlobs` personalizados passados para `scanPath` ou para a ferramenta MCP `audit_source` aceitam
no máximo 128 strings. Cada string é limitada a 1024 unidades de código UTF-16 e 16
caracteres de agrupamento de abertura não escapados (`(`, `{`, `[`). Entradas acima desses
limites geram um erro controlado antes da correspondência.
## Biblioteca de anti-padrões (12 regras)
| ID | Severidade | Dispara 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` |
O scanner resolve vinculações de `child_process` *renomeadas* antes da correspondência,
de modo que as formas perigosas abaixo são detectadas 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(...)`
As variantes síncronas (`spawnSync`, `execFileSync`) compartilham suas regras assíncronas,
e `shell_true_option` também dispara em um shell de string (`{ shell: "/bin/sh" }`)
ou em um valor de shell dinâmico — não apenas no literal `{ shell: true }`. Um
`promisify` de uma função que não é de child_process, uma desestruturação de outro
módulo e `{ shell: false }` permanecem limpos (sem falsos positivos).
## Pragmas
- `// shellguard:ignore-next-line` — suprime um achado
- `// shellguard:ignore-file` — suprime o arquivo inteiro (raro; prefira por linha)
## Por que isso existe
A Ox-Security divulgou (2026-05) que mais de 200 mil servidores MCP stdio envolvem
`child_process.exec` com template literals carregando entrada do usuário diretamente dos
argumentos de ferramentas do LLM. O LiteLLM v1.83.6 foi o exemplo canônico (CVE corrigida na 1.83.7).
Este pacote é a contraparte de segurança defensiva: um guard + scanner plug-and-play
que fecha essa classe. Inspirado nos níveis de `seccomp` do AWS Linux + sandbox do Chromium.
## Veja também
- `HOOK_RECIPES.md` — receitas de hooks do Claude Code que bloqueiam automaticamente chamadas de ferramentas perigosas
- `CHANGELOG.md` — histórico de versões
- Auditoria MCP da Ox-Security: <https://venturebeat.com/security/200000-mcp-stdio-servers/>
- CVE-2026-XXXX do LiteLLM: <https://github.com/BerriAI/litellm/security/advisories>
## Licença
MIT — Copyright (c) 2026 Matthias Meyer (StudioMeyer)