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

Published May 14, 2026
·
Updated

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

Summary

DbtMCP.calltool() in src/dbtmcp/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 DBTMCPSERVERFILELOGGING=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):

python 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 {endtime - starttime}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 DBTMCPSERVERFILELOGGING=true (a documented feature in the project README). The log file location is resolved by configurefilelogging(), 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:

python #!/usr/bin/env python3 poc4toolargslogged.py Vulnerable code: src/dbtmcp/mcp/server.py line 67, 77-79 configurefilelogging(): src/dbtmcp/telemetry/logging.py

import logging from pathlib import Path

LOGFILENAME = "dbt-mcp.log"

def configurefilelogging(loglevel: int = logging.INFO) -> Path: """Reproduction of configurefilelogging() from telemetry/logging.py.""" modulepath = Path(file).resolve().parent home = Path.home().resolve() for candidate in [modulepath, modulepath.parents]: if (candidate / ".git").exists() or (candidate / "pyproject.toml").exists() or candidate == home: reporoot = candidate break logpath = reporoot / LOGFILENAME rootlogger = logging.getLogger() rootlogger.setLevel(loglevel) filehandler = logging.FileHandler(logpath, encoding="utf-8") filehandler.setLevel(loglevel) filehandler.setFormatter( logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s") ) rootlogger.addHandler(filehandler) return logpath

logpath = configurefilelogging() serverlogger = logging.getLogger("dbtmcp.mcp.server")

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

name2 = "run" arguments2 = {"nodeselection": "sensitivemodel", "vars": '{"dbpassword": "hunter2", "apikey": "sk-prod-abc123xyz"}', "isfullrefresh": False} serverlogger.info(f"Calling tool: {name2} with arguments: {arguments2}")

Verify file contents lines = logpath.readtext(encoding="utf-8").splitlines() poclines = [l for l in lines if "dbtmcp.mcp.server" in l] print(f"[log file: {logpath}]") for line in poclines: print(f" {line}")

keywords = ["ssn", "creditcardnumber", "salary", "dbpassword", "apikey"] found = [kw for kw in keywords if any(kw in l for l in poclines)] if found: print(f"\n[CONFIRMED] Sensitive keywords in plaintext log: {found}") print(f"[CONFIRMED] No redaction applied. File persists at {logpath}")

Expected log file entries:

2026-04-27 ... INFO [dbtmcp.mcp.server] Calling tool: show with arguments: {'sqlquery': 'SELECT ssn, creditcardnumber, salary FROM customers', 'limit': 5}

2026-04-27 ... INFO [dbtmcp.mcp.server] Calling tool: run with arguments: {'nodeselection': 'sensitivemodel', 'vars': '{"dbpassword":"hunter2","apikey":"sk-prod-abc123"}', 'isfullrefresh': False}

[CONFIRMED] Sensitive keywords in plaintext log: ['ssn', 'creditcardnumber', 'salary', 'dbpassword', 'apikey'] [CONFIRMED] No redaction applied.

<img width="3798" height="462" alt="image" src="https://github.com/user-attachments/assets/b4c23a93-b3d3-4b7f-ba46-3d4a324d609f" />

Impact

Directly proven by this PoC:

- When DBTMCPSERVERFILELOGGING=true, the full arguments dict of every tool call — including sqlquery, vars, and nodeselection — is written to dbt-mcp.log in plaintext on every invocation. - A tool call that raises an exception produces two log entries with the same sensitive content (INFO + ERROR double-logging). - The log file has no automatic rotation, expiry, or access restriction beyond filesystem permissions.

Combined with Advisory 3 (telemetry), a single show tool call containing PII produces one telemetry transmission to dbt Labs and one (or two, on failure) persistent log entries on disk.

Remediation

redact known-sensitive argument values before logging:

python LOGREDACT = frozenset({"sqlquery", "vars"})

def safeargs(arguments: dict) -> dict: return {k: "redacted" if k in LOGREDACT else v for k, v in arguments.items()}

server.py line 67: logger.info(f"Calling tool: {name} with arguments: {safeargs(arguments)}")

server.py lines 77-79: logger.error( f"Error calling tool: {name} with arguments: {safeargs(arguments)} " f"in {endtime - starttime}ms: {e}" )

log argument keys only:

python logger.info(f"Calling tool: {name} with argument keys: {list(arguments.keys())}")

File logging: Consider reducing the default log level for the file handler to WARNING so that normal-operation INFO records (which include arguments) are not persisted. Sensitive content would only appear in file logs on error.

Other sources

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

— MITRE

Affected Software

2 affected componentsFixes available
pip/dbt-mcp<=1.17.0
1.17.1
getdbt Dbt Mcp Server<1.17.1

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/dbt-mcp to a version that resolves this vulnerability.

    Fixed in 1.17.1
  2. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Fixed in 1.17.1
  3. Configuration

    Set DBT_MCP_SERVER_FILE_LOGGING=false to prevent dbt-mcp.log plaintext persistence of tool arguments (including sql_query/vars/node_selection) when file logging is enabled.

    dbt-mcp file logging DBT_MCP_SERVER_FILE_LOGGING = false
  4. Configuration

    Configure logging redaction so that at least these argument keys are redacted before logging: sql_query and vars (as shown by _LOG_REDACT).

    dbt-mcp server log redaction _LOG_REDACT = {"sql_query","vars"}
  5. Configuration

    Reduce the default log level for the file handler to WARNING so normal-operation INFO records (which include arguments) are not persisted.

    dbt-mcp file handler logging level log_level = WARNING

Event History

May 14, 2026
Advisory Published
via GitHub·06:24 PM
Data Sourced
via GitHub·06:24 PM
DescriptionSeverityWeaknessAffected Software
Jul 16, 2026
CVE Published
via MITRE·05:49 PM
Data Sourced
via MITRE·05:49 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·06:16 PM
RemedyDescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What is the severity of CVE-2026-44969?

CVE-2026-44969 is identified as a moderate severity vulnerability due to improper logging of sensitive information.

2

How do I fix CVE-2026-44969?

To mitigate CVE-2026-44969, upgrade dbt-mcp to version 1.17.1 or later.

3

What software is affected by CVE-2026-44969?

CVE-2026-44969 affects dbt-mcp versions up to and including 1.17.0.

4

What is the nature of CVE-2026-44969?

CVE-2026-44969 is a vulnerability that allows sensitive arguments to be logged at the INFO level, potentially exposing them.

5

When was CVE-2026-44969 discovered?

CVE-2026-44969 was discovered through a manual source code review.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203