GHSA-j7qq-vr8c-xvw3 CVE-2026-116691
MLflow CrewAI autologging records LLM provider API keys in logged trace spans
- Severity
- 6.5 / 10
- Medium · CVSS 3.1
- Exploit chance (EPSS)
- 0.21%
- Chance of exploitation in the next 30 days. Higher than 10.86% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-200 Exposure of Sensitive Information to an Unauthorized Actor
- CWE-312 Cleartext Storage of Sensitive Information
- CWE-532 Insertion of Sensitive Information into Log File
- Fix
3.17.0- Upgrade mlflow to this version or later
Description
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. End-to-end trace disclosure
The issue can also be reproduced through MLflow's actual OTLP exporter.
Configure a local OTLP HTTP receiver and set:
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://127.0.0.1:4318/v1/traces
export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
export CREWAI_DISABLE_TELEMETRY=true
Then run:
import mlflow
from crewai import Agent, Crew, LLM, Task
mlflow.crewai.autolog()
llm = LLM(
model="gpt-4.1-mini",
api_key="sk-proj-EXAMPLE1234567890",
)
agent = Agent(
role="Support Responder",
goal="Answer support questions",
backstory="A support agent",
llm=llm,
)
task = Task(
description="Answer the user's question",
expected_output="A response",
agent=agent,
)
crew = Crew(
agents=[agent],
tasks=[task],
)
crew.kickoff()
A local OTLP receiver can decode the ExportTraceServiceRequest sent by MLflow.
The provider request is expected to fail because the credential is intentionally fake. This does not affect the vulnerability: the sensitive value is added to the MLflow span when the instrumentation starts, before the provider call succeeds or fails.
In the tested environment, the fake API key appeared in the exported telemetry in 5 locations across 2 spans, including:
Support Responder
attributes.llm
attributes.function_calling_llm
attributes.agent_executor
attributes.mlflow.spanInputs
Task.execute_sync
attributes.mlflow.spanInputs
The captured value is produced by MLflow's actual telemetry/export path rather than being manually inserted into a span.
All credentials used in the reproduction are locally generated fake values. No real API credential is required.
Impact
Type: Sensitive Information Disclosure / Credential Disclosure CWE: CWE-532 — Insertion of Sensitive Information into Log File
Any application using mlflow.crewai.autolog() with a CrewAI LLM configured with an API key can unintentionally disclose that credential through MLflow traces.
The credential is written to trace telemetry even though the application does not explicitly log it.
Anyone with access to the MLflow trace storage or configured OTLP destination may therefore obtain the provider credential. Depending on the deployment, this can include MLflow users, observability operators, or third-party telemetry providers.
The leaked credential can then potentially be used against the corresponding LLM provider account, including for unauthorized API usage and associated costs.
The issue is particularly significant because the credential crosses a trust boundary: a provider API key intended for use by the application is automatically copied into observability telemetry where a broader set of users or systems may have access.
The vulnerability does not require the developer to intentionally log the secret. The only relevant prerequisite is enabling the CrewAI autologging integration.
Patches
Fixed in MLflow 3.17.0 by #25845. mlflow.crewai.autolog() now reduces CrewAI LLM objects to their model name and drops credential-like keys before they are written to span attributes or mlflow.spanInputs.
Remediation
- Upgrade to
mlflow>=3.17.0. - Upgrading stops new leakage but does not remove credentials already written. Rotate any LLM provider key that a CrewAI
LLMobject held (for example one passed asLLM(api_key=...)) whilemlflow.crewai.autolog()was enabled on MLflow 2.19.0 through 3.16.x. - Delete or redact the affected traces in the MLflow tracking backend and in any OTLP destination they were exported to. The key appears in Agent and Crew span attributes and in
mlflow.spanInputs.
Workarounds
If you cannot upgrade yet, disable CrewAI autologging with mlflow.crewai.autolog(disable=True).
Affected versions
PyPI
| Package | Vulnerable | Fixed in |
|---|---|---|
| mlflow | >= 2.19.0, < 3.17.0 |
3.17.0 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
mlflow PyPI → 3.17.0
- pip
pip install --upgrade "mlflow>=3.17.0"- poetry
poetry add "mlflow>=3.17.0"- uv
uv add "mlflow>=3.17.0"
Severity breakdown
CVSS 3.1 6.5
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- Low
- User interaction
- None
- Scope
- Unchanged
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Weakness
CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
How to prevent it
- Architecture and Design: Compartmentalize the system to have "safe" areas where trust boundaries can be unambiguously drawn. Do not allow sensitive data to go outside of the trust boundary and always be careful when interfacing with a compartment outside of the safe area. Ensure that appropriate compartmentalization is built into the system design, and the compartmentalization allows for and reinforces privilege separation functionality. Architects and designers should rely on the principle of least privilege to decide the appropriate time to use privileges and the time to drop privileges.
CWE-312: Cleartext Storage of Sensitive Information
The product stores sensitive information in cleartext within a resource that might be accessible to another control sphere.
How to prevent it
- Implementation, System Configuration, Operation: When storing data in the cloud (e.g., S3 buckets, Azure blobs, Google Cloud Storage, etc.), use the provider's controls to encrypt the data at rest. [REF-1297] [REF-1299] [REF-1301]
- Implementation, System Configuration, Operation: In some systems/environments such as cloud, the use of "double encryption" (at both the software and hardware layer) might be required, and the developer might be solely responsible for both layers, instead of shared responsibility with the administrator of the broader system/environment.
CWE-532: Insertion of Sensitive Information into Log File
The product writes sensitive information to a log file.
How to prevent it
- Architecture and Design, Implementation: Consider seriously the sensitivity of the information written into log files. Do not write secrets into the log files.
- Distribution: Remove debug log files before deploying the application into production.
- Operation: Protect log files against unauthorized read/write.
- Implementation: Adjust configurations appropriately when software is transitioned from a debug state to production.
References
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-7gwp-5pfp-969j MLflow: Unauthenticated full-read SSRF in webhook delivery: _validate_webhook_url bypassed via unvalidated HTTP redirects (and DNS rebinding) | Critical Known exploited | Also affects mlflow |
| GHSA-26p8-2jq9-3vq9 MLflow vulnerable to remote code execution on unauthenticated servers via third-party scorer deserialization importing caller-placed artifact modules | Critical | Also affects mlflow |
| GHSA-m65p-9j7q-6w33 MLflow vulnerable to arbitrary file read on unauthenticated servers via prompt model version source | High | Also affects mlflow |
| GHSA-2wqp-q3c6-ccj5 MLflow: SSRF on unauthenticated MLflow servers via third-party scorer kwargs in the scorer invoke, register, and GenAI evaluate endpoints | Medium | Also affects mlflow |
| GHSA-wqmh-qccw-pqwf MLflow FileStore trace IDs allow path traversal and arbitrary file writes | High | Also affects mlflow |
| GHSA-gqvg-gmmx-x4hm MLFLOW_ALLOW_PICKLE_DESERIALIZATION=False safety control bypassed by mlflow.statsmodels flavor — RCE via crafted model artifact | High | Also affects mlflow |
| GHSA-h7x2-h6g9-p789 MLflow AI Gateway permits SSRF through an unvalidated api_base | High | Also affects mlflow |
| GHSA-2cm6-r77w-6g96 MLflow: trace API endpoints lack proper authorization validators | High | Also affects mlflow |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor