Every CVE Search/ Get API key

CVE-2026-44969

dbt-mcp: Tool Arguments Including SQL Queries and Credentials Logged in Plaintext Without Redaction When File Logging Is Enabled

2.5 LowFix available

dbt-mcp is a Model Context Protocol server for interacting with dbt. Prior to 1.17.1, DbtMCP.call_tool() in src/dbt_mcp/mcp/server.py logged the raw arguments dictionary at INFO level before each tool call and at ERROR level on exceptions, and configure_file_logging() wrote those records to dbt-mcp.log when DBT_MCP_SERVER_FILE_LOGGING=true, preserving sensitive sql_query, vars, and node_selection values in plaintext without automatic rotation or deletion. 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
Low
Integrity
None
Availability
None

Exploit likelihood

0.17%Up 0.2 points since Jun 12, 2026
90%50%10%1%

What each source reports

CVE List Score used 2.5

dbt-mcp: Tool Arguments Including SQL Queries and Credentials Logged in Plaintext Without Redaction When File Logging Is Enabled

dbt-mcp is a Model Context Protocol server for interacting with dbt. Prior to 1.17.1, DbtMCP.call_tool() in src/dbt_mcp/mcp/server.py logged the raw arguments dictionary at INFO level before each tool call and at ERROR level on exceptions, and configure_file_logging() wrote those records to dbt-mcp.log when DBT_MCP_SERVER_FILE_LOGGING=true, preserving sensitive sql_query, vars, and node_selection values in plaintext without automatic rotation or deletion. This issue is fixed in version 1.17.1.

Record
CVE-2026-44969
CVSS 3.1
2.5 LowCVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-532
Updated
2026-11-02
EUVDEUVD-2026-44993 2.5

dbt-mcp is a Model Context Protocol server for interacting with dbt. Prior to 1.17.1, DbtMCP.call_tool() in src/dbt_mcp/mcp/server.py logged the raw arguments dictionary at INFO level before each tool call and at ERROR level on exceptions, and configure_file_logging() wrote those records to dbt-mcp.log when DBT_MCP_SERVER_FILE_LOGGING=true, preserving sensitive sql_query, vars, and node_selection values in plaintext without automatic rotation or deletion. This issue is fixed in version 1.17.1.

Record
EUVD-2026-44993
CVSS 3.1
2.5 LowCVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-7xgw-6qf3-7w59 2.5

dbt MCP Server Logs Tool Arguments Including SQL Queries and Credentials in Plaintext Without Redaction When File Logging Is Enabled

Discovered through manual source code review. Verified by PoC execution against a local dbt-mcp v1.15.1 installation.

Summary

DbtMCP.call_tool() in src/dbt_mcp/mcp/server.py logs the complete raw arguments dictionary at INFO level on every tool invocation (line 67) and again at ERROR level if the call raises an exception (lines 77–79). No field is redacted before logging. When the documented DBT_MCP_SERVER_FILE_LOGGING=true feature is enabled, these log records are written to dbt-mcp.log in the project root directory as plaintext. Sensitive data — raw SQL queries, --vars payloads carrying credentials, node selectors — persists on disk indefinitely with no automatic rotation or deletion.

Details

Vulnerable log statements (server.py):

# Line 67 — emitted before every tool execution
logger.info(f"Calling tool: {name} with arguments: {arguments}")

# Lines 77–79 — emitted if the tool raises an exception (double-logging on failure)
logger.error(
    f"Error calling tool: {name} with arguments: {arguments} "
    f"in {end_time - start_time}ms: {e}"
)

arguments is the raw Python dict received from the MCP client. It is string-interpolated directly into the log message. On a tool call that raises an exception, the same dict is logged twice — once at INFO and once at ERROR.

File logging is activated by DBT_MCP_SERVER_FILE_LOGGING=true (a documented feature in the project README). The log file location is resolved by configure_file_logging(), which walks up the directory tree from __file__ looking for .git or pyproject.toml, falling back to $HOME. Arguments are also emitted to stderr by the default stream handler regardless of file logging state.

PoC

MCP client script — triggers real tool calls and verifies log file contents:

#!/usr/bin/env python3
# poc4_tool_args_logged.py
# Vulnerable code: src/dbt_mcp/mcp/server.py line 67, 77-79
# configure_file_logging(): src/dbt_mcp/telemetry/logging.py

import logging
from pathlib import Path

LOG_FILENAME = "dbt-mcp.log"

def configure_file_logging(log_level: int = logging.INFO) -> Path:
    """Reproduction of configure_file_logging() from telemetry/logging.py."""
    module_path = Path(__file__).resolve().parent
    home = Path.home().resolve()
    for candidate in [module_path, *module_path.parents]:
        if (candidate / ".git").exists() or (candidate / "pyproject.toml").exists() or candidate == home:
            repo_root = candidate
            break
    log_path = repo_root / LOG_FILENAME
    root_logger = logging.getLogger()
    root_logger.setLevel(log_level)
    file_handler = logging.FileHandler(log_path, encoding="utf-8")
    file_handler.setLevel(log_level)
    file_handler.setFormatter(
        logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s")
    )
    root_logger.addHandler(file_handler)
    return log_path

log_path = configure_file_logging()
server_logger = logging.getLogger("dbt_mcp.mcp.server")

# Exact log statements from server.py line 67 and line 77-79
name = "show"
arguments = {"sql_query": "SELECT ssn, credit_card_number, salary FROM customers WHERE id = 42", "limit": 5}
server_logger.info(f"Calling tool: {name} with arguments: {arguments}")

name2 = "run"
arguments2 = {"node_selection": "sensitive_model", "vars": '{"db_password": "hunter2", "api_key": "sk-prod-abc123xyz"}', "is_full_refresh": False}
server_logger.info(f"Calling tool: {name2} with arguments: {arguments2}")

# Verify file contents
lines = log_path.read_text(encoding="utf-8").splitlines()
poc_lines = [l for l in lines if "dbt_mcp.mcp.server" in l]
print(f"[log file: {log_path}]")
for line in poc_lines:
    print(f"  {line}")

