Skip to content

GHSA-vmf9-xx9w-86wx CVE-2026-57112 PYSEC-2026-3521 PYSEC-2026-3534

PraisonAI ToolsMCPServer legacy SSE transport accepts attacker Host/Origin and exposes registered tools

High PyPI
Severity
8.3 / 10
High · CVSS 3.1
Exploit chance (EPSS)
0.22%
Chance of exploitation in the next 30 days. Higher than 13.09% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-306 Missing Authentication for Critical Function
CWE-346 Origin Validation Error
CWE-862 Missing Authorization
Fix
Available
2 packages have a fixed version

Description

PraisonAI ToolsMCPServer legacy SSE transport accepts attacker Host/Origin and exposes registered tools

Summary

praisonaiagents.mcp.ToolsMCPServer.run_sse() builds a Starlette MCP HTTP+SSE server around mcp.server.sse.SseServerTransport. The server exposes /sse and /messages/, but it does not validate Origin, does not validate Host, and does not require any authentication.

This is reachable through supported PraisonAI code paths that wrap configured MCP server tools and re-expose them over legacy SSE:

  • praisonai mcp run <name> --transport sse
  • praisonai serve mcp --name <name> --transport sse
  • direct use of ToolsMCPServer(...).run_sse(...) or launch_tools_mcp_server(..., transport="sse")

A malicious website can use DNS rebinding against a local or internal PraisonAI SSE MCP server and send requests with attacker-controlled Host and Origin headers. The local PoV binds only to 127.0.0.1, sends an attacker Host and Origin, lists the registered tool, and invokes it successfully.

The same attacker Origin is rejected by PraisonAI's current Streamable HTTP transport with HTTP 403. The vulnerability is therefore a sibling transport guard gap in the legacy SSE wrapper, not intended behavior.

Affected product

  • Repository: MervinPraison/PraisonAI
  • Packages:
    • praisonaiagents
    • praisonai
  • Primary component: src/praisonai-agents/praisonaiagents/mcp/mcp_server.py
  • CLI wrappers:
    • src/praisonai/praisonai/cli/commands/mcp.py
    • src/praisonai/praisonai/cli/commands/serve.py
  • Latest verified release/current head:
    • praisonaiagents 1.6.58
    • PraisonAI 4.6.58
    • repo head 1ad58ca02975ff1398efeda694ea2ab78f20cf3e

Suggested affected ranges:

  • praisonaiagents >= 0.6.0, <= 1.6.58
  • praisonai >= 3.10.0, <= 4.6.58

No fixed version is known at submission time.

Confirmed source sweep:

v3.0.0   ToolsMCPServer.run_sse helper present, no Origin/Host/auth checks
v3.10.0  praisonai mcp run --transport sse wraps configured tools into helper
v3.12.3  praisonai serve mcp --name --transport sse wraps configured tools
v4.0.0   same vulnerable helper and CLI wrapping paths
v4.4.12  same vulnerable helper and CLI wrapping paths
v4.5.0   same vulnerable helper and CLI wrapping paths
v4.5.56  same vulnerable helper and CLI wrapping paths
v4.5.139 same vulnerable helper and CLI wrapping paths
v4.6.57  same vulnerable helper and CLI wrapping paths
v4.6.58  same vulnerable helper and dynamic PoV succeeds

Impact

If a PraisonAI user starts a local or internal legacy SSE MCP server with registered tools, an attacker who gets that user to visit a malicious website can use DNS rebinding to interact with the SSE server through the browser. The attacker can discover exposed tools and invoke them as the local user.

Impact depends on the configured tools. In realistic PraisonAI MCP deployments, registered tools may access local files, repositories, issue trackers, cloud APIs, internal services, or other automation targets. This can lead to confidentiality, integrity, and availability impact for the resources reachable by the exposed tools.

The PoV is local-only and harmless. It exposes one marker tool that writes a canary string to a temporary directory.

