CVE-2026-116691
MLflow CrewAI autologging records LLM provider API keys in logged trace spans
MLflow is an open source AI engineering platform for agents, large language models, and machine learning models. From 2.19.0 until 3.17.0, mlflow.crewai.autolog() records CrewAI Agent objects without consistently sanitizing nested credential-bearing values. mlflow/crewai/autolog.py::_get_agent_attributes() converts the Agent llm field to a string that includes the provider API key, while mlflow/crewai/autolog.py::_construct_full_inputs() expands the Agent object into mlflow.spanInputs. Applications can therefore send plaintext provider credentials to the MLflow trace backend or a configured OTLP destination, where trace readers can obtain them. This issue is fixed in version 3.17.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
6.5
MLflow CrewAI autologging records LLM provider API keys in logged trace spans
MLflow is an open source AI engineering platform for agents, large language models, and machine learning models. From 2.19.0 until 3.17.0, mlflow.crewai.autolog() records CrewAI Agent objects without consistently sanitizing nested credential-bearing values. mlflow/crewai/autolog.py::_get_agent_attributes() converts the Agent llm field to a string that includes the provider API key, while mlflow/crewai/autolog.py::_construct_full_inputs() expands the Agent object into mlflow.spanInputs. Applications can therefore send plaintext provider credentials to the MLflow trace backend or a configured OTLP destination, where trace readers can obtain them. This issue is fixed in version 3.17.0.
- Record
CVE-2026-116691- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200, CWE-312, CWE-532
- Updated
- 2026-11-03
EUVDEUVD-2026-111381
6.5
MLflow is an open source AI engineering platform for agents, large language models, and machine learning models. From 2.19.0 until 3.17.0, mlflow.crewai.autolog() records CrewAI Agent objects without consistently sanitizing nested credential-bearing values. mlflow/crewai/autolog.py::_get_agent_attributes() converts the Agent llm field to a string that includes the provider API key, while mlflow/crewai/autolog.py::_construct_full_inputs() expands the Agent object into mlflow.spanInputs. Applications can therefore send plaintext provider credentials to the MLflow trace backend or a configured OTLP destination, where trace readers can obtain them. This issue is fixed in version 3.17.0.
- Record
EUVD-2026-111381- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Updated
- 2026-11-03
GitHub AdvisoriesGHSA-j7qq-vr8c-xvw3
6.5
MLflow CrewAI autologging records LLM provider API keys in logged trace spans
Summary
mlflow.crewai.autolog() leaks CrewAI LLM provider API keys into MLflow trace telemetry.
When a CrewAI Agent is instrumented with MLflow autologging, MLflow serializes the agent's __dict__ and converts its fields to strings. The llm field contains the CrewAI LLM object, whose string representation includes its api_key in plaintext.
As a result, an application that calls mlflow.crewai.autolog() can unintentionally send its LLM provider API key to the MLflow trace backend or configured OTLP destination.
The application does not need to explicitly log the credential or place it in a span attribute. The credential is extracted automatically by MLflow's instrumentation.
This was verified on MLflow 3.16.0 with CrewAI 1.15.18 using MLflow's own OTLP exporter. The fake credential appeared in 5 attribute locations across 2 spans.
Details
The primary issue is in:
mlflow/crewai/autolog.py::_get_agent_attributes()
The function walks the CrewAI Agent.__dict__ and converts agent fields to strings. tools and embedder receive special handling, but credential-bearing objects such as the following are not sanitized:
llmfunction_calling_llmagent_executorcrew
For example, agent.llm is a CrewAI LLM object. In CrewAI 1.15.18, the underlying LLM model contains api_key as a normal field. Its string representation therefore contains the credential:
llm_type='openai' model='gpt-4.1-mini' ... api_key='sk-proj-...' ...
MLflow subsequently stores this string as a span attribute.
There is an important indication that this is an unintended disclosure: MLflow's dedicated LLM attribute extraction already explicitly excludes api_key in _get_llm_attributes(). The LLM span therefore does not expose the credential, while the broader Agent __dict__ serialization bypasses that protection.
There is also a second, independent serialization path in:
mlflow/crewai/autolog.py::_construct_full_inputs()
This code uses:
v.__dict__ if hasattr(v, "__dict__") else v
on call arguments. During CrewAI execution, the Agent object is passed as an argument, causing its __dict__ to be expanded and the credential-bearing LLM object to be included in mlflow.spanInputs.
Therefore, there are two independent leakage paths:
- Agent span attributes through
_get_agent_attributes() - Span inputs through
_construct_full_inputs()
Fixing only the Agent attribute serialization would leave the mlflow.spanInputs path vulnerable.
A safer approach would be to avoid recursively serializing arbitrary object __dict__ values. Agent telemetry should use an explicit allowlist of non-sensitive scalar fields, and credential-bearing objects such as llm, function_calling_llm, agent_executor, and crew should either be reduced to safe metadata or omitted.
Similarly, _construct_full_inputs() should not automatically expand arbitrary objects through __dict__, or the resulting values should pass through the same sanitization mechanism before being attached to a span.
PoC
Environment:
MLflow: 3.16.0
CrewAI: 1.15.18
Python: 3.12.3
opentelemetry-exporter-otlp-proto-http: 1.42.1
1. Direct serialization proof
The following demonstrates the vulnerability without starting a Crew, making a network request, or requiring an MLflow server:
import importlib
from crewai import LLM, Agent
autolog = importlib.import_module("mlflow.crewai.autolog")
llm = LLM(
model="gpt-4.1-mini",
api_key="sk-proj-EXAMPLE1234567890",
)
agent = Agent(
role="r",
goal="g",
backstory="b",
llm=llm,
)
attrs = autolog._get_agent_attributes(agent)
assert "sk-proj-EXAMPLE1234567890" in attrs["llm"]
print(attrs["llm"])
The output contains the API key in plaintext, for example:
llm_type='openai' model='gpt-4.1-mini' ... api_key='sk-proj-EXAMPLE1234567890' ...
No provider request is required.
2.…
- Record
GHSA-j7qq-vr8c-xvw3- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200, CWE-312, CWE-532
- Updated
- 2026-11-02
NVD
6.5
MLflow is an open source AI engineering platform for agents, large language models, and machine learning models. From 2.19.0 until 3.17.0, mlflow.crewai.autolog() records CrewAI Agent objects without consistently sanitizing nested credential-bearing values. mlflow/crewai/autolog.py::_get_agent_attributes() converts the Agent llm field to a string that includes the provider API key, while mlflow/crewai/autolog.py::_construct_full_inputs() expands the Agent object into mlflow.spanInputs. Applications can therefore send plaintext provider credentials to the MLflow trace backend or a configured OTLP destination, where trace readers can obtain them. This issue is fixed in version 3.17.0.
- Record
CVE-2026-116691- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200, CWE-312, CWE-532
- Updated
- 2026-11-03
OSV
6.5
MLflow CrewAI autologging records LLM provider API keys in logged trace spans
MLflow is an open source AI engineering platform for agents, large language models, and machine learning models. From 2.19.0 until 3.17.0, mlflow.crewai.autolog() records CrewAI Agent objects without consistently sanitizing nested credential-bearing values. mlflow/crewai/autolog.py::_get_agent_attributes() converts the Agent llm field to a string that includes the provider API key, while mlflow/crewai/autolog.py::_construct_full_inputs() expands the Agent object into mlflow.spanInputs. Applications can therefore send plaintext provider credentials to the MLflow trace backend or a configured OTLP destination, where trace readers can obtain them. This issue is fixed in version 3.17.0.
- Record
CVE-2026-116691- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200, CWE-312, CWE-532
- Updated
- 2026-11-07
OSVGHSA-j7qq-vr8c-xvw3
6.5
MLflow CrewAI autologging records LLM provider API keys in logged trace spans
Summary
mlflow.crewai.autolog() leaks CrewAI LLM provider API keys into MLflow trace telemetry.
When a CrewAI Agent is instrumented with MLflow autologging, MLflow serializes the agent's __dict__ and converts its fields to strings. The llm field contains the CrewAI LLM object, whose string representation includes its api_key in plaintext.
As a result, an application that calls mlflow.crewai.autolog() can unintentionally send its LLM provider API key to the MLflow trace backend or configured OTLP destination.
The application does not need to explicitly log the credential or place it in a span attribute. The credential is extracted automatically by MLflow's instrumentation.
This was verified on MLflow 3.16.0 with CrewAI 1.15.18 using MLflow's own OTLP exporter. The fake credential appeared in 5 attribute locations across 2 spans.
Details
The primary issue is in:
mlflow/crewai/autolog.py::_get_agent_attributes()
The function walks the CrewAI Agent.__dict__ and converts agent fields to strings. tools and embedder receive special handling, but credential-bearing objects such as the following are not sanitized:
llmfunction_calling_llmagent_executorcrew
For example, agent.llm is a CrewAI LLM object. In CrewAI 1.15.18, the underlying LLM model contains api_key as a normal field. Its string representation therefore contains the credential:
llm_type='openai' model='gpt-4.1-mini' ... api_key='sk-proj-...' ...
MLflow subsequently stores this string as a span attribute.
There is an important indication that this is an unintended disclosure: MLflow's dedicated LLM attribute extraction already explicitly excludes api_key in _get_llm_attributes(). The LLM span therefore does not expose the credential, while the broader Agent __dict__ serialization bypasses that protection.
There is also a second, independent serialization path in:
mlflow/crewai/autolog.py::_construct_full_inputs()
This code uses:
v.__dict__ if hasattr(v, "__dict__") else v
on call arguments. During CrewAI execution, the Agent object is passed as an argument, causing its __dict__ to be expanded and the credential-bearing LLM object to be included in mlflow.spanInputs.
Therefore, there are two independent leakage paths:
- Agent span attributes through
_get_agent_attributes() - Span inputs through
_construct_full_inputs()
Fixing only the Agent attribute serialization would leave the mlflow.spanInputs path vulnerable.
A safer approach would be to avoid recursively serializing arbitrary object __dict__ values. Agent telemetry should use an explicit allowlist of non-sensitive scalar fields, and credential-bearing objects such as llm, function_calling_llm, agent_executor, and crew should either be reduced to safe metadata or omitted.
Similarly, _construct_full_inputs() should not automatically expand arbitrary objects through __dict__, or the resulting values should pass through the same sanitization mechanism before being attached to a span.
PoC
Environment:
MLflow: 3.16.0
CrewAI: 1.15.18
Python: 3.12.3
opentelemetry-exporter-otlp-proto-http: 1.42.1
1. Direct serialization proof
The following demonstrates the vulnerability without starting a Crew, making a network request, or requiring an MLflow server:
import importlib
from crewai import LLM, Agent
autolog = importlib.import_module("mlflow.crewai.autolog")
llm = LLM(
model="gpt-4.1-mini",
api_key="sk-proj-EXAMPLE1234567890",
)
agent = Agent(
role="r",
goal="g",
backstory="b",
llm=llm,
)
attrs = autolog._get_agent_attributes(agent)
assert "sk-proj-EXAMPLE1234567890" in attrs["llm"]
print(attrs["llm"])
The output contains the API key in plaintext, for example:
llm_type='openai' model='gpt-4.1-mini' ... api_key='sk-proj-EXAMPLE1234567890' ...
No provider request is required.
2.…
- Record
GHSA-j7qq-vr8c-xvw3- CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N - Weakness
- CWE-200, CWE-312, CWE-532
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
mlflowPyPI |
2.19.0 up to 3.17.0 | 3.17.0 |
mlflowPyPI |
2.19.0 up to 3.17.0, plus listed versions 2.20.02.20.0rc02.20.12.20.22.20.3 Show 45 moreShow less2.20.42.21.02.21.0rc02.21.12.21.22.21.32.22.02.22.0rc02.22.12.22.22.22.32.22.42.22.53.0.03.0.0rc03.0.0rc13.0.0rc23.0.0rc33.0.13.1.03.1.0rc03.1.13.1.23.1.33.1.43.10.03.10.0rc03.10.13.11.03.11.0rc03.11.0rc13.11.13.12.03.12.0rc03.13.03.13.0rc03.14.03.15.03.15.13.15.23.16.03.16.13.2.03.2.0rc03.3.0 |
3.17.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| facts | NVD | |
| references | CVE List, EUVD | |
| 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-116691 is a medium vulnerability (CVSS 6.5) in Mlflow, published October 27, 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 mlflow 3.17.0.
References
github.com/mlflow/mlflow/security/advisories/GHSA-j7qq-vr8c-xvw3
github.com/mlflow/mlflow/pull/25845
github.com/mlflow/mlflow/commit/20927930d27f0072fcfeb7f024f24ad8c5e77c47
github.com/mlflow/mlflow/releases/tag/v3.17.0
github.com/advisories/GHSA-j7qq-vr8c-xvw3
github.com/CVEProject/cvelistV5/tree/main/cves/2026/116xxx/CVE-2026-116691.json
nvd.nist.gov/vuln/detail/CVE-2026-116691
github.com/mlflow/mlflow
Left out here: 17 versions and the rest of 2 source descriptions. The JSON record has them.