keywords = ["ssn", "credit_card_number", "salary", "db_password", "api_key"]
found = [kw for kw in keywords if any(kw in l for l in poc_lines)]
if found:
    print(f"\n[CONFIRMED] Sensitive keywords in plaintext log: {found}")
    print(f"[CONFIRMED] No…
Record
GHSA-7xgw-6qf3-7w59
CVSS 3.1
2.5 LowCVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-532
Updated
2026-11-02
NVD 2.5

dbt-mcp is a Model Context Protocol server for interacting with dbt. Prior to 1.17.1, DbtMCP.call_tool() in src/dbt_mcp/mcp/server.py logged the raw arguments dictionary at INFO level before each tool call and at ERROR level on exceptions, and configure_file_logging() wrote those records to dbt-mcp.log when DBT_MCP_SERVER_FILE_LOGGING=true, preserving sensitive sql_query, vars, and node_selection values in plaintext without automatic rotation or deletion. This issue is fixed in version 1.17.1.

Record
CVE-2026-44969
CVSS 3.1
2.5 LowCVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
CVSS 3.1
3.3 LowCVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-532
Updated
2026-11-02
OSV 2.5

dbt-mcp: Tool Arguments Including SQL Queries and Credentials Logged in Plaintext Without Redaction When File Logging Is Enabled

dbt-mcp is a Model Context Protocol server for interacting with dbt. Prior to 1.17.1, DbtMCP.call_tool() in src/dbt_mcp/mcp/server.py logged the raw arguments dictionary at INFO level before each tool call and at ERROR level on exceptions, and configure_file_logging() wrote those records to dbt-mcp.log when DBT_MCP_SERVER_FILE_LOGGING=true, preserving sensitive sql_query, vars, and node_selection values in plaintext without automatic rotation or deletion. This issue is fixed in version 1.17.1.

Record
CVE-2026-44969
CVSS 3.1
2.5 LowCVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-532
Updated
2026-11-02
OSVGHSA-7xgw-6qf3-7w59 2.5

dbt MCP Server Logs Tool Arguments Including SQL Queries and Credentials in Plaintext Without Redaction When File Logging Is Enabled

Discovered through manual source code review. Verified by PoC execution against a local dbt-mcp v1.15.1 installation.

Summary

DbtMCP.call_tool() in src/dbt_mcp/mcp/server.py logs the complete raw arguments dictionary at INFO level on every tool invocation (line 67) and again at ERROR level if the call raises an exception (lines 77–79). No field is redacted before logging. When the documented DBT_MCP_SERVER_FILE_LOGGING=true feature is enabled, these log records are written to dbt-mcp.log in the project root directory as plaintext. Sensitive data — raw SQL queries, --vars payloads carrying credentials, node selectors — persists on disk indefinitely with no automatic rotation or deletion.

Details

Vulnerable log statements (server.py):

# Line 67 — emitted before every tool execution
logger.info(f"Calling tool: {name} with arguments: {arguments}")

# Lines 77–79 — emitted if the tool raises an exception (double-logging on failure)
logger.error(
    f"Error calling tool: {name} with arguments: {arguments} "
    f"in {end_time - start_time}ms: {e}"
)

arguments is the raw Python dict received from the MCP client. It is string-interpolated directly into the log message. On a tool call that raises an exception, the same dict is logged twice — once at INFO and once at ERROR.

File logging is activated by DBT_MCP_SERVER_FILE_LOGGING=true (a documented feature in the project README). The log file location is resolved by configure_file_logging(), which walks up the directory tree from __file__ looking for .git or pyproject.toml, falling back to $HOME. Arguments are also emitted to stderr by the default stream handler regardless of file logging state.

PoC

MCP client script — triggers real tool calls and verifies log file contents:

#!/usr/bin/env python3
# poc4_tool_args_logged.py
# Vulnerable code: src/dbt_mcp/mcp/server.py line 67, 77-79
# configure_file_logging(): src/dbt_mcp/telemetry/logging.py

import logging
from pathlib import Path

LOG_FILENAME = "dbt-mcp.log"

def configure_file_logging(log_level: int = logging.INFO) -> Path:
    """Reproduction of configure_file_logging() from telemetry/logging.py."""
    module_path = Path(__file__).resolve().parent
    home = Path.home().resolve()
    for candidate in [module_path, *module_path.parents]:
        if (candidate / ".git").exists() or (candidate / "pyproject.toml").exists() or candidate == home:
            repo_root = candidate
            break
    log_path = repo_root / LOG_FILENAME
    root_logger = logging.getLogger()
    root_logger.setLevel(log_level)
    file_handler = logging.FileHandler(log_path, encoding="utf-8")
    file_handler.setLevel(log_level)
    file_handler.setFormatter(
        logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s")
    )
    root_logger.addHandler(file_handler)
    return log_path

log_path = configure_file_logging()
server_logger = logging.getLogger("dbt_mcp.mcp.server")

# Exact log statements from server.py line 67 and line 77-79
name = "show"
arguments = {"sql_query": "SELECT ssn, credit_card_number, salary FROM customers WHERE id = 42", "limit": 5}
server_logger.info(f"Calling tool: {name} with arguments: {arguments}")

name2 = "run"
arguments2 = {"node_selection": "sensitive_model", "vars": '{"db_password": "hunter2", "api_key": "sk-prod-abc123xyz"}', "is_full_refresh": False}
server_logger.info(f"Calling tool: {name2} with arguments: {arguments2}")

# Verify file contents
lines = log_path.read_text(encoding="utf-8").splitlines()
poc_lines = [l for l in lines if "dbt_mcp.mcp.server" in l]
print(f"[log file: {log_path}]")
for line in poc_lines:
    print(f"  {line}")

keywords = ["ssn", "credit_card_number", "salary", "db_password", "api_key"]
found = [kw for kw in keywords if any(kw in l for l in poc_lines)]
if found:
    print(f"\n[CONFIRMED] Sensitive keywords in plaintext log: {found}")
    print(f"[CONFIRMED] No…
Record
GHSA-7xgw-6qf3-7w59
CVSS 3.1
2.5 LowCVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
Weakness
CWE-532
Updated
2026-11-02
OSVPYSEC-2026-2441 2.5

dbt MCP Server Logs Tool Arguments Including SQL Queries and Credentials in Plaintext Without Redaction When File Logging Is Enabled

Discovered through manual source code review. Verified by PoC execution against a local dbt-mcp v1.15.1 installation.

Summary

DbtMCP.call_tool() in src/dbt_mcp/mcp/server.py logs the complete raw arguments dictionary at INFO level on every tool invocation (line 67) and again at ERROR level if the call raises an exception (lines 77–79). No field is redacted before logging. When the documented DBT_MCP_SERVER_FILE_LOGGING=true feature is enabled, these log records are written to dbt-mcp.log in the project root directory as plaintext. Sensitive data — raw SQL queries, --vars payloads carrying credentials, node selectors — persists on disk indefinitely with no automatic rotation or deletion.

Details

Vulnerable log statements (server.py):

# Line 67 — emitted before every tool execution
logger.info(f"Calling tool: {name} with arguments: {arguments}")

# Lines 77–79 — emitted if the tool raises an exception (double-logging on failure)
logger.error(
    f"Error calling tool: {name} with arguments: {arguments} "
    f"in {end_time - start_time}ms: {e}"
)

arguments is the raw Python dict received from the MCP client. It is string-interpolated directly into the log message. On a tool call that raises an exception, the same dict is logged twice — once at INFO and once at ERROR.

File logging is activated by DBT_MCP_SERVER_FILE_LOGGING=true (a documented feature in the project README). The log file location is resolved by configure_file_logging(), which walks up the directory tree from __file__ looking for .git or pyproject.toml, falling back to $HOME. Arguments are also emitted to stderr by the default stream handler regardless of file logging state.

PoC

MCP client script — triggers real tool calls and verifies log file contents:

#!/usr/bin/env python3
# poc4_tool_args_logged.py
# Vulnerable code: src/dbt_mcp/mcp/server.py line 67, 77-79
# configure_file_logging(): src/dbt_mcp/telemetry/logging.py

import logging
from pathlib import Path

LOG_FILENAME = "dbt-mcp.log"

def configure_file_logging(log_level: int = logging.INFO) -> Path:
    """Reproduction of configure_file_logging() from telemetry/logging.py."""
    module_path = Path(__file__).resolve().parent
    home = Path.home().resolve()
    for candidate in [module_path, *module_path.parents]:
        if (candidate / ".git").exists() or (candidate / "pyproject.toml").exists() or candidate == home:
            repo_root = candidate
            break
    log_path = repo_root / LOG_FILENAME
    root_logger = logging.getLogger()
    root_logger.setLevel(log_level)
    file_handler = logging.FileHandler(log_path, encoding="utf-8")
    file_handler.setLevel(log_level)
    file_handler.setFormatter(
        logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s")
    )
    root_logger.addHandler(file_handler)
    return log_path

log_path = configure_file_logging()
server_logger = logging.getLogger("dbt_mcp.mcp.server")

# Exact log statements from server.py line 67 and line 77-79
name = "show"
arguments = {"sql_query": "SELECT ssn, credit_card_number, salary FROM customers WHERE id = 42", "limit": 5}
server_logger.info(f"Calling tool: {name} with arguments: {arguments}")

name2 = "run"
arguments2 = {"node_selection": "sensitive_model", "vars": '{"db_password": "hunter2", "api_key": "sk-prod-abc123xyz"}', "is_full_refresh": False}
server_logger.info(f"Calling tool: {name2} with arguments: {arguments2}")

# Verify file contents
lines = log_path.read_text(encoding="utf-8").splitlines()
poc_lines = [l for l in lines if "dbt_mcp.mcp.server" in l]
print(f"[log file: {log_path}]")
for line in poc_lines:
    print(f"  {line}")

keywords = ["ssn", "credit_card_number", "salary", "db_password", "api_key"]
found = [kw for kw in keywords if any(kw in l for l in poc_lines)]
if found:
    print(f"\n[CONFIRMED] Sensitive keywords in plaintext log: {found}")
    print(f"[CONFIRMED] No…
Record
PYSEC-2026-2441
CVSS 3.1
2.5 LowCVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
dbt-mcp
PyPI
through 1.17.0
1.17.1
dbt-mcp
PyPI
before 1.17.1, plus listed versions
0.0.1a10.1.00.1.10.1.20.1.2rc1
Show 45 moreShow less
0.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

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-44969 is a low vulnerability (CVSS 2.5) 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.17%, the 5th percentile. Fixed in dbt-mcp 1.17.1.

References

This page doesn't list everything

Left out here: 19 versions and the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open