Root cause

Current ToolsMCPServer.run_sse() constructs a Starlette app directly:

sse_path = "/sse"
messages_path = "/messages/"
sse_transport = SseServerTransport(messages_path)

async def handle_sse(request: Request):
    async with sse_transport.connect_sse(
        request.scope, request.receive, request._send
    ) as (read_stream, write_stream):
        await mcp._mcp_server.run(
            read_stream,
            write_stream,
            mcp._mcp_server.create_initialization_options()
        )

app = Starlette(
    debug=self._debug,
    routes=[
        Route(sse_path, endpoint=handle_sse),
        Mount(messages_path, app=sse_transport.handle_post_message),
    ]
)

uvicorn.run(app, host=host, port=port)

There is no middleware or route-level check for:

  • Origin
  • Host
  • Authorization
  • API key
  • allowed origins / allowed hosts

The configured CLI wrapper exposes this path:

from praisonaiagents.mcp import MCP, ToolsMCPServer
cmd_string = " ".join(cmd)
mcp = MCP(cmd_string, timeout=60, env=server.env or {})
tools = mcp.get_tools()
mcp_server = ToolsMCPServer(name=name, tools=tools)
mcp_server.run_sse(host=host, port=port)

By contrast, the current Streamable HTTP transport validates Origin and returns HTTP 403 for an invalid origin:

origin = request.headers.get("Origin")
if not self._validate_origin(origin):
    return JSONResponse(..., status_code=403)

Local-only PoV

Run from the harness checkout:

uv run --with mcp --with starlette --with uvicorn --with httpx --with anyio \
  python submission-bundle/praisonai-prai-cand-015-mcp-sse-host-origin-bypass/poc/pov_prai_cand_015_sse_mcp_host_origin_bypass.py \
  --repo-src artifacts/repos/praisonai-v4.6.58/src

Observed current-head result:

{
  "candidate": "PRAI-CAND-015",
  "http_stream_control": {
    "attacker_origin": "http://attacker.example.test",
    "rejects_attacker_origin": true,
    "status_code": 403,
    "transport": "current_http_stream"
  },
  "source_checks": {
    "has_auth_check": false,
    "has_host_check": false,
    "has_origin_check": false,
    "has_sse_transport": true,
    "route_count": 2
  },
  "sse_probe": {
    "attacker_headers": {
      "Host": "attacker.example.test:62380",
      "Origin": "http://attacker.example.test:62380"
    },
    "bind_host": "127.0.0.1",
    "marker_value": "executed-from-attacker-origin",
    "marker_written": true,
    "server_started": true,
    "tool_call_content": [
      "recorded:executed-from-attacker-origin"
    ],
    "tool_call_error": false,
    "tool_names": [
      "record_marker"
    ],
    "vulnerable": true
  },
  "vulnerable": true
}

The PoV:

  1. imports the current ToolsMCPServer;
  2. registers one marker tool;
  3. monkey-patches uvicorn.run only to capture the exact Starlette app created by run_sse();
  4. starts that app on 127.0.0.1;
  5. connects to /sse with attacker-controlled Host and Origin;
  6. lists tools and calls the marker tool;
  7. runs a control against PraisonAI's current Streamable HTTP transport and confirms the same attacker Origin is rejected with HTTP 403.

Why this is not intended behavior

This is not only a trust-model disagreement.

PraisonAI's MCP documentation describes Streamable HTTP, WebSocket, and legacy SSE as supported MCP transport mechanisms. The same documentation says the MCP module's security properties include origin validation, authentication headers, and secure session IDs. The transport guide also has a dedicated security section for origin validation as DNS rebinding prevention and authentication.

The official MCP specification warns that HTTP transports need origin validation to prevent DNS rebinding, should bind locally for local servers, and should implement authentication. It also says that without those protections, remote websites can interact with local MCP servers.

