PraisonAI: Webhook signature verification skipped (fail-open) when secret unset, allowing forged inbound webhooks (WhatsApp & Linear bots)
The WhatsApp and Linear bot adapters verify the inbound webhook HMAC signature only when a secret is configured. When the secret environment variable is unset — the default on a fresh install and common in development — verification is skipped entirely and the webhook body is parsed and dispatched as a genuine, trusted event. A remote, unauthenticated attacker who can reach the bot's webhook endpoint can inject arbitrary platform events.
Affected code:
WhatsApp - src/praisonai/praisonai/bots/whatsapp.py
if self._app_secret: gates the ENTIRE check; when falsy the body is
json.loads()'d and dispatched to _process_webhook_data() with no verification.Linear - src/praisonai/praisonai/bots/linear.py
if self._signing_secret: fail-open guard.The _verify_signature implementations themselves are correct (constant-time HMAC-SHA256); the defect is that verification is bypassed when the secret is absent.
Impact:
Proof of concept (bot started without the secret - the default):
curl -X POST http://VICTIM:PORT/webhook
-H 'Content-Type: application/json'
-d '{"object":"whatsapp_business_account","entry":[{"changes":[{"value":
{"messages":[{"from":"15551234567","id":"wamid.x","type":"text",
"text":{"body":"attacker-injected message"}}]}}]}]}'
A self-contained PoC that executes the real _handle_webhook / _verify_signature source extracted from the repo confirms: secret unset -> status 200, payload dispatched (VULNERABLE); secret set + no signature -> status 403, nothing dispatched (control).
Remediation: Fail closed. When no secret is configured, reject all webhooks (HTTP 403) and refuse to start the adapter unless a secret is set (or an explicit, clearly-named insecure-dev override is given):
if not self._app_secret: return web.Response(status=403, text="Webhook secret not configured") signature = request.headers.get("X-Hub-Signature-256", "") if not self._verify_signature(body, signature): return web.Response(status=403, text="Invalid signature")
Distinct from prior advisories: The accepted default-insecure advisories cover a different surface/mechanism — CALL_SERVER_TOKEN unset (GHSA-86qc-r5v2-v6x6) and the JWT key default "dev-secret-change-me" (GHSA-3qg8-5g3r-79v5). This is in the bot webhook adapters and the mechanism is skipping signature verification entirely when the secret is absent, not a weak default key.
왜 이 VPI인가 (설명가능 · 실험적)
VPI 산정 기준
| 영향도 | 86.00 |
| 악용 신호(추가 악용신호 없음) | ×1.00 |
| VPI | 86.00 |
VPI 공식 vpi-v1 기준