Inicio / Artículos

Cero hallazgos en cuanto a la cantidad de código: auditoría de 300 paquetes MCP npm

Una auditoría de archivos comprimidos de 300 paquetes de servidor MCP en npm encontró 217 con código en tiempo de ejecución que el escáner nunca leyó. Por eso un resultado limpio requiere que se adjunte un comprobante de cobertura.

Editor: REACTAPP.TOP8 min de lectura1871 palabras

Fuentes: I Audited 300 MCP-Related npm Packages. My Scanner Couldn’t Inspect Runtime Code in 217. So I created Driftward.

Trescientos paquetes npm cuyo nombre incluía MCP fueron extraídos del registro y examinados como archivos tarball publicados, sin que nunca fueran instalados ni ejecutados. El resultado más sorprendente no tuvo nada que ver con la presencia de malware. Se trató de un fallo en el análisis de cobertura: en 217 de los 300 paquetes (72.3%), el archivo tarball contenía código de ejecución, pero cada archivo correspondiente se encontraba en una ruta que el escáner omite intencionadamente, como dist/, build/ o archivos resultantes de minificación. La herramienta podía leer un archivo manifest y uno README, informar un resultado positivo y no examinar ninguno de los códigos que realmente se ejecutarían.

La distribución fue de 217 casos de deficiencias en el análisis de cobertura, 80 paquetes con código de ejecución válido y 3 en los que no se encontró ningún código de ejecución.

Eso no significa que el 72,3 % de los paquetes MCP sean invisibles para el análisis estático en general. Los paquetes pueden ser analizados, los mapas de código a veces restauran el código original, y las herramientas especializadas pueden desofuscar o comprender el JavaScript generado. La conclusión es más específica y útil:

Un resultado estático negativo no tiene sentido a menos que también indique cuánto código ejecutable se inspeccionó realmente.

El alcance es importante aquí. El resultado significa que un escáner en particular no encontró nada que pudiera ser leído en 217 archivos tar. No afirma que esos paquetes sean maliciosos o inseguros, ni que otro analizador no pudiera inspeccionarlos.

Por qué MCP eleva la apuesta

Un servidor MCP local no es simplemente una biblioteca que una aplicación importa. Mediante el transporte estándar stdio, el cliente lo inicia como un proceso hijo. La política de seguridad del proyecto establece que el servidor posee los mismos privilegios a nivel de entorno que el cliente y que el transporte stdio del SDK no ofrece un entorno aislado. Si el agente puede acceder a credenciales, direcciones de red o herramientas locales, es probable que un servidor sin aislamiento también pueda hacerlo.

La guía oficial de seguridad MCP recomienda iniciar servidores locales con privilegios mínimos y limitar el acceso al sistema de archivos y a la red. Lograr ese aislamiento corresponde al anfitrión u operador. La revisión del código sigue siendo útil, pero la pregunta pendiente es qué puede decir realmente sobre los componentes que los desarrolladores instalan en realidad.

Cómo se creó la muestra

Se trató de una muestra práctica y no de un censo completo del ecosistema.

El coleccionista realizó 11 búsquedas en el registro de npm, utilizando frases como mcp server, model context protocol, mcp connector y modelcontextprotocol. Cada consulta arrojaba hasta tres páginas con 100 resultados cada una; se eliminaron los duplicados, se conservaron los nombres que contenían “mcp” y la búsqueda se detuvo en 300 resultados. El ranking de las búsquedas determina quiénes aparecen: un mismo editor puede mostrarse varias veces, y un nombre que contenga “mcp” no garantiza necesariamente la existencia de un servidor MCP.

Para cada paquete se determinó la versión más reciente en el momento de la recolección, se descargó directamente el archivo tarball del registro, se verificó el valor de integridad publicado por npm y los componentes del paquete se cargaron en memoria. Las 300 verificaciones de integridad se superaron sin problemas, y no hubo errores al descargar ni al analizar los datos. Nada se instaló, desempaquetó en un entorno ejecutable ni se ejecutó.

El escáner está ajustado para lograr precisión. Busca APIs de red, hosts de URL literales, llamadas a subprocesos, menciones de rutas de credenciales y un conjunto limitado de frases de inyección, mientras ignora el código de prueba, los fixtures, los ejemplos, las carpetas de salida compilada, los archivos minificados, los mapas de origen y cualquier archivo superior a 1 MB. Estos omisiones reducen el ruido, pero también generan puntos ciegos cuantificables.

Resultados obtenidos

La cobertura debe presentarse junto a los hallazgos, no en una nota al pie. La primera línea de los resultados es el hallazgo principal, y todas las demás líneas deben leerse en relación con ella. Por ejemplo, un resultado de cero referencias a credenciales no indica que el corpus nunca haga referencia a ellas; solo muestra que el escáner no encontró ninguna en el código que consideró apto, aunque 217 paquetes no le proporcionaron nada que pudiera analizarse.

