Every CVE Search/ Get API key

CVE-2026-47391

PraisonAI's unauthenticated A2A official example can reach real LLM-driven `eval()` tool execution

9.8 CriticalFix available

PraisonAI is a multi-agent teams system. Prior to version 4.6.40, PraisonAI's first-party A2A server example exposes an unauthenticated A2A JSON-RPC endpoint and registers a calculate(expression) tool implemented with Python eval(). The example also binds to 0.0.0.0. A remote unauthenticated attacker can send message/send to /a2a; the request reaches agent.chat(), and a real LLM can invoke the registered calculate tool. In testing with gemini/gemini-2.5-flash-lite, this resulted in arbitrary Python execution in the server process, confirmed by creation of a marker file from an unauthenticated HTTP request. The issue affects deployments following the official A2A example or similar unauthenticated public A2A deployments with unsafe tools. The default unauthenticated A2A surface also exposes task history and task cancellation APIs, increasing confidentiality and integrity impact. Version 4.6.40 patches the issue.

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
High
Integrity
High
Availability
High

Exploit likelihood

1.50%Up 1.4 points since Jun 12, 2026
90%50%10%1%

What each source reports

CVE List Score used 9.8

PraisonAI's unauthenticated A2A official example can reach real LLM-driven `eval()` tool execution

PraisonAI is a multi-agent teams system. Prior to version 4.6.40, PraisonAI's first-party A2A server example exposes an unauthenticated A2A JSON-RPC endpoint and registers a calculate(expression) tool implemented with Python eval(). The example also binds to 0.0.0.0. A remote unauthenticated attacker can send message/send to /a2a; the request reaches agent.chat(), and a real LLM can invoke the registered calculate tool. In testing with gemini/gemini-2.5-flash-lite, this resulted in arbitrary Python execution in the server process, confirmed by creation of a marker file from an unauthenticated HTTP request. The issue affects deployments following the official A2A example or similar unauthenticated public A2A deployments with unsafe tools. The default unauthenticated A2A surface also exposes task history and task cancellation APIs, increasing confidentiality and integrity impact. Version 4.6.40 patches the issue.

Record
CVE-2026-47391
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-95, CWE-306
Updated
2026-11-02
EUVDEUVD-2026-46281 9.8

PraisonAI is a multi-agent teams system. Prior to version 4.6.40, PraisonAI's first-party A2A server example exposes an unauthenticated A2A JSON-RPC endpoint and registers a calculate(expression) tool implemented with Python eval(). The example also binds to 0.0.0.0. A remote unauthenticated attacker can send message/send to /a2a; the request reaches agent.chat(), and a real LLM can invoke the registered calculate tool. In testing with gemini/gemini-2.5-flash-lite, this resulted in arbitrary Python execution in the server process, confirmed by creation of a marker file from an unauthenticated HTTP request. The issue affects deployments following the official A2A example or similar unauthenticated public A2A deployments with unsafe tools. The default unauthenticated A2A surface also exposes task history and task cancellation APIs, increasing confidentiality and integrity impact. Version 4.6.40 patches the issue.

Record
EUVD-2026-46281
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Updated
2026-11-02
GitHub AdvisoriesGHSA-vg22-4gmj-prxw 9.8

PraisonAI's unauthenticated A2A official example can reach real LLM-driven `eval()` tool execution

Summary

The first-party PraisonAI A2A server example combines three behaviors into a remotely exploitable Critical chain:

  1. The example exposes an A2A server without configuring auth_token.
  2. The same example binds the server to 0.0.0.0.
  3. The example registers a calculate(expression) tool implemented with Python eval(expression).

An unauthenticated network client can send a JSON-RPC message/send request to /a2a. The A2A handler passes the attacker-controlled message to agent.chat(). With a real Gemini LLM (gemini/gemini-2.5-flash-lite), the model invoked the registered calculate tool, causing the example's eval() call to execute Python in the server process. The canary wrote a marker file from an unauthenticated /a2a request.

This is not a claim that every A2A deployment is automatically RCE. The Critical chain is confirmed for the first-party A2A example, and for deployments that follow the same pattern: public unauthenticated A2A plus an unsafe tool such as this eval()-based calculate tool. The default unauthenticated A2A surface is the remote entry point; the official example's eval() tool provides the code execution sink.

Earlier note:

The unsafe official example existed earlier, but the complete unauthenticated /a2a message/send to agent.chat() exploit chain is only claimed here for versions where that endpoint is present and confirmed.

