
Solución directa para la vulnerabilidad de inyección de comandos sin parchear en MCP STDIO (familia CVE-2026-30623)
# mcpshield

[](LICENSE)
Un parche de integración directa para la vulnerabilidad sin parchear de inyección de comandos en MCP STDIO
(la familia CVE-2026-30623, divulgada por OX Security en abril de 2026 como "por
diseño" -- no llegará ningún parche del SDK). Importa una línea, y cada servidor MCP stdio
que lance tu aplicación Python verá su command/args/env validado antes de que
el SO llegue a crear un proceso.
Si eres nuevo aquí, lee primero [Alcance](#alcance), luego [Instalación](#instalación)
y [Primeros pasos](#primeros-pasos) te protegerán en menos de dos
minutos.
## Tabla de contenidos
- [Estado del proyecto](#estado-del-proyecto)
- [Alcance](#alcance)
- [Instalación](#instalación)
- [Primeros pasos](#primeros-pasos)
- [Opción A: el parche automático (hosts MCP de Python)](#opción-a-el-parche-automático-hosts-mcp-de-python)
- [Opción B: comprobación estática (cualquier lenguaje, cero ejecución)](#opción-b-comprobación-estática-cualquier-lenguaje-cero-ejecución)
- [Opción C: supervisor de lanzamiento (hosts que no son Python)](#opción-c-supervisor-de-lanzamiento-hosts-que-no-son-python)
- [Qué se bloquea frente a qué recibe una advertencia](#qué-se-bloquea-frente-a-qué-recibe-una-advertencia)
- [Las vías de escape](#las-vías-de-escape)
- [Referencia de CLI](#referencia-de-cli)
- [Limitaciones conocidas](#limitaciones-conocidas)
- [Estructura del proyecto](#estructura-del-proyecto)
- [Desarrollo](#desarrollo)
## Estado del proyecto
Pre-1.0, en desarrollo activo.
- El motor de validación, el parche automático y la CLI (`check`/`launch`/`rules`) están
implementados y cubiertos por una suite de pruebas automatizada que se ejecuta contra los
binarios reales instalados en la máquina de pruebas (`python`, `node`, `npx`) --
no mocks -- incluyendo un auténtico handshake MCP de extremo a extremo a través de un
fixture real de servidor lanzado, y una prueba genuina a nivel de subproceso de `launch`.
- Aún no está en PyPI -- consulta [Instalación](#instalación).
- Consulta [SECURITY.md](https://github.com/csinexus/mcpshield/blob/master/SECURITY.md) para ver exactamente qué está y qué no está cubierto.
## Alcance
**Dentro del alcance:** validar el lanzamiento de un servidor MCP stdio (command + args + env)
antes de que llegue a la capa de creación de procesos del SO, para cerrar concretamente la
vía de inyección de comandos/argumentos descrita en [SECURITY.md](https://github.com/csinexus/mcpshield/blob/master/SECURITY.md).
**Explícitamente fuera del alcance:** escanear las *herramientas declaradas* de un servidor
en busca de capacidades riesgosas (eso es un problema distinto -- consulta AgentGuard), el
sandboxing del proceso lanzado y los transportes MCP que no son stdio (SSE/HTTP).
## Instalación
```bash
git clone <this-repo>
cd mcpshield
pip install -e . # core CLI: click + rich only
pip install -e ".[mcp]" # if you also want the Python autopatch (needs the `mcp` SDK)
```
**Comprueba que funciona:**
```bash
mcpshield --version
mcpshield --help
```
## Primeros pasos
### Opción A: el parche automático (hosts MCP de Python)
Si tu aplicación está escrita en Python y construye `StdioServerParameters` /
llama a `mcp.client.stdio.stdio_client` por sí misma, añade un import al
principio de tu punto de entrada -- antes de que cualquier otra cosa importe `mcp.client.stdio`:
```python
import mcpshield.autopatch # side-effect import; must come first
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
# ... use stdio_client exactly as before -- it's now validated
```
Un lanzamiento inseguro ahora lanza `mcpshield.core.errors.UnsafeConfigurationError`
(una subclase de `ValueError`) en lugar de llegar a crear un proceso.
### Opción B: comprobación estática (cualquier lenguaje, cero ejecución)
Audita un archivo de configuración de estilo `mcpServers` sin ejecutar nada:
```bash
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
```
Sale con código distinto de cero si algo está `BLOCKED` (añade `--strict` para que también falle
con `WARN`) -- puedes integrarlo directamente en tu CI.
### Opción C: supervisor de lanzamiento (hosts que no son Python)
Para un cliente MCP (Node, Java, Rust, ...) que no pueda usar el parche
automático de Python, apunta su configuración a `mcpshield` en lugar del comando real:
```json
{
"command": "mcpshield",
"args": ["launch", "--", "npx", "-y", "some-mcp-server"]
}
```
`launch` valida y después ejecuta el comando real con el mismo stdio
que tu cliente MCP espera (paso transparente) -- o se niega con un
error claro si el lanzamiento no es seguro.
## Qué se bloquea frente a qué recibe una advertencia
| Comprobación | Binario nativo (p. ej. `python.exe`) | Interpretable por shell (script `.cmd`/`.bat`/shebang) |
|---|---|---|
| Metacaracteres de shell (`&`, `\|`, `;`, comilla invertida, `$(...)`, ...) en un argumento | Permitido | **Bloqueado** |
| Byte NUL / salto de línea en un argumento | **Bloqueado** | **Bloqueado** |
| El comando se resuelve mediante path traversal relativo (`..`) | **Bloqueado** | **Bloqueado** |
| El comando no se resuelve a un archivo real | **Bloqueado** | **Bloqueado** |
| `LD_PRELOAD` / `NODE_OPTIONS` / etc. en env | Eliminado (advertencia) | Eliminado (advertencia) |
| `PYTHONPATH` en env | Marcado (advertencia), no eliminado | Marcado (advertencia), no eliminado |
Los binarios nativos reciben comprobaciones de argumentos más laxas porque hacen `exec` directamente --
no hay shell que vuelva a interpretar la lista de argumentos. Los comandos interpretables por shell
(lo más habitual: `npx.cmd`/`npx.bat` en Windows) reciben comprobaciones estrictas
porque ese es exactamente el mecanismo que explota la CVE subyacente.
## Las vías de escape
Ambas son opciones deliberadas de aceptación por valor -- nunca una bandera genérica
de "desactivar comprobaciones":
- `allow_raw_args=["--some-value-with-a-pipe"]` (librería) exime valores de argumento
específicos que hayas revisado y en los que confíes.
- `allow_env=["SOME_VAR"]` permite que una variable de entorno normalmente eliminada
pase sin modificaciones.
## Referencia de CLI
| Comando | Qué hace |
|---|---|
| `mcpshield check <config> [--format table\|json] [--strict]` | Auditoría estática de una configuración `mcpServers`. Nunca ejecuta nada. Sale con código distinto de cero ante cualquier `BLOCKED` (o también `WARN`, con `--strict`). |
| `mcpshield launch -- <command> [args...]` | Valida y después ejecuta el comando real con pass-through de stdio. |
| `mcpshield rules list` | Muestra la lista negra activa de metacaracteres de shell, las listas de variables de entorno y los binarios lanzadores seguros conocidos. |
## Limitaciones conocidas
- Aún no está en PyPI -- la instalación requiere `git clone`.
- El parche automático solo parchea `mcp.client.stdio.stdio_client` tal como se busca
*en el momento del parcheo*. El código que ya tenga su propia referencia (mediante
`from mcp.client.stdio import stdio_client` ejecutado antes de
`import mcpshield.autopatch`) lo omitirá -- importa mcpshield.autopatch
primero, siempre.
- La comprobación de metacaracteres de shell se basa en una lista negra y solo se aplica cuando
el comando resuelto se detecta como interpretable por shell. No es un parser completo
de la gramática del shell -- consulta [SECURITY.md](https://github.com/csinexus/mcpshield/blob/master/SECURITY.md) para conocer el límite exacto
del alcance.
- `check` resuelve los comandos usando la máquina en la que se ejecuta. Una configuración que
se resolvería de forma diferente en la máquina donde realmente se despliega (un PATH
distinto, herramientas instaladas diferentes) puede dar resultados distintos allí.
## Estructura del proyecto
```
mcpshield/
autopatch.py # one-line-import fix for Python MCP hosts
core/
validate.py # the validation engine (command/args/env checks)
rules.py # blocklist/allowlist data
errors.py # UnsafeConfigurationError
cli/
main.py
commands/ (check.py, launch.py, rules.py)
tests/
fixtures/ # real benign MCP server + sample/malicious configs
```
## Desarrollo
```bash
pip install -e ".[dev,mcp]"
pytest
```
La suite de pruebas valida contra los binarios reales `python`/`node`/`npx`
instalados en la máquina donde se ejecuta (resueltos de la misma forma en que el motor
los resuelve), e incluye un auténtico handshake MCP de extremo a extremo
a través de un fixture real de servidor lanzado -- no mocks.