Destacan cuatro cifras:

  • Código en tiempo de ejecución pero cero archivos aptos: 217 paquetes.
  • De esos 217, 107 incluían un mapa de fuentes y los otros 110 no.
  • Una API de red o señal literal-host en el código apto: 50 paquetes.
  • Casos similares a inyección de comandos: 2, y la revisión manual descartó ambos como falsos positivos.

Dos alarmas falsas

Aparecieron frases de tipo inyección en dos paquetes, y una lectura manual mostró que ambos eran texto explicativo. Uno, el servidor de documentación Mastra en la versión 1.2.22, contenía ejemplos de evaluación con cadenas como IGNORE PREVIOUS INSTRUCTIONS. El otro, el servidor REMnux MCP en la versión 0.1.71, advierte que las muestras de malware pueden contener cadenas de inyección de comandos e indica a los analistas cómo tratarlas.

Tampoco hubo evidencia de un intento de manipular un agente. Esta es una debilidad conocida del escaneo por palabras clave: la documentación de seguridad, los entornos de prueba y las instrucciones maliciosas pueden contener cadenas idénticas, y solo el contexto permite determinar cuál es cuál.

Un paquete de prueba malicioso que obtuvo cero puntos

Un paquete funcionó como herramienta de control de cobertura. malicious-mcp-server 1.5.0 se describe a sí mismo como un servidor MCP deliberadamente malicioso para pruebas de extremo a extremo. Su archivo tar contenía tres archivos: package.json de 970 bytes, README.md de 742 bytes y dist/index.js de 610,358 bytes.

El escaneo en la primera pasada leyó los dos archivos de texto pequeños, ignoró dist/index.js, no encontró señales de comportamiento y asignó al paquete una puntuación de riesgo de 0. Al contar los tokens brutos en el paquete ignorado posteriormente, se detectaron seis apariciones de exec, dos de eval(), 52 de base64 y tres de raw.githubusercontent.com.

Por lo tanto, el veredicto honesto no fue uno favorable. Se determinó que no era posible establecer una cobertura durante la ejecución según las reglas en vigor. Esas cadenas de texto por sí solas no prueban ni la ejecución ni la mala intención, y el manifiesto del paquete proporciona el contexto faltante. El problema es que el conjunto que conllevaba todo el riesgo nunca llegó a la fase que arrojó un resultado de cero.

La técnica no tuvo la culpa

Omitir los resultados generados es una decisión de diseño, no una regla de la disciplina. Un analizador diferente podría parsear el JavaScript empaquetado, inspeccionar el árbol sintáctico, desempaquetar módulos, extraer mapas de fuente, rastrear metadatos de dependencias, comparar el origen o ejecutar reglas diseñadas para código minificado. Cada uno de estos métodos sacrifica algo de velocidad y precisión a cambio de mayor alcance.

El error radica en no considerar los hallazgos como una sola condición, ya que pueden referirse a al menos tres situaciones diferentes:

  1. Se leyó el código relevante y no se activó ninguna regla.
  2. Se leyó el código relevante, pero las reglas no pudieron reconocer ese comportamiento.
  3. El código relevante nunca se leyó.

Solo el primer caso representa un resultado positivo significativo, y aun así no es prueba de seguridad.

Qué debe revelar un buen informe

