
Fix drop-in per la falla di iniezione di comandi non corretta in MCP STDIO (famiglia CVE-2026-30623)
Una correzione drop-in per la falla di command-injection negli MCP stdio non ancora corretta (famiglia CVE-2026-30623, segnalata da OX Security nell'aprile 2026 come "by design" -- nessuna patch SDK in arrivo). Basta importare una riga e ogni server MCP stdio avviato dalla tua app Python viene validato in command/args/env prima ancora che il sistema operativo crei un processo.
Se sei nuovo qui, leggi prima Scope, poi Installazione e Per iniziare ti proteggeranno in meno di due minuti.
Pre-1.0, in sviluppo attivo.
check/launch/rules) sono
implementati e coperti da una suite di test automatici che viene eseguita
contro i binari reali installati sulla macchina di test (python, node,
npx) -- non mock -- inclusa una vera handshake MCP end-to-end attraverso
una fixture di server reale avviato, e un vero test a livello di
sottoprocesso per launch.In scope: validazione di un avvio di server MCP stdio (command + args + env) prima che raggiunga il livello di creazione dei processi del sistema operativo, in particolare per chiudere il percorso di injection di comandi/argomenti descritto in SECURITY.md.
Esplicitamente fuori scope: scansione dei tool dichiarati di un server per capacità rischiose (è un problema diverso -- vedi AgentGuard), sandboxing del processo avviato e trasporti MCP non stdio (SSE/HTTP).
git clone <questo-repo>
cd mcpshield
pip install -e . # CLI principale: solo click + rich
pip install -e ".[mcp]" # se vuoi anche l'auto-patch Python (richiede l'SDK `mcp`)
Verifica che funzioni:
mcpshield --version
mcpshield --help
Se la tua app è scritta in Python e costruisce StdioServerParameters /
chiama direttamente mcp.client.stdio.stdio_client, aggiungi un import
all'inizio assoluto del tuo entrypoint -- prima che qualsiasi altro import
carichi mcp.client.stdio:
import mcpshield.autopatch # import con side-effect; deve essere il primo
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
# ... usa stdio_client esattamente come prima -- ora è validato
Un avvio non sicuro ora solleva mcpshield.core.errors.UnsafeConfigurationError
(un sottotipo di ValueError) invece di creare mai un processo.
Controlla un file di configurazione in stile mcpServers senza eseguire nulla:
mcpshield check claude_desktop_config.json
+---------------------------------------------------------------+
| Server | Status | Command | Detail |
|------------------+---------+---------+------------------------|
| filesystem | OK | npx | - |
| evil-server | BLOCKED | npx | Argument '...' contains|
| | | | shell metacharacter |
+---------------------------------------------------------------+
1 ok, 0 warned, 1 blocked
Esce con codice non-zero se qualcosa è BLOCKED (aggiungi --strict per
fallire anche con WARN) -- inseriscilo direttamente in CI.
Per un client MCP (Node, Java, Rust, ...) che non può usare l'auto-patch
Python, punta la sua configurazione a mcpshield invece che al comando reale:
{
"command": "mcpshield",
"args": ["launch", "--", "npx", "-y", "some-mcp-server"]
}
launch valida e poi esegue il comando reale con lo stesso stdin/stdout che
il tuo client MCP si aspetta (passthrough trasparente) -- oppure rifiuta con
un errore chiaro se l'avvio non è sicuro.
I binari nativi ricevono controlli sugli argomenti più permissivi perché
eseguono direttamente con exec -- non c'è una shell che ri-parse la lista
degli argomenti. I comandi interpretabili dalla shell (più comunemente
npx.cmd/npx.bat su Windows) ricevono controlli rigorosi perché è
esattamente il meccanismo sfruttato dalla CVE sottostante.
Entrambe sono opt-in deliberati, per singolo valore -- mai un flag generico "disabilita i controlli":
allow_raw_args=["--some-value-with-a-pipe"] (libreria) esenta specifici
valori di argomenti che hai rivisto e di cui ti fidi.allow_env=["SOME_VAR"] consente a una variabile d'ambiente normalmente
rimossa di passare senza modifiche.git clone.mcp.client.stdio.stdio_client come risolto
al momento della patch. Il codice che ha già un proprio riferimento (tramite
from mcp.client.stdio import stdio_client eseguito prima di
import mcpshield.autopatch) lo bypasserà -- importa sempre prima
mcpshield.autopatch.check risolve i comandi usando la macchina su cui viene eseguito. Una
configurazione che verrebbe risolta diversamente sulla macchina in cui è
effettivamente distribuita (un PATH diverso, strumenti installati diversi)
potrebbe riportare risultati diversi.mcpshield/
autopatch.py # fix con import a una riga per host MCP Python
core/
validate.py # il motore di validazione (check su command/args/env)
rules.py # dati per blocklist/allowlist
errors.py # UnsafeConfigurationError
cli/
main.py
commands/ (check.py, launch.py, rules.py)
tests/
fixtures/ # server MCP benigno reale + configurazioni di esempio/maligne
pip install -e ".[dev,mcp]"
pytest
La suite di test valida contro i binari reali python/node/npx installati
sulla macchina su cui viene eseguita (risolti nello stesso modo in cui li
risolve il motore stesso) e include una vera handshake MCP end-to-end
attraverso una fixture di server reale avviato -- non mock.
| Check | Binario nativo (es. python.exe) | Interpretabile dalla shell (.cmd/.bat/script con shebang) |
|---|
Metacaratteri di shell (&, |, ;, backtick, $(...), ...) in un argomento | Consentiti | Bloccati |
| Byte NUL / newline in un argomento | Bloccato | Bloccato |
Il comando risolve tramite path traversal relativo (..) | Bloccato | Bloccato |
| Il comando non risolve a un file reale | Bloccato | Bloccato |
LD_PRELOAD / NODE_OPTIONS / ecc. nelle env | Rimosso (avviso) | Rimosso (avviso) |
PYTHONPATH nelle env | Segnalato (avviso), non rimosso | Segnalato (avviso), non rimosso |
| Comando | Cosa fa |
|---|
mcpshield check <config> [--format table|json] [--strict] | Audit statico di una configurazione mcpServers. Non esegue mai nulla. Uscita non-zero se qualcosa è BLOCKED (o anche WARN, con --strict). |
mcpshield launch -- <command> [args...] | Valida, poi esegue il comando reale con stdio in passthrough. |
mcpshield rules list | Mostra la blocklist attiva dei metacaratteri di shell, le liste di variabili d'ambiente e i binari noti sicuri come launcher. |