Aug 13, 2026·7 min read·20 visits
A Confused Deputy SSRF vulnerability in Pydantic AI UI adapters allows remote, unauthenticated attackers to retrieve and exfiltrate private cloud storage files by injecting manipulated metadata structures into client-submitted message histories.
A Server-Side Request Forgery (SSRF) / Confused Deputy vulnerability has been identified in Pydantic AI UI Adapters (such as VercelAIAdapter). Under certain conditions, a malicious client can supply manipulated message history with provider metadata that forces the server to resolve files within privileged cloud environments (AWS S3, Google Cloud Storage) or model providers. This occurs because the adapters deserialize client-provided metadata structures directly into UploadedFile instances without validation, which are subsequently fetched using high-privilege server credentials.
Pydantic AI is a pythonic agent framework built on Pydantic to simplify Generative AI application development. Among its features, it provides UI adapters like VercelAIAdapter and AGUIAdapter to ease integrations with frontend protocol standards (e.g., the Vercel AI SDK). These adapters act as intermediaries, translating standard external client JSON payloads containing chat histories into internal message models like ModelRequest and ModelResponse structures.
When processing message history, frontend protocols often allow users to include file attachments. The server-side UI adapters must deserialize these attachment records into a structured format before passing them to the language model provider. Inside the vulnerable versions, the library parsed metadata elements (such as providerMetadata fields) to reconstruct representation structures of uploaded assets. Specifically, it mapped files to instances of the UploadedFile class.
This architecture exposes an attack surface because the deserializer failed to distinguish between client-uploaded files and server-side assets. While standard FileUrl strings were checked against an explicit allowlist of valid URL schemes (defaulting to HTTP and HTTPS), parameters matching UploadedFile bypassed this filter entirely. Consequently, a client-supplied identifier could represent file systems, private storage buckets, or internal vendor resources, leading directly to a Server-Side Request Forgery (SSRF) condition.
The root cause of this vulnerability lies in the improper trust boundary validation within the UI adapters' message-parsing logic. Specifically, the adapters trusted the structure of the JSON-serialized metadata sent directly by the untrusted web client. If a client sent a message payload containing a providerMetadata object under the pydantic_ai namespace, the deserialization logic implicitly trusted this input to populate the parameters of an UploadedFile instance.
An UploadedFile structure can point directly to external, restricted assets using a model-provider file ID or a raw cloud storage URI, such as s3:// or gs://. Because the library lacked a verification routine for client-submitted file metadata, these references were passed unchanged downstream. When the Pydantic AI agent initiated a request to the model provider (e.g., AWS Bedrock or OpenAI), it supplied the structured UploadedFile references to be processed by the LLM.
At this juncture, the system operates as a classic Confused Deputy. The model provider, acting on behalf of the server-side application, resolves the cloud-storage URI or file ID using the server's high-privilege context, such as AWS IAM instance profiles, service account credentials, or provider API keys. The server does not validate whether the active client session has the authorization to access that specific asset. If the storage asset exists and the server credentials have read access, the file contents are fetched, ingested into the LLM context, and ultimately leaked back to the client.
The fix for this vulnerability was introduced in pull request #5772. The core remediation strategy forces the UI adapters to strip and ignore any client-submitted UploadedFile references by default, unless the system is explicitly configured with a new security parameter: preserve_file_data = True.
In pydantic_ai_slim/pydantic_ai/ui/_adapter.py, the base UIAdapter class was updated to declare and default this property:
# Base UIAdapter updates
preserve_file_data: bool = False
"""Whether to keep UploadedFile items from client-submitted messages.
Defaults to False. By default, UploadedFile items in client-submitted messages are
dropped with a warning before the messages are passed to the agent...
"""The message sanitization routine (sanitize_messages) was updated to track dropped uploaded file providers and raise a warning to alert administrators of potential exploitation or misconfigurations. The inner sanitization of user content demonstrates how the validation logic was altered:
def _filter_user_content(
self,
content: Sequence[UserContent],
disallowed_schemes: set[str],
reset_force_download_values: set[ForceDownloadMode],
dropped_uploaded_file_providers: set[str],
) -> list[UserContent]:
filtered: list[UserContent] = []
for item in content:
if isinstance(item, FileUrl):
# Checks URL schemes
scheme = urlparse(item.url).scheme.lower()
if scheme not in self.allowed_file_url_schemes:
disallowed_schemes.add(scheme)
continue
item = self._sanitize_file_url(item, reset_force_download_values)
# FIX: Check if UploadedFile structure is supplied
elif isinstance(item, UploadedFile) and not self.preserve_file_data:
# Record the dropped file provider and omit from the payload
dropped_uploaded_file_providers.add(item.provider_name)
continue
filtered.append(item)
return filteredAdditionally, identical logic was added inside _sanitize_tool_return_content to prevent nested file structures within tool returns from bypassing the security filter. If preserve_file_data is configured to True, the library delegates trust entirely to the frontend configuration, indicating that the developer has separately implemented authorization mechanisms or operates within a trusted, authenticated perimeter.
Exploitation of CVE-2026-54249 does not require authentication to the backend model provider, but requires access to the public endpoint exposing the UI adapter. The attack relies on guessing or obtaining a valid cloud storage identifier or provider file ID that the backend's identity is authorized to read.
An attacker begins by sending an HTTP POST request representing a user message to the endpoint mapped to the UI adapter. The message content urges the LLM to read and output the details of a file. In the payload, the attacker defines a parts array where they inject a standard URL, but override the providerMetadata dictionary with target parameters:
{
"role": "user",
"content": "Verify and summarize this file for me.",
"parts": [
{
"type": "file",
"mediaType": "application/pdf",
"url": "https://legitimate.example.com/placeholder.pdf",
"providerMetadata": {
"pydantic_ai": {
"file_id": "s3://internal-company-confidential/salaries-2026.pdf",
"provider_name": "bedrock"
}
}
}
]
}Upon receiving the request, the server translates the payload. Because the client has structured the providerMetadata to mimic a legitimate file upload, the adapter translates this into a model-request container with an UploadedFile object targeting the restricted S3 URI. The backend agent executes the model request; Bedrock accesses the cloud object using the server's IAM credentials, processes the PDF, and the model streams the summary containing sensitive data back to the attacker.
The security impact of CVE-2026-54249 is classified as a High-severity confidentiality compromise, receiving a CVSS v3.1 base score of 6.8. The attack complexity is rated as High because the attacker must successfully identify or guess valid target cloud-storage URIs or file IDs to execute the payload. The vulnerability requires no client interaction and can be executed over the network by an unauthenticated attacker, yielding full read access to any storage resource accessible by the server's execution context.
The scope is changed (S:C) because the exploit enables an attacker to cross security boundaries—moving from the public application interface into internal storage structures (e.g., private Amazon S3 buckets or Google Cloud Storage) that are isolated from the web. The integrity and availability of the system remain unaffected, as the vulnerability does not provide direct file write or deletion permissions.
To fully address this vulnerability, security teams must apply immediate remediation controls across application code, infrastructure privileges, and runtime configurations.
Upgrading the Pydantic AI library is the primary fix. All systems using pydantic-ai or pydantic-ai-slim must be updated to version 1.106.0 or 2.0.0b6 or higher. These patched versions automatically sanitize client-provided data and exclude unsafe UploadedFile objects by default.
If upgrading is delayed, developers must ensure that UI adapters are configured in a highly restrictive environment. Avoid setting preserve_file_data = True unless the frontend has implemented rigorous file ID and user authorization mapping. Furthermore, execute the Principle of Least Privilege for all server roles. Restrict AWS IAM and Google Cloud service account roles associated with the application servers, ensuring they only have read privileges to bucket directories strictly required for public uploads, and explicitly denying access to sensitive or administrative Buckets.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
pydantic-ai Pydantic | >= 1.65.0, < 1.106.0 | 1.106.0 |
pydantic-ai-slim Pydantic | >= 1.65.0, < 1.106.0 | 1.106.0 |
pydantic-ai Pydantic | >= 2.0.0b1, < 2.0.0b6 | 2.0.0b6 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-918 |
| Attack Vector | Network |
| CVSS v3.1 Score | 6.8 |
| EPSS Score | 0.00197 |
| Impact | Partial Confidentiality Loss (High) |
| Exploit Status | none |
| KEV Status | Not Listed |
The web application receives a URL or file identifier from an upstream request and processes it using server privileges without sufficient validation.
An elevation of privilege vulnerability exists in .NET Aspire versions prior to 13.6.5 due to the insertion of sensitive information into sent data (CWE-201). When the Developer Control Plane (DCP) container tunneling feature is enabled, the orchestration engine implicitly exposes all endpoints of the Aspire Dashboard—including its administrative frontend and management APIs—to containerized workloads instead of restricting tunnel access solely to OpenTelemetry Protocol (OTLP) collection endpoints. This exposure allows an attacker operating within a compromised or untrusted container to query the dashboard frontend, extract sensitive environment variables and connection strings, and elevate privileges across the host orchestration boundary.
CVE-2026-118750 is an uncontrolled resource allocation vulnerability (CWE-770) affecting the Microsoft.AspNetCore.WebUtilities component in ASP.NET Core. Specifically, the FileBufferingReadStream class eagerly allocates large memory buffers from the shared ArrayPool<byte> inside its constructor prior to reading any incoming request payload data. An unauthenticated remote attacker can exploit this behavior by opening numerous concurrent connections and stalling or omitting request body transmission, exhausting shared memory pools and causing a server-wide Denial of Service (DoS).
CVE-2026-114155 is a local elevation of privilege vulnerability affecting Microsoft .NET on Linux and macOS platforms. The vulnerability exists within the dotnet-watch (Hot Reload) component and is caused by a Time-of-Check Time-of-Use (TOCTOU) race condition (CWE-367) during the generation and execution of temporary XML solution files (.slnx) in shared system temporary directories. A local attacker with low privileges can intercept and modify the temporary solution file before MSBuild evaluates it, achieving arbitrary code execution in the security context of the user running dotnet-watch.
CVE-2026-118748 is a remote Denial of Service (DoS) vulnerability in Microsoft ASP.NET Core (.NET 11.0 preview releases prior to 11.0.0) and Microsoft Visual Studio 2026 (prior to 18.12.0). The flaw originates from unbounded resource allocation (CWE-770) within the Blazor static server-side rendering (static-SSR) validation pipeline, where dynamic or attacker-supplied form field identifiers are cached in a singleton-scoped memory structure without size limits or eviction policies. Unauthenticated remote attackers can exploit this condition over the network by sending repeated HTTP requests with unique field names, exhausting heap memory and terminating the host process via an unhandled OutOfMemoryException.
CVE-2026-114150 is a network-accessible information disclosure vulnerability affecting Microsoft ASP.NET Core in .NET 11.0 preview releases and Microsoft Visual Studio 2026 prior to version 18.12.0. The flaw stems from the use of uninitialized resources (CWE-908) within response buffering components, specifically ResponseBufferingStream used by middleware such as HTTP Logging and Output Caching. Because rented memory segments from ArrayPool<byte> or MemoryPool<byte> are not cleared upon return or strictly bounded to the exact written byte count during subsequent reads, residual response payloads from earlier HTTP requests remain in the buffer tail. Downstream consumers or log sinks reading these buffers expose confidential cross-session data, including authentication tokens and personally identifiable information.
An incorrect authorization vulnerability (CWE-863) exists in the ASP.NET Core SignalR HTTP Connection Dispatcher (`Microsoft.AspNetCore.Http.Connections`). The connection negotiation process failed to bind the authenticated caller identity to the generated connection state, and subsequent transport or message dispatch handlers failed to verify caller consistency. Consequently, an authenticated low-privilege attacker who obtains a valid connection identifier or token can inject messages into or hijack another user's SignalR session, achieving an elevation of privilege across security boundaries.