Un escáner no debe combinar la opción “no se encontró nada” con “no se inspeccionó nada” en un único resultado positivo. En el caso de los artefactos del paquete, cada resumen de hallazgos debe ir acompañado de un comprobante de cobertura:

  • Cuántos archivos en tiempo de ejecución existen, cuántos se analizaron y cuántos bytes se cubrieron.
  • Todo lo excluido, clasificado por causa: rutas generadas, límites de tamaño, formatos no soportados, fallos de análisis.
  • La presencia de mapas de origen y si se pudieron recuperar los archivos originales a partir de ellos.
  • Qué conjunto de reglas y versión del escáner generaron el resultado.
  • Un estado de resultado distinto para cuando no se encuentran problemas en el código inspeccionado, frente a la situación en que no está disponible la cobertura en tiempo de ejecución.
  • Se trata de un pequeño cambio en la forma de informar que genera una gran mejora en la confianza, ya que los revisores pueden determinar si un resultado silencioso constituye evidencia o simplemente refleja una falta de visibilidad.

    Por qué el comportamiento en tiempo de ejecución sigue siendo importante

    La revisión estática es una instantánea. El comportamiento puede cambiar con una actualización, y un comando no fijado como npx package-name puede referirse a código nuevo mientras la configuración local de MCP permanece igual. Por lo tanto, los controles en tiempo de ejecución complementan el trabajo estático en lugar de competir con él. Cada servidor local debe ejecutarse bajo su propia identidad con una política de menor privilegio. Sus destinos reales y capacidades deben registrarse y se debe aprobar explícitamente una línea de base. Las ejecuciones posteriores se comparan entonces con el historial propio de ese servidor, de modo que un cambio en la configuración genere una desviación en la identidad, y una actualización de paquete bajo una definición inalterada siga siendo visible si las capacidades en tiempo de ejecución cambian.

    Una herramienta de código abierto sigue este modelo al encapsular cada servidor MCP como un principal independiente, por ejemplo mcp:github, con una política separada, una línea de base firmada y un registro de desviaciones. Está diseñada como respaldo en tiempo de ejecución para la revisión del código fuente y no como sustituto. El soporte en las plataformas es desigual: en macOS, Seatbelt vincula los sockets salientes a un proxy de bucle local, mientras que en Linux, bubblewrap ofrece hasta el momento confinamiento del sistema de archivos y de los procesos además de variables de entorno del proxy, sin un vínculo de red tan estricto.

    Investigaciones más recientes siguen la misma línea. TrustShiftProbe, un preprint de agosto de 2026, modela servidores MCP que funcionan normalmente y luego cambian su comportamiento en un determinado momento, y prueba defensas en tiempo de ejecución basadas en líneas de base de comportamiento. Un preprint no representa consenso, pero su modelo de amenazas sensible al tiempo es el correcto, ya que la confianza puede erosionarse después de realizar una revisión.

    Límites de la auditoría

    • La selección se realizó a partir de la clasificación en búsquedas y el filtrado por nombre, por lo que la muestra no es ni aleatoria ni representativa.
    • Todas las cifras se refieren al 1 de septiembre de 2026, y tanto el contenido de npm como los resultados de búsqueda cambian con el tiempo.
    • Lo que se midió fueron los archivos tarball, no si cada uno de ellos es un servidor MCP funcional.
    • La expresión “scanner blind” se aplica a las reglas de exclusión de este escáner, no al análisis estático en general.
    • Sí se contaron los mapas de código fuente, pero nadie verificó si cada uno incluye código fuente utilizable.
    • Las URLs y nombres de API literales son indicios estáticos y no demuestran que un camino de código se esté ejecutando.
    • Nunca se ejecutaron los paquetes, por lo que las destinos de red en tiempo de ejecución siguen siendo desconocidos.
    • No encontrar nada no constituye un veredicto de seguridad.

    Reproducir la ejecución

    Para reconstruir el conjunto exacto de archivos tarball, utilice el archivo con resultados congelados como entrada de bloqueo:

    # self-bootstrapping — no install needed
    git clone https://github.com/abdalhafeezbushara/driftward.git
    cd driftward
    python3 research/mcp-npm-audit-2026-09-01/audit.py \
      research/mcp-npm-audit-2026-09-01/results.json \
      /tmp/mcp-audit-rerun.json
    

    Para obtener una captura de estado actualizada con 300 paquetes de los resultados actuales de búsqueda en npm:

    N=300 ./detonate/fetch-corpus.sh /tmp/mcp-packages.txt
    python3 research/mcp-npm-audit-2026-09-01/audit.py \
      /tmp/mcp-packages.txt /tmp/mcp-audit-current.json
    

    Ninguna de las rutas instala un paquete ni ejecuta su código; ambas solo descargan y leen archivos tarball.

    Una breve lista de verificación para usuarios de MCP

    • Fije las versiones. Una versión exacta junto con un valor de integridad es mejor que una llamada a npx sin especificar versión.
    • Examine el artefacto publicado. Un repositorio limpio tiene poco valor si el archivo tarball instalado contiene un paquete generado que nadie ha abierto.
    • Pida cifras de cobertura. Si un informe muestra que no hay archivos en tiempo de ejecución válidos, considérelo como no disponible y no como limpio.
    • Aíslate cada servidor de forma individual, con su propia identidad, ámbito del sistema de archivos y hosts permitidos, ya sea que funcione localmente o a través de un puente.
    • Haga del proceso de confianza un paso deliberado: registre una ejecución conocida como correcta, firme esa referencia y bloquee los procesos de CI o exija una revisión cuando cambien las capacidades.

    Dejar de lado el análisis estático no es la lección importante; lo relevante es indicar su nivel de cobertura. En el caso de los servidores MCP y herramientas similares utilizadas por los agentes, leer el código fuente, analizar los artefactos generados, verificar el origen del mismo, utilizar entornos aislados, registrar el comportamiento en tiempo de ejecución y detectar desviaciones abordan cada uno un aspecto distinto de un mismo problema. La línea más preocupante de un informe de seguridad podría no ser la detección de malware, sino el hecho de no haber encontrado nada, lo que plantea la siguiente pregunta: ¿cero hallazgos en qué cantidad de código?