Trust Boundary

The boundary that should be preserved is:

Unauthenticated network clients must not be able to drive server-side agent tools that can execute code or mutate server state.

The affected example breaks that boundary. A remote unauthenticated A2A client can supply a prompt that reaches the server's LLM-backed agent. The LLM can then invoke a registered local tool. In the official example, that registered local tool directly evaluates attacker-influenced input with eval().

Vulnerable Code

Official example:

inbox/PraisonAI/examples/python/a2a/a2a-server.py

Relevant lines:

23 def calculate(expression: str) -> str:
24     """Calculate a mathematical expression."""
25     try:
26         return f"Result: {eval(expression)}"
27     except Exception:
28         return "Invalid expression"

30 agent = Agent(
31     name="Research Assistant",
32     role="Research Analyst",
33     goal="Help users research topics and answer questions",
34     tools=[search_web, calculate]
35 )

38 a2a = A2A(
39     agent=agent,
40     url="http://localhost:8000/a2a",
41     version="1.0.0"
42 )

51 if __name__ == "__main__":
52     import uvicorn
53     uvicorn.run(app, host="0.0.0.0", port=8000)

A2A defaults and authentication behavior:

inbox/PraisonAI/src/praisonai-agents/praisonaiagents/ui/a2a/a2a.py

Relevant lines:

125 def serve(self, host: str = "0.0.0.0", port: int = 8000):
...
142     uvicorn.run(app, host=host, port=port)

162 # Auth dependency — only applied to POST /a2a, not discovery endpoints
163 async def _verify_auth(authorization: Optional[str] = Header(None)):
164     """Verify bearer token if auth_token is configured."""
165     if self.auth_token is None:
166         return  # No auth configured — open access

192 from fastapi import Depends
193 _a2a_deps = [Depends(_verify_auth)] if self.auth_token else []
194 @router.post("/a2a", dependencies=_a2a_deps)
195 async def handle_jsonrpc(request: Request):

message/send reaches the agent:

309 try:
310     # Extract user input text
311     user_input = extract_user_input([message])
312
313     # Run agent or agents (offload sync call to thread pool)
314     if self.agent:
315         response = await asyncio.to_thread(self.agent.chat, user_input)

Attack Model

The attacker is an unauthenticated remote client that can reach the A2A HTTP service. This is realistic because the official example binds to 0.0.0.0, does not configure auth_token, and exposes /a2a.

The attacker does not…

Record
GHSA-vg22-4gmj-prxw
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-95, CWE-306
Updated
2026-11-02
NVD 9.8

PraisonAI is a multi-agent teams system. Prior to version 4.6.40, PraisonAI's first-party A2A server example exposes an unauthenticated A2A JSON-RPC endpoint and registers a calculate(expression) tool implemented with Python eval(). The example also binds to 0.0.0.0. A remote unauthenticated attacker can send message/send to /a2a; the request reaches agent.chat(), and a real LLM can invoke the registered calculate tool. In testing with gemini/gemini-2.5-flash-lite, this resulted in arbitrary Python execution in the server process, confirmed by creation of a marker file from an unauthenticated HTTP request. The issue affects deployments following the official A2A example or similar unauthenticated public A2A deployments with unsafe tools. The default unauthenticated A2A surface also exposes task history and task cancellation APIs, increasing confidentiality and integrity impact. Version 4.6.40 patches the issue.

Record
CVE-2026-47391
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-95, CWE-306
Updated
2026-11-02
OSV 9.8

PraisonAI's unauthenticated A2A official example can reach real LLM-driven `eval()` tool execution

PraisonAI is a multi-agent teams system. Prior to version 4.6.40, PraisonAI's first-party A2A server example exposes an unauthenticated A2A JSON-RPC endpoint and registers a calculate(expression) tool implemented with Python eval(). The example also binds to 0.0.0.0. A remote unauthenticated attacker can send message/send to /a2a; the request reaches agent.chat(), and a real LLM can invoke the registered calculate tool. In testing with gemini/gemini-2.5-flash-lite, this resulted in arbitrary Python execution in the server process, confirmed by creation of a marker file from an unauthenticated HTTP request. The issue affects deployments following the official A2A example or similar unauthenticated public A2A deployments with unsafe tools. The default unauthenticated A2A surface also exposes task history and task cancellation APIs, increasing confidentiality and integrity impact. Version 4.6.40 patches the issue.

