GHSA-xpww-f6pm-cfhq dbt MCP Server has an Argument Injection in dbt CLI Tool Wrappers via node_selection and resource_type Parameters
*Discovered through manual source code review. Verified by PoC execution against a local dbt-mcp v1.15.1 installation.**Summary`_run_dbt_command()` in `src/dbt_mcp/dbt_cli/tools.py` constructs the dbt subprocess argument list by appending user-supplied MCP tool parameters without sanitization. Two independent injection vectors exist. An MCP client can inject arbitrary dbt global flags — such as `--profiles-dir`, `--project-dir`, and `--target` — by crafting the `node_selection` string (Vector 1) or the `resource_type` JSON array (Vector 2). Because `subprocess.Popen` is called with `shell=False` and a list argument, shell metacharacter injection is not possible; however, this provides no defense against argument list injection (CWE-88), where attacker-controlled tokens are interpreted by the target process as flags rather than values.Details**Vector 1 — `node_selection` string**
Affected tools: `build`, `compile`, `run`, `test`, `clone`, `list`, `get_node_details_dev``str.split(" ")` does not distinguish dbt selector tokens from flag tokens. Input `"my_model --profiles-dir /tmp/evil"` produces:dbt parses the injected `--profiles-dir` as a global option and loads configuration from the attacker-supplied path.**Vector 2 — `resource_type` list**
Affected tool: `list`Each JSON array element is appended verbatim to argv. Input `["model", "--profiles-dir", "/tmp/evil"]` produces:Both vectors share the same root cause: no validation prevents tokens starting with `-` from being appended as independent argv elements.PoC**1. Environment setup (run once)****2. MCP client exploit — triggers injection through the real protocol stack****Expected server log (INFO level, `src/dbt_mcp/mcp/server.py` line 67):**The injected flags reach `_run_dbt_command()` unchanged and are passed verbatim to `subprocess.Popen`.ScreenshotImpactThe following is directly demonstrated by the PoC above:An MCP client can inject arbitrary dbt global flags into `subprocess.Popen`'s argv list via either `node_selection` or `resource_type`.`--profiles-dir` is accepted by dbt as a global option, overriding the server's configured profile directory.When an attacker-controlled `profiles.yml` exists at the injected path, dbt executes with the attacker's database configuration — demonstrated by the DuckDB file write to `/tmp/PWNED_by_injection.duckdb`.**Preconditions and scope:** The attacker must be able to supply crafted MCP tool arguments (normal MCP client access) and must have a `profiles.yml` accessible at the injected path on the host running dbt-mcp. In the common local-development deployment model, a prompt-injected LLM agent sharing the filesystem can write this file before invoking the dbt tool. Additional injectable flags beyond `--profiles-dir` include `--project-dir` and `--target`, which redirect dbt's project root and execution environment respectively.Remediation**Vector 1 — validate each `node_selection` token before extending argv:****Vector 2 — validate `resource_type` against an explicit allowlist:****Hardening:** Add `pattern` regex constraints to the Pydantic `Field` definitions for `node_selection` so that malformed inputs are rejected at the MCP schema layer before reaching `_run_dbt_command()`. Add regression tests in `tests/unit/` with payloads containing `--profiles-dir`, `--project-dir`, and `--target` to prevent re-introduction.
CVSS v3.1 score: 6.3