The upstream MCP Python SDK advisory GHSA-9h52-p55h-vw2f / CVE-2025-66416 classifies unauthenticated localhost HTTP/SSE MCP servers without DNS rebinding protection as a High severity issue because malicious websites can invoke tools or access resources exposed by the local MCP server. That advisory also says custom low-level SseServerTransport configurations should explicitly configure transport security settings when running unauthenticated localhost servers.

PraisonAI's current Streamable HTTP implementation already enforces an Origin guard and rejects the exact attacker Origin used in the PoV. The issue is that the legacy SSE sibling path lacks the same boundary.

Suggested severity

Suggested severity: High.

Rationale:

  • AV: the attack uses browser-origin HTTP requests to a local/internal service.
  • AC: practical exploitation requires DNS rebinding or equivalent browser origin setup.
  • PR: no PraisonAI credentials are required by the SSE server.
  • UR: the user must visit an attacker-controlled page.
  • S: the vulnerable transport exposes tools that operate on resources outside the HTTP transport itself.
  • C/I/A: exposed tools may read, mutate, or disrupt local/internal resources depending on the configured MCP server.

Suggested fix

Bring legacy SSE server security in line with the current Streamable HTTP transport, or disable the legacy SSE server path.

Recommended changes:

  1. Add explicit allowed-origin and allowed-host validation to both /sse and /messages/.
  2. Reject invalid Origin with HTTP 403 before opening the SSE stream or accepting POST messages.
  3. Validate Host for local and internal deployments to mitigate DNS rebinding even when browsers omit or vary Origin.
  4. Require authentication for all non-stdio MCP HTTP transports, including SSE.
  5. Add --api-key, --allowed-origins, and --allowed-hosts options to praisonai mcp run and praisonai serve mcp when --transport sse is used.
  6. Where the installed MCP SDK supports it, configure the SDK transport-security settings for low-level SseServerTransport usage instead of mounting it without Host/Origin protection.
  7. Consider deprecating or disabling --transport sse server mode in favor of the current Streamable HTTP implementation.
  8. Add regression tests proving that attacker Host and Origin values are rejected on both /sse and /messages/, and that current Streamable HTTP and legacy SSE enforce the same boundary.

Affected versions

PyPI

PackageVulnerableFixed in
praisonai
>= 3.10.0, < 4.6.59
4.6.59
praisonaiagents
>= 0.6.0, < 1.6.59
1.6.59

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.

praisonai PyPI → 4.6.59

pip
pip install --upgrade "praisonai>=4.6.59"
poetry
poetry add "praisonai>=4.6.59"
uv
uv add "praisonai>=4.6.59"

praisonaiagents PyPI → 1.6.59

pip
pip install --upgrade "praisonaiagents>=1.6.59"
poetry
poetry add "praisonaiagents>=1.6.59"
uv
uv add "praisonaiagents>=1.6.59"

Severity breakdown

CVSS 3.1 8.3

CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H

Attack vector
Network
Attack complexity
High
Privileges required
None
User interaction
Required
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

Weakness

CWE-306: Missing Authentication for Critical Function

The product does not perform any authentication for functionality that requires a provable user identity or consumes a significant amount of resources.

How to prevent it
  • Architecture and Design: Divide the software into anonymous, normal, privileged, and administrative areas. Identify which of these areas require a proven user identity, and use a centralized authentication capability. Identify all potential communication channels, or other means of interaction with the software, to ensure that all channels are appropriately protected, including those channels that are assumed to be accessible only by authorized parties. Developers sometimes perform authentication at the primary channel, but open up a secondary channel that is assumed to be private. For example, a login mechanism may be listening on one network port, but after successful authentication, it may open up a second port where it waits for the connection, but avoids authentication because it assumes that only the authenticated party will connect to the port. In general, if the software or protocol allows a single session or user state to persist across multiple connections or channels, authentication and appropriate credential management need to be used throughout.
  • Architecture and Design: For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
  • Architecture and Design: Where possible, avoid implementing custom, "grow-your-own" authentication routines and consider using authentication capabilities as provided by the surrounding framework, operating system, or environment. These capabilities may avoid common weaknesses that are unique to authentication; support automatic auditing and tracking; and make it easier to provide a clear separation between authentication tasks and authorization tasks. In environments such as the World Wide Web, the line between authentication and authorization is sometimes blurred. If custom authentication routines are required instead of those provided by the server, then these routines must be applied to every single page, since these pages could be requested directly.
  • Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. For example, consider using libraries with authentication capabilities such as OpenSSL or the ESAPI Authenticator [REF-45].
  • 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 require strong authentication for users who should be allowed to access the data [REF-1297] [REF-1298] [REF-1302].