Record
CVE-2026-47391
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-306, CWE-95
Updated
2026-11-02
OSVGHSA-vg22-4gmj-prxw 9.8

PraisonAI's unauthenticated A2A official example can reach real LLM-driven `eval()` tool execution

Summary

The first-party PraisonAI A2A server example combines three behaviors into a remotely exploitable Critical chain:

  1. The example exposes an A2A server without configuring auth_token.
  2. The same example binds the server to 0.0.0.0.
  3. The example registers a calculate(expression) tool implemented with Python eval(expression).

An unauthenticated network client can send a JSON-RPC message/send request to /a2a. The A2A handler passes the attacker-controlled message to agent.chat(). With a real Gemini LLM (gemini/gemini-2.5-flash-lite), the model invoked the registered calculate tool, causing the example's eval() call to execute Python in the server process. The canary wrote a marker file from an unauthenticated /a2a request.

This is not a claim that every A2A deployment is automatically RCE. The Critical chain is confirmed for the first-party A2A example, and for deployments that follow the same pattern: public unauthenticated A2A plus an unsafe tool such as this eval()-based calculate tool. The default unauthenticated A2A surface is the remote entry point; the official example's eval() tool provides the code execution sink.

Earlier note:

The unsafe official example existed earlier, but the complete unauthenticated /a2a message/send to agent.chat() exploit chain is only claimed here for versions where that endpoint is present and confirmed.

Trust Boundary

The boundary that should be preserved is:

Unauthenticated network clients must not be able to drive server-side agent tools that can execute code or mutate server state.

The affected example breaks that boundary. A remote unauthenticated A2A client can supply a prompt that reaches the server's LLM-backed agent. The LLM can then invoke a registered local tool. In the official example, that registered local tool directly evaluates attacker-influenced input with eval().

Vulnerable Code

Official example:

inbox/PraisonAI/examples/python/a2a/a2a-server.py

Relevant lines:

23 def calculate(expression: str) -> str:
24     """Calculate a mathematical expression."""
25     try:
26         return f"Result: {eval(expression)}"
27     except Exception:
28         return "Invalid expression"

30 agent = Agent(
31     name="Research Assistant",
32     role="Research Analyst",
33     goal="Help users research topics and answer questions",
34     tools=[search_web, calculate]
35 )

38 a2a = A2A(
39     agent=agent,
40     url="http://localhost:8000/a2a",
41     version="1.0.0"
42 )

51 if __name__ == "__main__":
52     import uvicorn
53     uvicorn.run(app, host="0.0.0.0", port=8000)

A2A defaults and authentication behavior:

inbox/PraisonAI/src/praisonai-agents/praisonaiagents/ui/a2a/a2a.py

Relevant lines:

125 def serve(self, host: str = "0.0.0.0", port: int = 8000):
...
142     uvicorn.run(app, host=host, port=port)

162 # Auth dependency — only applied to POST /a2a, not discovery endpoints
163 async def _verify_auth(authorization: Optional[str] = Header(None)):
164     """Verify bearer token if auth_token is configured."""
165     if self.auth_token is None:
166         return  # No auth configured — open access

192 from fastapi import Depends
193 _a2a_deps = [Depends(_verify_auth)] if self.auth_token else []
194 @router.post("/a2a", dependencies=_a2a_deps)
195 async def handle_jsonrpc(request: Request):

message/send reaches the agent:

309 try:
310     # Extract user input text
311     user_input = extract_user_input([message])
312
313     # Run agent or agents (offload sync call to thread pool)
314     if self.agent:
315         response = await asyncio.to_thread(self.agent.chat, user_input)

Attack Model

The attacker is an unauthenticated remote client that can reach the A2A HTTP service. This is realistic because the official example binds to 0.0.0.0, does not configure auth_token, and exposes /a2a.

The attacker does not…

Record
GHSA-vg22-4gmj-prxw
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weakness
CWE-306, CWE-95
Updated
2026-11-02
OSVPYSEC-2026-478 9.8

PraisonAI's unauthenticated A2A official example can reach real LLM-driven `eval()` tool execution

Summary

The first-party PraisonAI A2A server example combines three behaviors into a remotely exploitable Critical chain:

  1. The example exposes an A2A server without configuring auth_token.
  2. The same example binds the server to 0.0.0.0.
  3. The example registers a calculate(expression) tool implemented with Python eval(expression).

