
Endurecimiento de la cadena de suministro de Capa 2 para servidores MCP — manifiestos de herramientas firmados con Ed25519, atestación de spawn en tiempo de ejecución, saneador de argumentos con denegación por defecto. Protege contra el envenenamiento del marketplace + CVE-2025-69256 + CVE-2025-61591.
<!-- studiomeyer-mcp-stack-banner:start --> > **Parte del [StudioMeyer MCP Stack](https://studiomeyer.io)** — Hecho en Mallorca 🌴 · ⭐ si lo usas <!-- studiomeyer-mcp-stack-banner:end --> # mcp-server-attestation <!-- badges --> [](https://www.npmjs.com/package/mcp-server-attestation) [](https://www.npmjs.com/package/mcp-server-attestation)    <!-- /badges -->Endurecimiento de la cadena de suministro de capa 2 para servidores del Model Context Protocol. Manifiestos de herramientas firmados con Ed25519, atestación de spawn en tiempo de ejecución, saneador de argumentos de denegación por defecto. Respuesta directa a: - **Envenenamiento del marketplace de OX Security, abril de 2026** — 9 de 11 registros MCP aceptaron servidores maliciosos. La posición publicada de Anthropic: "comportamiento esperado". - **CVE-2025-69256** — RCE en Serverless Framework MCP mediante inyección de comandos en `child_process.exec()`. - **CVE-2025-61591** — RCE en Cursor MCP a través de un servidor malicioso instalado por OAuth con secuestro de spawn. Este paquete proporciona lo que Anthropic decidió no ofrecer: verificación criptográfica de qué herramientas puede exponer un servidor y qué llamadas de spawn puede realizar. Es una dependencia de sustitución directa, no un reemplazo del runtime. ## Una nota de nuestra parte Llevamos dos años construyendo herramientas y sistemas para nosotros mismos. Que este repositorio sea pequeño y tenga pocas estrellas no se debe a que sea nuevo. Se debe a que hace poco que decidimos compartir lo que hemos construido. No es un experimento reciente, es una historia larga con un commit reciente. Nos encanta construir cosas y compartirlas. No nos gustan las tácticas de redes sociales, los trucos de crecimiento ni perseguir estrellas y seguidores. Por eso este repositorio es pequeño. El código es real, se usa y las incidencias se responden. Juzga por ti mismo. Si te resulta útil, compartirlo, probarlo y dar tu opinión nos ayuda. Si podría ser mejor, una incidencia es más útil. Si construyes algo con él, cuéntanoslo en [email protected]. Eso de verdad nos alegra el día. Desde un pequeño estudio en Palma de Mallorca. ## Paquetes (npm workspaces) | Paquete | Propósito | | --- | --- | | `mcp-server-attestation` (`packages/lib`) | Librería: firma/verificación Ed25519, esquema de manifiesto, saneador, atestador de spawn, almacén de confianza TOFU. | | `mcp-attest-cli` (`packages/cli`) | CLI `mcp-attest`: `keygen`, `sign`, `verify`, `inspect`, `fingerprint`, `check-pin`. | | `mcp-attest-demo` (`packages/demo-server`) | Servidor MCP de referencia (stdio, spec 2025-06-18) que expone 5 herramientas que demuestran la librería. | ## Instalación ```bash npm install mcp-server-attestation # CLI: npm install -g mcp-attest-cli # Reference MCP server: npx mcp-attest-demo ``` Node 20+. Sin dependencias criptográficas externas: usa primitivas Ed25519 de `node:crypto`. ## Inicio rápido del servidor en cinco líneas ```ts import { verifyManifestStrict, attestSpawnStrict, type SignedManifest } from "mcp-server-attestation"; import signed from "./signed/manifest.json" assert { type: "json" }; // 1. At startup: prove the manifest you ship is the manifest you signed. verifyManifestStrict(signed); // 2. Before every child_process.spawn: attestSpawnStrict(signed as SignedManifest, { command, args }); ``` Esa es toda la integración. Dos llamadas a funciones, sin SaaS, sin daemon. Si prefieres volver a verificar la firma en cada spawn (defensa en profundidad contra que un manifiesto no verificado o sustituido llegue a la compuerta), usa en su lugar la única llamada a prueba de fallos: verifica y *luego* atesta: ```ts import { attestSpawnVerified, type SignedManifest } from "mcp-server-attestation"; attestSpawnVerified(signed as SignedManifest, { command, args }); ``` ## Herramientas (servidor de referencia `mcp-attest-demo`) | # | Nombre | readOnlyHint | destructiveHint | | - | ---- | ------------ | --------------- | | 1 | `attest_verify_manifest` | true | false | | 2 | `attest_inspect_spawn` | true | false | | 3 | `attest_generate_manifest_template` | true | false | | 4 | `attest_sign_manifest` | false | false | | 5 | `attest_keygen` | false | false | Las anotaciones son honestas: `attest_sign_manifest` y `attest_keygen` escriben archivos en el disco, por lo que no son de solo lectura, pero no destruyen datos existentes, por lo que destructiveHint se mantiene en false. Consulta `docs/THREAT-MODEL.md` para ver la tabla de capacidades por herramienta. ## CLI ```bash mcp-attest keygen --out-dir ./keys --name prod mcp-attest sign --manifest manifest.json --private-key keys/prod.key --out signed.json mcp-attest verify --signed signed.json --pin # TOFU pin mcp-attest verify --signed signed.json --sigstore # opt-in Rekor cross-ref mcp-attest inspect --signed signed.json --command /usr/bin/echo --arg "hello" mcp-attest fingerprint --public-key keys/prod.pub mcp-attest check-pin --server my-server --signed signed.json ``` `verify` sale con código 2 ante una firma incorrecta y con código 3 ante una discrepancia de pin. ## Compatibilidad con la especificación MCP | Versión de la especificación | Estado | | --- | --- | | 2024-11-05 | analizable en el manifiesto, no es objetivo del servidor de referencia | | 2025-03-26 | analizable en el manifiesto, no es objetivo del servidor de referencia | | 2025-06-18 | objetivo completo | La librería es agnóstica al transporte. El servidor de referencia es solo stdio. ## Modelo de seguridad - **Trust-on-First-Use** es el comportamiento por defecto. La primera vez que verificas un servidor, su clave pública se fija en `~/.mcp-attest/trust.json` (anulación: `MCP_ATTEST_TRUST_FILE`). Las verificaciones posteriores rechazan cualquier clave nueva para el mismo nombre de servidor con `TRUST_PIN_MISMATCH`. Esto bloquea el vector de actualización maliciosa al estilo Cursor. - **Sin lista de claves de confianza incluida.** Este paquete no actúa como guardián. Si quieres una garantía más sólida, activa `--sigstore` para contrastar la huella de la clave pública con el registro de transparencia Rekor de Sigstore. - **Saneador de argumentos de denegación por defecto.** `shellSafeString` bloquea todos los metacaracteres ASCII de shell, NUL, CR, LF, VT, FF, NEL, caracteres de ancho cero, BOM, anulaciones RTL/LTR, aisladores Trojan-Source y confundibles latinos de ancho completo. El comportamiento de lista de permitidos requiere los tipos de regla explícitos `regex` / `enum` / `prefix` / `literal`. - **Reglas `regex` seguras frente a ReDoS.** El patrón de una regla `regex` está firmado por el autor, pero el valor del argumento lo controla el atacante. El saneador detecta estáticamente los patrones propensos a retroceso (cuantificadores anidados sin límite como `(a+)+`) y se niega a ejecutarlos, por lo que un único argumento manipulado no puede congelar la ruta crítica del spawn. Las reglas `regex` también llevan un límite de entrada `maxLength` (4096 por defecto). - **Reglas `prefix` seguras frente a traversal.** Las reglas `prefix` rechazan los componentes de ruta `..` por defecto (`denyTraversal: true`, incluida la forma codificada `%2e%2e`), por lo que `/safe/../../etc/passwd` se bloquea aunque cumpla `prefix: "/safe/"`. - **Verificar y luego atestar.** `attestSpawnVerified` comprueba la firma del manifiesto antes de atestar el spawn en una única llamada a prueba de fallos: úsala en lugar de `attestSpawnStrict` a menos que hayas sacado la verificación Ed25519 por spawn de un bucle realmente crítico. - **JSON canónico** es la superficie firmada. La reserialización no puede cambiar los bytes firmados. Lo que este paquete NO hace (fuera del alcance): - Hacer sandbox o contenerizar el proceso del servidor. - Endurecimiento del flujo OAuth (build separado de `mcp-oauth-shield`). - Control de tráfico de salida de red. - Parcheo automático de servidores existentes. ## Pruebas ```bash npm install npm run typecheck npm test ``` El corpus de pruebas incluye fixtures de reproducción de CVE (`packages/lib/tests/fixtures/cve-2025-69256-payloads.json`, `cve-2025-61591-payloads.json`). La compilación es una comprobación de regresión: todos los payloads deben bloquearse. ## Distribución - Publicación en npm mediante GitHub Actions OIDC con `--provenance`. - Servidor de referencia enviado a mcp.so y FastMCP Directory. - Librería publicada por separado como `mcp-server-attestation`. ## Acerca de StudioMeyer [StudioMeyer](https://studiomeyer.io) es un estudio de diseño e IA con sede en Palma de Mallorca que trabaja con clientes de todo el mundo. Creamos sitios web personalizados e infraestructura de IA para pequeñas y medianas empresas. Stack de producción sobre Claude Agent SDK, MCP y n8n, con Sentry, Langfuse y LangGraph para la observabilidad y una capa de protección propia. ## Licencia MIT, Copyright 2026 Matthias Meyer (StudioMeyer). Consulta `LICENSE`.