Inyección de argumentos en dbt-mcp: las entradas del agente se vuelven opciones de dbt
Un fallo del servidor MCP de dbt corregido el 2026-07-16 permitía a un cliente MCP colar opciones globales de dbt como --profiles-dir a través de parámetros de herramienta, llegando a la CLI pese a shell=False.
¿De qué se trata?
El 2026-07-16, dbt Labs publicó un aviso de seguridad sobre un fallo de inyección de argumentos en dbt-mcp, el servidor Model Context Protocol que permite a un agente LLM manejar la herramienta de línea de comandos dbt. En versiones anteriores a la 1.17.1, dos parámetros de herramienta controlados por el cliente MCP —la selección de nodos y el tipo de recurso— se pasaban al subproceso de dbt sin separarse del analizador de opciones de dbt. Un cliente MCP podía, por tanto, hacer que esos parámetros parecieran opciones globales de dbt y cambiar la forma en que dbt se ejecutaba.
El fallo merece atención no por ser exótico, sino por ser corriente. Es un error clásico de inyección de argumentos en línea de comandos (CWE-88) que reaparece en el mundo de los agentes, donde el «usuario» que aporta esos parámetros suele ser un modelo de lenguaje al que un tercero puede influir mediante inyección de prompt indirecta.
Cómo funciona
El servidor construía la invocación de dbt añadiendo las cadenas suministradas por quien llama a la lista de argumentos de subprocess.Popen(...) con shell=False. Fijar shell=False es la base correcta: significa que el sistema operativo no lanza ningún shell, por lo que los metacaracteres de shell (;, |, comillas invertidas, $( )) nunca se interpretan y no pueden encadenar comandos. Esa defensa se mantiene aquí.
Lo que shell=False no hace es evitar que un valor sea leído como otra opción por el programa que se lanza. dbt acepta opciones globales como --profiles-dir, --project-dir y --target. Si un valor controlado por el agente se coloca en la lista de argumentos como su propio token, el analizador de dbt ve una nueva opción en lugar de un dato. En dbt-mcp, los parámetros de selección de nodos y de tipo de recurso fluían a esa lista, de modo que un cliente MCP podía dirigir dbt a un directorio de configuración de su elección en lugar del del operador.
El modelo mental:
# shell=False bloquea UNA cosa, no la otra
Shell metacharacter injection -> BLOCKED by shell=False
(";", "|", "$(...)" never reach a shell)
Argument / flag injection -> NOT blocked by shell=False
(a controlled token is parsed by dbt as --profiles-dir / --target)
El impacto afecta a la confidencialidad y la integridad, no a la ejecución remota de código: redirigir --profiles-dir apunta dbt a otro profiles.yml, que define las conexiones y los objetivos de base de datos. Por eso el aviso se clasifica como de severidad media, con vector local y alta complejidad: el atacante necesita controlar los parámetros de herramienta y disponer de una configuración alternativa alcanzable, pero la ganancia es ejecutar dbt contra conexiones que el operador nunca previó. Aquí no se reproduce ningún payload funcional; el tema es el comportamiento de análisis, no una receta.
Por qué importa
En un script no agéntico, «quien llama controla los argumentos» suele ser aceptable, porque quien llama es código de confianza. Un servidor MCP invierte esa premisa. Quien llama es un LLM, y en despliegues reales los argumentos de herramienta del LLM pueden ser moldeados por el contenido que ha leído —un ticket, un README, la descripción de una tabla, una página web—, cualquiera de los cuales puede portar una inyección de prompt indirecta. Un parámetro que un desarrollador imagina como «un nombre de modelo» se convierte en un canal no confiable que llega a un subproceso.
Esto sitúa el error en el centro de la clase de envoltorios de herramientas de agente que reaparece una y otra vez en el ecosistema: las entradas estructuradas del modelo se tratan como datos seguros del lado del servidor, cuando deberían tratarse como datos adversarios. Se corresponde con la agencia excesiva y el diseño inseguro de herramientas del OWASP LLM Top 10. La lección se generaliza a cualquier servidor MCP que invoque un subproceso —dbt, git, gestores de paquetes, CLI de nube— donde las herramientas con opciones están a un token mal colocado de ser reutilizadas.
Defensas
El arreglo llegó en dbt-mcp 1.17.1; actualizar es el primer paso. Más allá del parche, las mitigaciones duraderas para toda esta clase son:
- Terminar el análisis de opciones. Inserte un separador
--entre las opciones y los datos posicionales para que el programa hijo deje de interpretar los tokens siguientes como opciones, o pase los valores por interfaces que nunca se traten como opciones. Es el arreglo estructural más fiable contra la inyección de argumentos. - Lista blanca, no saneamiento. Valide los parámetros controlados por el agente contra un conjunto explícito de valores permitidos (nombres de modelo conocidos, tipos de recurso conocidos) y rechace todo lo demás, en lugar de intentar eliminar caracteres peligrosos.
- Tratar los argumentos de herramienta MCP como entrada no confiable. Asuma que cualquier cadena que un LLM pase pudo haber sido dirigida por el contenido que ingirió. Aplique validación de entrada en la frontera de la herramienta como si viniera de un usuario anónimo de internet.
- Fijar la configuración que el subproceso puede ver. Defina
--profiles-dir,--project-diry--targetdesde la configuración del servidor o el entorno, y no permita que los parámetros de la petición los sobrescriban. Ejecute el subproceso con el mínimo privilegio y un directorio de trabajo restringido. - Registrar y revisar el argv final. Emita el vector de argumentos exacto que produce cada ejecución de herramienta, para que las opciones anómalas sean visibles en la supervisión y la respuesta a incidentes.
Estado
| Elemento | Referencia | Fecha | Notas |
|---|---|---|---|
| Inyección de argumentos dbt-mcp | CVE-2026-44968 / GHSA-xpww-f6pm-cfhq | 2026-07-16 | CVSS 6.3 (medio), CWE-88; afecta a dbt-mcp < 1.17.1 |
| Versión corregida | dbt-mcp v1.17.1 | 2026-07-16 | node_selection / resource_type ya no llegan a dbt como opciones |
| Clase | OWASP LLM Top 10 (2025) | 2025 | Agencia excesiva / diseño inseguro de herramientas |
La vulnerabilidad está divulgada públicamente y parcheada; los detalles anteriores proceden del registro CVE y del aviso del proveedor. La conclusión no es específica de dbt: toda herramienta de agente que añada valores suministrados por el modelo a un subproceso debe asumir que shell=False detiene el encadenamiento de comandos pero no la inyección de opciones, y diseñar la frontera en consecuencia.