
Pacchetto di difesa in profondità per server MCP stdio: wrapper guardExec/guardSpawn sostitutivi, CLI di audit AST, server MCP di riferimento. Risolve la vulnerabilità di classe stdio-RCE che ha colpito 200k server (Ox-Security) (LiteLLM CVE-2025-69256). MIT, TypeScript, Node >= 20.
Parte dello StudioMeyer MCP Stack — Realizzato a Maiorca 🌴 · ⭐ se lo usi
@modelcontextprotocol/sdk ^1.29.0npm install mcp-stdio-shellguard
Oppure esegui la CLI di audit direttamente senza installare:
npx -y -p mcp-stdio-shellguard mcp-shellguard-audit scan ./src
Tre livelli, attivabili singolarmente:
guardExec / guardSpawn integrabili che chiami dal tuo server MCP. Lista consentita predefinita-negata, profili sandbox, finestra replay.mcp-shellguard-audit scan <path> esamina l'AST, segnala 12 anti-pattern da BASSO (no timeout) a CRITICO (exec(\...${userInput}...`)`).mcp-stdio-shellguard-demo espone 8 strumenti così che MCP Inspector / Claude Desktop possano guidare direttamente il bundle.Il chiamante può stringere tramite timeoutMs / fdBudget per chiamata. Il chiamante non può allargare oltre il profilo.
| Livello | Condizione |
|---|---|
| BASSO | strumento non registrato (predefinito-negato) |
| MEDIO | registrato ma vuoto (qualsiasi argomento consentito) |
Solleva BASSO → CRITICO registrando lo strumento + impostando argsPatterns + eseguendo tramite guardExec/guardSpawn (che attivano sempre 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
Codici di uscita:
0 pulito (nessun risultato al livello o superiore)1 risultati presenti2 errori di parsing / IOLo scanner risolve i binding child_process rinominati prima di confrontarli, quindi le forme pericolose sotto vengono catturate anche quando la chiamata passa attraverso un alias piuttosto che un letterale child_process.exec:
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(...)Le varianti sincrone (spawnSync, execFileSync) condividono le loro regole asincrone, e shell_true_option si attiva anche su una shell stringa ({ shell: "/bin/sh" }) o un valore shell dinamico — non solo il letterale { shell: true }. Un promisify di una funzione non-child_process, una destrutturazione da un altro modulo, e { shell: false } rimangono puliti (nessun falso positivo).
// shellguard:ignore-next-line — sopprime un risultato// shellguard:ignore-file — sopprime l'intero file (raro; preferire per riga)Ox-Security ha divulgato (2026-05) che oltre 200k server MCP stdio avvolgono child_process.exec con template literal che trasportano input utente direttamente dagli argomenti degli strumenti LLM. LiteLLM v1.83.6 era l'esempio canonico (CVE corretto in 1.83.7). Questo bundle è la controparte di sicurezza difensiva: un guard + scanner integrabile che chiude la classe. Ispirato da AWS Linux seccomp + livelli sandbox di Chromium.
HOOK_RECIPES.md — ricette hook di Claude Code che bloccano automaticamente chiamate pericolose agli strumentiCHANGELOG.md — cronologia delle versioniMIT — Copyright (c) 2026 Matthias Meyer (StudioMeyer)
| Strumento | Tipo | Scopo |
|---|
guard_exec | distruttivo | Protegge child_process.exec. Impone vettore args[], lista consentita + sandbox + replay. Restituisce stdout, stderr, exitCode, canonicalHash, isReplay, trustTier. |
guard_spawn | distruttivo | Protegge child_process.spawn. Restituisce hash SHA-256 di stdout/stderr invece dei corpi completi. Rifiuta duramente shell:true. |
register_allowlist | mutante | Registra un nome di strumento con regex eseguibile + argomenti. Senza registrazione si applica il predefinito-negato. |
audit_source | sola lettura | Scansiona un percorso TS/JS per anti-pattern di shell injection. Restituisce AuditFinding[] + sommario. |
audit_report | sola lettura | Formatta un risultato audit come markdown / json / SARIF 2.1.0. |
replay_check | sola lettura | Calcola l'hash SHA-256 canonico per un'invocazione e segnala se è già presente nella finestra replay. |
sandbox_status | sola lettura | Riporta il profilo sandbox attivo + limiti concreti + flag cgroup-v2 attivo. |
trust_tier | sola lettura | Deriva il livello BASSO/MEDIO/ALTO/CRITICO per uno strumento registrato più suggerimenti di miglioramento. |
| Profilo | Timeout | Max stdout | Max stderr | Budget FD | cgroup-v2 |
|---|
strict | 5 s | 1 MB | 256 KB | 32 | sì (cpu/memoria) |
standard (predefinito) | 30 s | 10 MB | 1 MB | 256 | sì |
permissive | 5 min | 100 MB | 10 MB | 1024 | no |
argsPatterns| ALTO | argsPatterns impostato ma sandbox o tracciatore replay inattivo |
| CRITICO | argsPatterns + sandbox + replay tutti attivi |
| ID | Gravità | Si attiva su |
|---|
exec_template_literal_with_input | CRITICO | child_process.exec(\ls ${x}`)` |
exec_dynamic_string | CRITICO | child_process.exec(cmd) |
exec_sync_dynamic_string | CRITICO | child_process.execSync(cmd) |
eval_near_child_process | CRITICO | eval(...) |
function_constructor_near_child_process | CRITICO | new Function(...) |
spawn_dynamic_file_args | ALTO | spawn(bin, userArgs) |
exec_file_dynamic | ALTO | execFile(bin, ...) |
shell_true_option | ALTO | { shell: true } |
os_system_equivalent | ALTO | Deno.run / Bun.spawn |
spawn_literal_dynamic_args | MEDIO | spawn('git', userArgs) |
unbounded_buffer | BASSO | exec senza maxBuffer |
missing_timeout | BASSO | exec/spawn senza timeout |