Summary
praisonai.bots.DiscordApproval approves a pending dangerous tool call when it sees any later non-bot message in the configured Discord channel whose text is classified as approval, such as yes.
The decision is not bound to:
- a Discord reply to the approval message;
- a Discord thread created for that request;
- a Discord interaction/button callback for that request;
- an explicit approver user allowlist; or
- an approval nonce visible only to intended approvers.
As a result, any user who can post in the configured approval channel can approve a pending high-risk tool call by sending yes after the approval message appears. the same PoV also shows that the Slack and Telegram messaging approval backends have no explicit approver allowlist parameter, but the primary report-grade issue is the Discord backend's unthreaded channel cross-talk: the approving message does not need to be a reply or otherwise request-bound.
Technical Details
DiscordApproval.request_approval() posts an approval message to the configured channel and records the returned message id.
_poll_for_response() then polls channel history with:
f"/channels/{channel_id}/messages?after={message_id}&limit=10"
For each later non-bot message, it reads content, classifies the text, and returns ApprovalDecision(approved=True) when the text is approve/yes. There is no check that the message is a Discord reply to the approval message, belongs to a request-specific thread, came from an intended approver, or contains a request-specific approval token.
Important source evidence from origin/main:
_discord_approval.py lines 57-73: constructor accepts token, channel_id, timeout, and poll_interval; no approver allowlist.
_discord_approval.py line 239: polls all messages after the approval message in the configured channel.
_discord_approval.py lines 252-265: skips bot messages, classifies the remaining text, and approves when the keyword is approve.
The Slack and Telegram backends are better request-scoped than Discord:
- Slack uses
conversations.replies for the approval message thread.
- Telegram checks the callback/reply message id.
However, both still lack an explicit approver identity parameter. They are included in the PoV and suggested fix because the authorization model should be consistent across all messaging approval backends.
Why This Is Not Intended Behavior
This report does not claim that a deliberately private approval channel with only trusted approvers is unsafe by itself. The narrower issue is that the Discord backend treats an unrelated later channel message as the approval decision for a specific dangerous tool request.
PraisonAI's approval documentation describes approval as a safety control that pauses before dangerous tools and asks a human or channel to allow or deny the specific request. A random later yes in the channel is not evidence that an intended approver reviewed that request.
The existing Slack and Telegram implementations already show request-binding patterns that Discord lacks:
- Slack scopes to replies for the approval message timestamp.
- Telegram checks the callback/reply
message_id.
The Discord backend should provide at least the same request binding and should also support explicit approver identity checks for deployments where channel membership is broader than approval authority.
PoV
Run against the latest local checkout:
python3 poc/pov_poc.py \
--repo /path/to/PraisonAI \
--json
The PoV is local-only. It mocks Slack, Telegram, and Discord API helpers in-process and does not contact those services.
For Discord, the mock sequence is:
DiscordApproval posts a critical execute_command approval request to D_APPROVAL_CHANNEL.
- The mocked channel-history endpoint returns a later ordinary non-bot channel message from
D_INTRUDER with content yes.
DiscordApproval returns approved=True and approver="D_INTRUDER".
Observed output from evidence/pov-v4.6.58.json:
{
"approval_from_unconfigured_channel_participant": {
"discord": true,
"slack": true,
"telegram": true
},
"backends": [
{
"backend": "discord",
"configured_channel_id": "D_APPROVAL_CHANNEL",
"decision_approved": true,
"decision_approver": "D_INTRUDER",
"decision_reason": "Approved via Discord by intruder",
"intruder_user": "D_INTRUDER"
}
],
"no_explicit_approver_allowlist_parameters": true,
"vulnerable": true
}
The command in the approval request is a harmless local sentinel: `touch /tmp/poc. The PoV stops at the approval decision; it does not execute the tool.
PoC
The PoV section above contains the local reproduction command, input, and decisive output.
Impact
If an application uses DiscordApproval for dangerous tools such as shell commands, file writes, deletes, deployments, or other privileged operations, a low-privileged Discord user with write access to the configured approval channel can approve pending dangerous tool executions.
This can lead to code execution, file modification, deployment changes, or data access with the privileges of the PraisonAI process, depending on which tools the agent exposes behind approval.
The attacker does not need the model API key, shell access, repository access, or PraisonAI process access. They only need to be able to post an approval-looking message in the configured approval channel after the approval prompt appears.
Severity
Suggested severity: High.
Suggested CVSS v3.1:
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Rationale:
AV:N: the attacker interacts through a networked Discord channel.
AC:L: sending yes after an approval prompt is enough.
PR:L: the attacker needs permission to post in the configured approval channel, but no approver-specific permission.
UI:N: no separate victim interaction is needed after the prompt exists.
S:U: the vulnerable approval backend and approved tool run in the PraisonAI application's security scope.
C:H/I:H/A:H: approved dangerous tools can disclose, modify, or destroy data depending on the configured agent tools.
Suggested CWEs:
CWE-863: Incorrect Authorization
CWE-345: Insufficient Verification of Data Authenticity
Suggested Fix
Recommended fixes:
- For Discord, require approvals to be tied to the request, not merely any later channel message. Use Discord interactions/buttons with opaque server-side request ids, or require a Discord reply whose
message_reference.message_id matches the approval message.
- Add explicit approver identity configuration to all messaging approval backends, for example
approver_user_ids or allowed_approvers.
- Reject approvals from users outside the configured approver set, even if the message appears in the configured channel.
- Include a per-request nonce or opaque approval id in callbacks and verify it server side before returning
ApprovalDecision(approved=True).
- Add regression tests for Discord where:
- an unrelated later
yes in the channel is ignored;
- a reply from a non-approver is ignored;
- a request-bound reply/callback from an allowed approver succeeds.
Affected Package/Versions
- Repository:
MervinPraison/PraisonAI
- Ecosystem:
pip
- Package:
praisonai
- Component: Python messaging approval backends
- Primary affected file:
src/praisonai/praisonai/bots/_discord_approval.py
- Related sibling files:
src/praisonai/praisonai/bots/_slack_approval.py
src/praisonai/praisonai/bots/_telegram_approval.py
- Latest PyPI version validated:
4.6.58
- Current
origin/main validated: 1ad58ca02975ff1398efeda694ea2ab78f20cf3e
- Current
origin/main tag validated: v4.6.58
Suggested affected range:
pip:praisonai >= 4.5.2, <= 4.6.58
Representative local sweep:
4.5.0 Discord approval backend not present
4.5.2 vulnerable
4.5.128 vulnerable
4.6.9 vulnerable
4.6.10 vulnerable
4.6.56 vulnerable
4.6.57 vulnerable
4.6.58 vulnerable
Advisory History
Visible PraisonAI advisories and prior submissions were checked. This is distinct from related approval issues:
GHSA-63v4-w882-g4x2 / poc covers XSS in the Python HTTPApproval web page.
GHSA-h2w2-v7j6-xqm4 / poc covers npm AgentLoop.onToolCall running after tool execution.
GHSA-ffp3-3562-8cv3 covers coarse-grained shell approval cache reuse.
GHSA-4wr3-f4p3-5wjh covers unauthenticated manipulation of approval safety controls.
GHSA-qwgj-rrpj-75xm covers Chainlit hardcoded auto-approval.
GHSA-qvpf-j64c-jmhr / poc covers Slack app_mention authorization bypass for bot invocation, not messaging approval decisions.
No checked advisory covers DiscordApproval accepting unrelated channel messages as request approvals.
References
- PraisonAI approval documentation:
https://docs.praison.ai/docs/features/approval https://github.com/MervinPraison/PraisonAI/security/policy
- Slack
conversations.replies documentation: https://docs.slack.dev/reference/methods/conversations.replies/
- Telegram callback-query reference:
https://core.telegram.org/bots/api#callbackquery
- Discord message/channel API documentation:
https://docs.discord.com/developers/resources/channel#get-channel-messages
- FIRST CVSS v3.1 calculator:
https://www.first.org/cvss/calculator/3.1
Summary
praisonai.bots.DiscordApprovalapproves a pending dangerous tool call when it sees any later non-bot message in the configured Discord channel whose text is classified as approval, such asyes.The decision is not bound to:
As a result, any user who can post in the configured approval channel can approve a pending high-risk tool call by sending
yesafter the approval message appears. the same PoV also shows that the Slack and Telegram messaging approval backends have no explicit approver allowlist parameter, but the primary report-grade issue is the Discord backend's unthreaded channel cross-talk: the approving message does not need to be a reply or otherwise request-bound.Technical Details
DiscordApproval.request_approval()posts an approval message to the configured channel and records the returned message id._poll_for_response()then polls channel history with:f"/channels/{channel_id}/messages?after={message_id}&limit=10"For each later non-bot message, it reads
content, classifies the text, and returnsApprovalDecision(approved=True)when the text isapprove/yes. There is no check that the message is a Discord reply to the approval message, belongs to a request-specific thread, came from an intended approver, or contains a request-specific approval token.Important source evidence from
origin/main:_discord_approval.pylines 57-73: constructor acceptstoken,channel_id,timeout, andpoll_interval; no approver allowlist._discord_approval.pyline 239: polls all messages after the approval message in the configured channel._discord_approval.pylines 252-265: skips bot messages, classifies the remaining text, and approves when the keyword isapprove.The Slack and Telegram backends are better request-scoped than Discord:
conversations.repliesfor the approval message thread.However, both still lack an explicit approver identity parameter. They are included in the PoV and suggested fix because the authorization model should be consistent across all messaging approval backends.
Why This Is Not Intended Behavior
This report does not claim that a deliberately private approval channel with only trusted approvers is unsafe by itself. The narrower issue is that the Discord backend treats an unrelated later channel message as the approval decision for a specific dangerous tool request.
PraisonAI's approval documentation describes approval as a safety control that pauses before dangerous tools and asks a human or channel to allow or deny the specific request. A random later
yesin the channel is not evidence that an intended approver reviewed that request.The existing Slack and Telegram implementations already show request-binding patterns that Discord lacks:
message_id.The Discord backend should provide at least the same request binding and should also support explicit approver identity checks for deployments where channel membership is broader than approval authority.
PoV
Run against the latest local checkout:
The PoV is local-only. It mocks Slack, Telegram, and Discord API helpers in-process and does not contact those services.
For Discord, the mock sequence is:
DiscordApprovalposts a criticalexecute_commandapproval request toD_APPROVAL_CHANNEL.D_INTRUDERwith contentyes.DiscordApprovalreturnsapproved=Trueandapprover="D_INTRUDER".Observed output from
evidence/pov-v4.6.58.json:{ "approval_from_unconfigured_channel_participant": { "discord": true, "slack": true, "telegram": true }, "backends": [ { "backend": "discord", "configured_channel_id": "D_APPROVAL_CHANNEL", "decision_approved": true, "decision_approver": "D_INTRUDER", "decision_reason": "Approved via Discord by intruder", "intruder_user": "D_INTRUDER" } ], "no_explicit_approver_allowlist_parameters": true, "vulnerable": true }The command in the approval request is a harmless local sentinel: `touch /tmp/poc. The PoV stops at the approval decision; it does not execute the tool.
PoC
The PoV section above contains the local reproduction command, input, and decisive output.
Impact
If an application uses
DiscordApprovalfor dangerous tools such as shell commands, file writes, deletes, deployments, or other privileged operations, a low-privileged Discord user with write access to the configured approval channel can approve pending dangerous tool executions.This can lead to code execution, file modification, deployment changes, or data access with the privileges of the PraisonAI process, depending on which tools the agent exposes behind approval.
The attacker does not need the model API key, shell access, repository access, or PraisonAI process access. They only need to be able to post an approval-looking message in the configured approval channel after the approval prompt appears.
Severity
Suggested severity: High.
Suggested CVSS v3.1:
Rationale:
AV:N: the attacker interacts through a networked Discord channel.AC:L: sendingyesafter an approval prompt is enough.PR:L: the attacker needs permission to post in the configured approval channel, but no approver-specific permission.UI:N: no separate victim interaction is needed after the prompt exists.S:U: the vulnerable approval backend and approved tool run in the PraisonAI application's security scope.C:H/I:H/A:H: approved dangerous tools can disclose, modify, or destroy data depending on the configured agent tools.Suggested CWEs:
CWE-863: Incorrect AuthorizationCWE-345: Insufficient Verification of Data AuthenticitySuggested Fix
Recommended fixes:
message_reference.message_idmatches the approval message.approver_user_idsorallowed_approvers.ApprovalDecision(approved=True).yesin the channel is ignored;Affected Package/Versions
MervinPraison/PraisonAIpippraisonaisrc/praisonai/praisonai/bots/_discord_approval.pysrc/praisonai/praisonai/bots/_slack_approval.pysrc/praisonai/praisonai/bots/_telegram_approval.py4.6.58origin/mainvalidated:1ad58ca02975ff1398efeda694ea2ab78f20cf3eorigin/maintag validated:v4.6.58Suggested affected range:
Representative local sweep:
Advisory History
Visible PraisonAI advisories and prior submissions were checked. This is distinct from related approval issues:
GHSA-63v4-w882-g4x2/ poc covers XSS in the PythonHTTPApprovalweb page.GHSA-h2w2-v7j6-xqm4/ poc covers npmAgentLoop.onToolCallrunning after tool execution.GHSA-ffp3-3562-8cv3covers coarse-grained shell approval cache reuse.GHSA-4wr3-f4p3-5wjhcovers unauthenticated manipulation of approval safety controls.GHSA-qwgj-rrpj-75xmcovers Chainlit hardcoded auto-approval.GHSA-qvpf-j64c-jmhr/ poc covers Slackapp_mentionauthorization bypass for bot invocation, not messaging approval decisions.No checked advisory covers DiscordApproval accepting unrelated channel messages as request approvals.
References
https://docs.praison.ai/docs/features/approvalhttps://github.com/MervinPraison/PraisonAI/security/policyconversations.repliesdocumentation:https://docs.slack.dev/reference/methods/conversations.replies/https://core.telegram.org/bots/api#callbackqueryhttps://docs.discord.com/developers/resources/channel#get-channel-messageshttps://www.first.org/cvss/calculator/3.1