An unauthenticated network client can send a JSON-RPC message/send request to /a2a. The A2A handler passes the attacker-controlled message to agent.chat(). With a real Gemini LLM (gemini/gemini-2.5-flash-lite), the model invoked the registered calculate tool, causing the example's eval() call to execute Python in the server process. The canary wrote a marker file from an unauthenticated /a2a request.

This is not a claim that every A2A deployment is automatically RCE. The Critical chain is confirmed for the first-party A2A example, and for deployments that follow the same pattern: public unauthenticated A2A plus an unsafe tool such as this eval()-based calculate tool. The default unauthenticated A2A surface is the remote entry point; the official example's eval() tool provides the code execution sink.

Earlier note:

The unsafe official example existed earlier, but the complete unauthenticated /a2a message/send to agent.chat() exploit chain is only claimed here for versions where that endpoint is present and confirmed.

Trust Boundary

The boundary that should be preserved is:

Unauthenticated network clients must not be able to drive server-side agent tools that can execute code or mutate server state.

The affected example breaks that boundary. A remote unauthenticated A2A client can supply a prompt that reaches the server's LLM-backed agent. The LLM can then invoke a registered local tool. In the official example, that registered local tool directly evaluates attacker-influenced input with eval().

Vulnerable Code

Official example:

inbox/PraisonAI/examples/python/a2a/a2a-server.py

Relevant lines:

23 def calculate(expression: str) -> str:
24     """Calculate a mathematical expression."""
25     try:
26         return f"Result: {eval(expression)}"
27     except Exception:
28         return "Invalid expression"

30 agent = Agent(
31     name="Research Assistant",
32     role="Research Analyst",
33     goal="Help users research topics and answer questions",
34     tools=[search_web, calculate]
35 )

38 a2a = A2A(
39     agent=agent,
40     url="http://localhost:8000/a2a",
41     version="1.0.0"
42 )

51 if __name__ == "__main__":
52     import uvicorn
53     uvicorn.run(app, host="0.0.0.0", port=8000)

A2A defaults and authentication behavior:

inbox/PraisonAI/src/praisonai-agents/praisonaiagents/ui/a2a/a2a.py

Relevant lines:

125 def serve(self, host: str = "0.0.0.0", port: int = 8000):
...
142     uvicorn.run(app, host=host, port=port)

162 # Auth dependency — only applied to POST /a2a, not discovery endpoints
163 async def _verify_auth(authorization: Optional[str] = Header(None)):
164     """Verify bearer token if auth_token is configured."""
165     if self.auth_token is None:
166         return  # No auth configured — open access

192 from fastapi import Depends
193 _a2a_deps = [Depends(_verify_auth)] if self.auth_token else []
194 @router.post("/a2a", dependencies=_a2a_deps)
195 async def handle_jsonrpc(request: Request):

message/send reaches the agent:

309 try:
310     # Extract user input text
311     user_input = extract_user_input([message])
312
313     # Run agent or agents (offload sync call to thread pool)
314     if self.agent:
315         response = await asyncio.to_thread(self.agent.chat, user_input)

Attack Model

The attacker is an unauthenticated remote client that can reach the A2A HTTP service. This is realistic because the official example binds to 0.0.0.0, does not configure auth_token, and exposes /a2a.

The attacker does…

Record
PYSEC-2026-478
CVSS 3.1
9.8 CriticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
praisonai
PyPI
through 4.6.39
4.6.40
praisonai
PyPI
before 4.6.40, plus listed versions
0.0.10.0.100.0.110.0.120.0.13
Show 45 moreShow less
0.0.140.0.150.0.160.0.170.0.180.0.190.0.20.0.200.0.210.0.220.0.230.0.240.0.250.0.260.0.270.0.280.0.290.0.30.0.300.0.310.0.320.0.330.0.340.0.350.0.360.0.370.0.380.0.390.0.40.0.400.0.410.0.420.0.430.0.440.0.450.0.460.0.470.0.480.0.490.0.50.0.500.0.520.0.530.0.540.0.55
4.6.40

History

History
RecordedChangedSources
aliasesEUVD
first indexedCVE List, GitHub Advisories, NVD, OSV

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

In short

CVE-2026-47391 is a critical vulnerability (CVSS 9.8) in Mervinpraison Praisonai, published May 29, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 1.50%, the 74th percentile. Fixed in praisonai 4.6.40.

References

This page doesn't list everything

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

MoveEnter Open