Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
"aliases": [
"CVE-2026-56835"
],
"summary": "PraisonAI Slack app_mention bypasses configured user/channel authorization",
"details": "# PraisonAI Slack `app_mention` bypasses configured user/channel authorization\n\n## Summary\n\nPraisonAI's Slack bot applies its configured `allowed_users`,\n`allowed_channels`, and unknown-user pairing policy in the normal Slack\n`message` event handler, but not in the adjacent Slack `app_mention` event\nhandler.\n\nA Slack workspace user who can mention the bot in a channel where the Slack app\nis present can trigger the configured PraisonAI agent even when:\n\n- the sender is not in `BotConfig.allowed_users`;\n- the channel is not in `BotConfig.allowed_channels`;\n- `unknown_user_policy=\"deny\"` is configured; and\n- the same event content is correctly dropped by the normal `message` handler.\n\nThis is a sibling-handler guard-coverage issue. Slack documents\n`app_mention` as a distinct event type rather than a `message.*` event, so\ndeployments subscribed to app mentions can route unauthorized sender input\naround the guarded message path.\n\n## Affected product\n\n- Repository: `MervinPraison/PraisonAI`\n- Package: `praisonai`\n- Component: `src/praisonai/praisonai/bots/slack.py`\n- Configuration component: `src/praisonai-agents/praisonaiagents/bots/config.py`\n\nConfirmed affected:\n\n```text\nv3.11.0 7f37d754a72511a71f7eeaaa8e9f367a5dc45fd8\nv3.11.14 44b800df0eddf32dd5242f47da7513e4a3159d76\nv3.12.0 51f95ad904a6616b35caede1cd74026ec8f7152c\nv4.4.5 9a3363c900fa3be3fce5483be7f6c1f418757ebb\nv4.4.6 90b00f9a25ee5c7ccf4b6ab3152700e1881f262d\nv4.4.12 7d0657632fc477673153ad116cecf692b454bfa3\nv4.5.2 8ddbb4ee7152d3fa68fbaaf6e6c610ae03d938d3\nv4.5.16 02a19776517cc76483fd58dcd6a5fdf8c2c45170\nv4.5.28 16f93251766505a79f237a0f07f68a0ecb17e358\nv4.5.112 bfe3d94bad6db92fc2927c2e3c081ae8303e209e\nv4.5.128 b4e3a8a84ade44ac3dd9102b792cdb4311a95937\nv4.6.10 4b1b17b963cbd0625e41394a30168c95b26429b2\nv4.6.33 dfbb8d78ec7e8dc7118bc722ab1b2524bc98ddab\nv4.6.34 e5928449f73f66cc8af1de61621aa974ab255133\nv4.6.56 d3c4a2afadfbf3a3e172e460e607ba4efad263a6\nv4.6.57 e90d92231853161ad931f3498da57651a9f8b528\nv4.6.58 1ad58ca02975ff1398efeda694ea2ab78f20cf3e\n```\n\nUnaffected boundary control:\n\n```text\nv3.10.24 de1734c29a50af18cf8c69e1d1d90e0f8e391aae\n```\n\n`v3.10.24` does not contain `src/praisonai/praisonai/bots/slack.py`.\n\nSuggested affected range: `praisonai >= 3.11.0, <= 4.6.58`.\n\n## Root cause\n\nThe guarded `message` handler converts the Slack event into a `BotMessage`,\nthen applies channel and sender policy before any agent call:\n\n```python\n@self._app.event(\"message\")\nasync def handle_message(event, say):\n if event.get(\"bot_id\"):\n return\n\n bot_message = self._convert_event_to_message(event)\n bot_message._channel_type = \"slack\"\n self.fire_message_received(bot_message)\n\n if not self.config.is_channel_allowed(\n bot_message.channel.channel_id if bot_message.channel else \"\"\n ):\n return\n\n user_id = bot_message.sender.user_id if bot_message.sender else \"\"\n is_explicitly_allowed = (\n bool(self.config.allowed_users) and self.config.is_user_allowed(user_id)\n )\n if not is_explicitly_allowed:\n user_allowed = await UnknownUserHandler.handle(bot_message, self._bot_context)\n if not user_allowed:\n return\n```\n\nOnly after these checks does `handle_message` call the session manager and\nagent.\n\nThe adjacent `app_mention` handler strips the bot mention and directly invokes\nthe agent session. It never calls `is_channel_allowed()`,\n`is_user_allowed()`, or `UnknownUserHandler.handle()`:\n\n```python\n@self._app.event(\"app_mention\")\nasync def handle_mention(event, say):\n if event.get(\"bot_id\"):\n return\n\n text = event.get(\"text\", \"\")\n if self._bot_user:\n text = text.replace(f\"<@{self._bot_user.user_id}>\", \"\").strip()\n\n if self._agent:\n user_id = event.get(\"user\", \"unknown\")\n response = await self._session.chat(\n self._agent, user_id, text,\n chat_id=str(event.get(\"channel\", \"\")),\n thread_id=event.get(\"thread_ts\", \"\") or \"\",\n message_id=event.get(\"ts\", \"\"),\n account=self._config.get(\"account\", \"default\"),\n )\n```\n\nOlder affected releases use `self._agent.chat(text)` instead of\n`self._session.chat(...)`, but have the same policy gap: `message` checks\n`allowed_users` and `allowed_channels`; `app_mention` does not.\n\n## Local-only PoV\n\nRun from the harness checkout:\n\n```fish\nenv PYTHONPATH=\"artifacts/repos/praisonai-v4.6.58/src/praisonai:artifacts/repos/praisonai-v4.6.58/src/praisonai-agents\" \\\n python3 submission-bundle/praisonai-prai-cand-017-slack-app-mention-authz-bypass/poc/pov_prai_cand_017_slack_app_mention_authz_bypass.py \\\n --repo artifacts/repos/praisonai-v4.6.58 \\\n --label v4.6.58\n```\n\nThe PoV mocks Slack Bolt and Slack SDK in-process. It does not connect to\nSlack, bind a network port, or require real tokens.\n\nThe PoV configures:\n\n```python\nBotConfig(\n allowed_users=[\"U_ALLOWED\"],\n allowed_channels=[\"C_ALLOWED\"],\n unknown_user_policy=\"deny\",\n mention_required=True,\n)\n```\n\nIt then invokes the real registered SlackBot handlers with the same event\npayloads.\n\nObserved `v4.6.58` result:\n\n```json\n{\n \"affected\": true,\n \"scenarios\": [\n {\n \"name\": \"blocked_user_blocked_channel\",\n \"message_delta\": 0,\n \"app_mention_delta\": 1\n },\n {\n \"name\": \"blocked_user_allowed_channel\",\n \"message_delta\": 0,\n \"app_mention_delta\": 1\n },\n {\n \"name\": \"allowed_user_blocked_channel\",\n \"message_delta\": 0,\n \"app_mention_delta\": 1\n },\n {\n \"name\": \"allowed_user_allowed_channel_control\",\n \"message_delta\": 1,\n \"app_mention_delta\": 1\n }\n ]\n}\n```\n\nThe first three scenarios are authorization bypasses. The fourth is the\npositive control showing that an allowed user in an allowed channel can invoke\nthe agent through both paths.\n\nStored evidence:\n\n- `evidence/pov-v4.6.58.json`\n- `evidence/version-sweep.tsv`\n\n## Why this is not intended behavior\n\nPraisonAI's bot security documentation describes Slack user/channel allowlists\nand built-in DM filtering as Slack security features. It also describes\n`unknown_user_policy=\"deny\"` and pairing flows as production controls for\nunknown users.\n\nThe code itself confirms the intended boundary: the normal `message` handler\nperforms channel and sender authorization before any agent call. The vulnerable\n`app_mention` path is adjacent to that handler and routes the same sender,\nchannel, text, timestamp, and thread metadata to the agent without those checks.\n\nSlack's own documentation states that `app_mention` is a separate event type,\nnot a `message.*` event. A PraisonAI deployment that subscribes to\n`app_mention` therefore cannot rely on the normal `message` handler to apply\nthe same sender/channel policy.\n\nThe PoV includes a direct control: the same unauthorized payloads are dropped\nby the guarded `message` handler and accepted by `app_mention`.\n\n## Impact\n\nAn attacker needs the ability to cause Slack to deliver an `app_mention` event\nto the PraisonAI Slack app, such as by mentioning the bot in a channel where the\napp is present or by using Slack's invite-by-mention flow where applicable.\n\nWhen exploited, the attacker can submit arbitrary prompt text to the configured\nPraisonAI agent despite Slack bot allowlists or pairing policy. The downstream\nimpact depends on the deployed agent and tools. PraisonAI's default bot config\nauto-approves safe tools and includes web, memory, scheduling, file, planning,\nand skill tools, so unauthorized invocation can affect confidentiality and\nintegrity in real deployments.\n\nThis report does not claim compromise of Slack itself, bypass of Slack request\nsignature verification, or arbitrary code execution by default.\n\n## Suggested fix\n\nUse one shared authorization preflight for every Slack ingress path before\nfiring message hooks or invoking agents.\n\nConcrete patch direction:\n\n1. Convert `app_mention` events through `_convert_event_to_message()` or a\n dedicated equivalent that preserves sender, channel, text, timestamp, and\n thread metadata.\n2. Set `bot_message._channel_type = \"slack\"` for `app_mention`.\n3. Run the same channel check, user allowlist check, and\n `UnknownUserHandler.handle(...)` policy used by `handle_message`.\n4. Only then strip the bot mention and invoke `_session.chat(...)`.\n5. Add regression tests for:\n - blocked user plus blocked channel: `message` and `app_mention` both drop;\n - blocked user in allowed channel: both drop;\n - allowed user in blocked channel: both drop;\n - allowed user in allowed channel: both invoke;\n - `unknown_user_policy=\"pair\"`: `app_mention` starts the same pairing flow\n instead of invoking the agent.",
"summary": "Slack app_mention Bypasses Configured User/Channel Authorization",
"details": "## Summary\n\nPraisonAI's Slack bot applies its configured `allowed_users`, `allowed_channels`, and unknown-user pairing policy in the normal Slack `message` event handler, but not in the adjacent Slack `app_mention` event handler.\n\nA Slack workspace user who can mention the bot in a channel where the Slack app is present can trigger the configured PraisonAI agent even when:\n\n- the sender is not in `BotConfig.allowed_users`;\n- the channel is not in `BotConfig.allowed_channels`;\n- `unknown_user_policy=\"deny\"` is configured; and\n- the same event content is correctly dropped by the normal `message` handler.\n\nThis is a sibling-handler guard-coverage issue. Slack documents `app_mention` as a distinct event type rather than a `message.*` event, so deployments subscribed to app mentions can route unauthorized sender input around the guarded message path.\n\n## Technical Details\n\nThe guarded `message` handler converts the Slack event into a `BotMessage`, then applies channel and sender policy before any agent call:\n\n```python\n@self._app.event(\"message\")\nasync def handle_message(event, say):\n if event.get(\"bot_id\"):\n return\n\n bot_message = self._convert_event_to_message(event)\n bot_message._channel_type = \"slack\"\n self.fire_message_received(bot_message)\n\n if not self.config.is_channel_allowed(\n bot_message.channel.channel_id if bot_message.channel else \"\"\n ):\n return\n\n user_id = bot_message.sender.user_id if bot_message.sender else \"\"\n is_explicitly_allowed = (\n bool(self.config.allowed_users) and self.config.is_user_allowed(user_id)\n )\n if not is_explicitly_allowed:\n user_allowed = await UnknownUserHandler.handle(bot_message, self._bot_context)\n if not user_allowed:\n return\n```\n\nOnly after these checks does `handle_message` call the session manager and agent.\n\nThe adjacent `app_mention` handler strips the bot mention and directly invokes the agent session. It never calls `is_channel_allowed()`, `is_user_allowed()`, or `UnknownUserHandler.handle()`:\n\n```python\n@self._app.event(\"app_mention\")\nasync def handle_mention(event, say):\n if event.get(\"bot_id\"):\n return\n\n text = event.get(\"text\", \"\")\n if self._bot_user:\n text = text.replace(f\"<@{self._bot_user.user_id}>\", \"\").strip()\n\n if self._agent:\n user_id = event.get(\"user\", \"unknown\")\n response = await self._session.chat(\n self._agent, user_id, text,\n chat_id=str(event.get(\"channel\", \"\")),\n thread_id=event.get(\"thread_ts\", \"\") or \"\",\n message_id=event.get(\"ts\", \"\"),\n account=self._config.get(\"account\", \"default\"),\n )\n```\n\nOlder affected releases use `self._agent.chat(text)` instead of `self._session.chat(...)`, but have the same policy gap: `message` checks `allowed_users` and `allowed_channels`; `app_mention` does not.\n\n### Why This Is Not Intended Behavior\n\nPraisonAI's bot security documentation describes Slack user/channel allowlists and built-in DM filtering as Slack security features. It also describes `unknown_user_policy=\"deny\"` and pairing flows as production controls for unknown users.\n\nThe code itself confirms the intended boundary: the normal `message` handler performs channel and sender authorization before any agent call. The vulnerable `app_mention` path is adjacent to that handler and routes the same sender, channel, text, timestamp, and thread metadata to the agent without those checks.\n\nSlack's own documentation states that `app_mention` is a separate event type, not a `message.*` event. A PraisonAI deployment that subscribes to `app_mention` therefore cannot rely on the normal `message` handler to apply the same sender/channel policy.\n\nThe PoV includes a direct control: the same unauthorized payloads are dropped by the guarded `message` handler and accepted by `app_mention`.\n\n## PoV\n\nRun from a local reproduction checkout:\n\n```fish\nenv PYTHONPATH=\"/path/to/PraisonAI/src/praisonai:/path/to/PraisonAI/src/praisonai-agents\" \\\n python3 poc/pov_poc.py \\\n --repo /path/to/PraisonAI \\\n --label v4.6.58\n```\n\nThe PoV mocks Slack Bolt and Slack SDK in-process. It does not connect to Slack, bind a network port, or require real tokens.\n\nThe PoV configures:\n\n```python\nBotConfig(\n allowed_users=[\"U_ALLOWED\"],\n allowed_channels=[\"C_ALLOWED\"],\n unknown_user_policy=\"deny\",\n mention_required=True,\n)\n```\n\nIt then invokes the real registered SlackBot handlers with the same event payloads.\n\nObserved `v4.6.58` result:\n\n```json\n{\n \"affected\": true,\n \"scenarios\": [\n {\n \"name\": \"blocked_user_blocked_channel\",\n \"message_delta\": 0,\n \"app_mention_delta\": 1\n },\n {\n \"name\": \"blocked_user_allowed_channel\",\n \"message_delta\": 0,\n \"app_mention_delta\": 1\n },\n {\n \"name\": \"allowed_user_blocked_channel\",\n \"message_delta\": 0,\n \"app_mention_delta\": 1\n },\n {\n \"name\": \"allowed_user_allowed_channel_control\",\n \"message_delta\": 1,\n \"app_mention_delta\": 1\n }\n ]\n}\n```\n\nThe first three scenarios are authorization bypasses. The fourth is the positive control showing that an allowed user in an allowed channel can invoke the agent through both paths.\n\n## PoC\n\nThe PoV section above contains the local reproduction command, input, and decisive output.\n\n## Impact\n\nAn attacker needs the ability to cause Slack to deliver an `app_mention` event to the PraisonAI Slack app, such as by mentioning the bot in a channel where the app is present or by using Slack's invite-by-mention flow where applicable.\n\nWhen exploited, the attacker can submit arbitrary prompt text to the configured PraisonAI agent despite Slack bot allowlists or pairing policy. The downstream impact depends on the deployed agent and tools. PraisonAI's default bot config auto-approves safe tools and includes web, memory, scheduling, file, planning, and skill tools, so unauthorized invocation can affect confidentiality and integrity in real deployments.\n\nThis report does not claim compromise of Slack itself, bypass of Slack request signature verification, or arbitrary code execution by default.\n\n### Severity\n\nSuggested severity: High.\n\nSuggested CVSS v3.1:\n\n```text\nCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:L\n```\n\nSuggested CWEs:\n\n- `CWE-863`: Incorrect Authorization\n- `CWE-862`: Missing Authorization\n\nThe score uses `PR:L` because the attacker generally needs Slack workspace or channel-level ability to mention the installed app. Confidentiality and integrity are high because the attacker can drive the configured agent and its tools across the Slack trust boundary; availability is low for model/API budget or workflow side effects.\n\n## Suggested Fix\n\nUse one shared authorization preflight for every Slack ingress path before firing message hooks or invoking agents.\n\nConcrete patch direction:\n\n1. Convert `app_mention` events through `_convert_event_to_message()` or a dedicated equivalent that preserves sender, channel, text, timestamp, and thread metadata.\n2. Set `bot_message._channel_type = \"slack\"` for `app_mention`.\n3. Run the same channel check, user allowlist check, and `UnknownUserHandler.handle(...)` policy used by `handle_message`.\n4. Only then strip the bot mention and invoke `_session.chat(...)`.\n5. Add regression tests for:\n- blocked user plus blocked channel: `message` and `app_mention` both drop;\n- blocked user in allowed channel: both drop;\n- allowed user in blocked channel: both drop;\n- allowed user in allowed channel: both invoke;\n- `unknown_user_policy=\"pair\"`: `app_mention` starts the same pairing flow instead of invoking the agent.\n\n## Affected Package/Versions\n\n- Repository: `MervinPraison/PraisonAI`\n- Package: `praisonai`\n- Component: `src/praisonai/praisonai/bots/slack.py`\n- Configuration component: `src/praisonai-agents/praisonaiagents/bots/config.py`\n\nConfirmed affected:\n\n```text\nv3.11.0 7f37d754a72511a71f7eeaaa8e9f367a5dc45fd8\nv3.11.14 44b800df0eddf32dd5242f47da7513e4a3159d76\nv3.12.0 51f95ad904a6616b35caede1cd74026ec8f7152c\nv4.4.5 9a3363c900fa3be3fce5483be7f6c1f418757ebb\nv4.4.6 90b00f9a25ee5c7ccf4b6ab3152700e1881f262d\nv4.4.12 7d0657632fc477673153ad116cecf692b454bfa3\nv4.5.2 8ddbb4ee7152d3fa68fbaaf6e6c610ae03d938d3\nv4.5.16 02a19776517cc76483fd58dcd6a5fdf8c2c45170\nv4.5.28 16f93251766505a79f237a0f07f68a0ecb17e358\nv4.5.112 bfe3d94bad6db92fc2927c2e3c081ae8303e209e\nv4.5.128 b4e3a8a84ade44ac3dd9102b792cdb4311a95937\nv4.6.10 4b1b17b963cbd0625e41394a30168c95b26429b2\nv4.6.33 dfbb8d78ec7e8dc7118bc722ab1b2524bc98ddab\nv4.6.34 e5928449f73f66cc8af1de61621aa974ab255133\nv4.6.56 d3c4a2afadfbf3a3e172e460e607ba4efad263a6\nv4.6.57 e90d92231853161ad931f3498da57651a9f8b528\nv4.6.58 1ad58ca02975ff1398efeda694ea2ab78f20cf3e\n```\n\nUnaffected boundary control:\n\n```text\nv3.10.24 de1734c29a50af18cf8c69e1d1d90e0f8e391aae\n```\n\n`v3.10.24` does not contain `src/praisonai/praisonai/bots/slack.py`.\n\nSuggested affected range: `praisonai >= 3.11.0, <= 4.6.58`.\n\n## Advisory History\n\nVisible PraisonAI advisories and prior submissions were checked for exact duplicates.\n\nNo existing public or private PraisonAI advisory found in this check covers Slack `app_mention` bypassing `allowed_users`, `allowed_channels`, or the unknown-user pairing policy.\n\nNearby PraisonAI advisories are distinct:\n\n- `GHSA-fc26-m9pf-v56q` / local poc covers LinearBot unsigned webhook fail-open behavior.\n- Published API/AgentOS/MCP auth advisories cover HTTP server authentication, not Slack bot sender/channel policy.\n- prior reports do not cover Slack bot `app_mention` authorization.\n\nThis report is structurally similar to public OpenClaw Slack authorization bypass advisories where non-message Slack event handlers failed to enforce the same sender/channel policy as the primary message path, but it is a PraisonAI SlackBot-specific sibling-handler issue.\n\n## References\n\n- PraisonAI Bot Security & DM Policy: https://docs.praison.ai/docs/best-practices/bot-security\n- Slack `app_mention` event documentation: https://docs.slack.dev/reference/events/app_mention/\n- OpenClaw Slack sender authorization bypass advisory: https://github.com/openclaw/openclaw/security/advisories/GHSA-v8cg-4474-49v8\n- PyPI latest PraisonAI release page: https://pypi.org/project/PraisonAI/\n",
"severity": [
{
"type": "CVSS_V3",
Expand Down Expand Up @@ -58,4 +58,4 @@
"github_reviewed_at": "2026-06-18T13:52:47Z",
"nvd_published_at": null
}
}
}