CWE-346: Origin Validation Error

The product does not properly verify that the source of data or communication is valid.

CWE-862: Missing Authorization

The product does not perform an authorization check when an actor attempts to access a resource or perform an action.

How to prevent it
  • Architecture and Design: Divide the product into anonymous, normal, privileged, and administrative areas. Reduce the attack surface by carefully mapping roles with data and functionality. Use role-based access control (RBAC) [REF-229] to enforce the roles at the appropriate boundaries. Note that this approach may not protect against horizontal authorization, i.e., it will not protect a user from attacking others with the same role.
  • Architecture and Design: Ensure that access control checks are performed related to the business logic. These checks may be different than the access control checks that are applied to more generic resources such as files, connections, processes, memory, and database records. For example, a database may restrict access for medical records to a specific database user, but each record might only be intended to be accessible to the patient and the patient's doctor [REF-7].
  • Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. For example, consider using authorization frameworks such as the JAAS Authorization Framework [REF-233] and the OWASP ESAPI Access Control feature [REF-45].
  • Architecture and Design: For web applications, make sure that the access control mechanism is enforced correctly at the server side on every page. Users should not be able to access any unauthorized functionality or information by simply requesting direct access to that page. One way to do this is to ensure that all pages containing sensitive information are not cached, and that all such pages restrict access to requests that are accompanied by an active and authenticated session token associated with a user who has the required permissions to access that page.
  • System Configuration, Installation: Use the access control capabilities of your operating system and server environment and define your access control lists accordingly. Use a "default deny" policy when defining these ACLs.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-26mh-57q7-jfvr
PraisonAI vulnerable to Remote Code Execution via Broken AST Sandbox
High Also affects praisonai
GHSA-6g6r-q6gw-w8fg
PraisonAI has a Browser Server WebSocket origin validation bypass via unanchored regex (patch bypass of CVE-2026-40289 / GHSA-8x8f-54wf-vv92)
Critical Also affects praisonai
GHSA-pvph-5j39-v8qc
PraisonAI: Origin-validation bypass (startswith prefix match) enables unauthenticated cross-site request forgery against the PraisonAI MCP HTTP server
High Also affects praisonai
GHSA-gfq8-hmph-9gjv
PraisonAI: Authentication fail-open in Recipe server allows unauthenticated access when API key or JWT auth is configured without a secret
High Also affects praisonai
GHSA-2jgc-f764-c5r2
PraisonAI: [Auth Bypass] PraisonAI async Jobs API (`/api/v1/runs`) has no authentication — unauthenticated job execution, result theft, cancel and delete
High Also affects praisonai
GHSA-gxmw-5f7x-6g22
praisonaiagents vulnerable to arbitrary file write via unsanitized `user_id` in `FileMemory.__init__()` — path traversal to any writable location
High Also affects praisonaiagents
GHSA-pvxx-r596-f5qj
PraisonAI: `--api-key` flag on `praisonai serve` is not properly enforced
High Also affects praisonai
GHSA-hmfx-4v44-9qw9
PraisonAI vulnerable to Server-Side Request Forgery via DNS rebinding bypass in webhook_url validation
Medium Also affects praisonai
Is this in your code?

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