fix(security): reuse auth chain when tagging unverified senders in Slack threads - #55979
Merged
Conversation
…ack threads Mitigates indirect prompt injection (CWE-863) in Slack thread context. When the bot is mentioned mid-thread for the first time, _fetch_thread_context pulls the full thread via conversations.replies and prepends every reply to the LLM prompt. Replies from senders not on the allowlist were rendered identically to authorised senders, letting a third party in a shared channel inject instructions the model might act on when answering the next authorised message. - BasePlatformAdapter.set_authorization_check / _is_sender_authorized, registered by GatewayRunner._make_adapter_auth_check() with a closure over the existing _is_user_authorized chain (platform/global/group allowlists, allow-all flags, pairing store all stay the single source of truth — no env-var re-parsing). - Tags non-bot thread messages whose sender fails the auth check with an [unverified] prefix; strengthens the header with soft guidance only when at least one unverified message is present, so setups without an allowlist see no behaviour change. - Wired into all three adapter-init sites in run.py (start, reconnect watcher, restart) so the reconnect path is covered too. Softened wording: adapted from the original [untrusted] tag to [unverified] and non-accusatory header framing — the label reflects allowlist status, not a judgment about the person. Adapter relocated to plugins/platforms/slack/ since the PR was authored. Salvaged from #17059.
Collaborator
Related: this is the salvage of #17059 (authorship preserved by @syahidfrd, adapter relocated to |
1 task
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Slack thread context now marks messages from senders not on the allowlist as
[unverified], so the LLM treats them as background reference rather than authoritative input — closing an indirect prompt-injection vector (CWE-863).Root cause: When the bot is @-mentioned mid-thread for the first time,
_fetch_thread_context()pulls the full thread viaconversations.repliesand prepends every reply to the LLM prompt. Replies from non-allowlisted senders were rendered identically to authorised ones, so a third party in a shared channel could plant instructions the model might act on when answering the next authorised message.Changes
gateway/platforms/base.py:BasePlatformAdapter.set_authorization_check()/_is_sender_authorized()— an optional, adapter-agnostic auth hook. ReturnsNonewhen no check is registered (legacy behaviour preserved).gateway/run.py:GatewayRunner._make_adapter_auth_check()builds a platform-bound callback that closes over the existing_is_user_authorizedchain — platform + global + group allowlists, allow-all flags, and the pairing store stay the single source of truth. No env-var re-parsing inside adapters. Wired into all three adapter-init sites (start, reconnect watcher, restart).plugins/platforms/slack/adapter.py: tags non-bot thread replies whose sender fails the auth check with[unverified]; strengthens the context header with soft guidance only when at least one unverified message is present, so allowlist-free setups see no behaviour change.tests/gateway/test_slack.py: 7 tests (legacy format when no callback, no tags when all authorised, tagging when any unauthorised, strong vs. legacy header switch, callback-exception safety, forwardedchat_type/chat_id).Wording
Softened from the original
[untrusted]tag to[unverified]with non-accusatory header framing. The label reflects the sender's allowlist status (identity not confirmed) — not a judgment about the person. Same security behaviour; the model still won't act on injected instructions.Validation
tests/gateway/test_slack.pySessionSourcebuilt with correct fieldsRelation to #10035
#10035 fixes the same bug (submitted earlier) but re-parses
SLACK_ALLOWED_USERS/*_ALLOW_ALL_USERSenv vars directly inside the adapter — missing the pairing store, the globalGATEWAY_ALLOWED_USERSallowlist, and group allowlists, and duplicating auth logic. This PR routes through the single_is_user_authorizedsource of truth. Closing #10035 in favour of this, with credit.Salvaged from #17059 by @syahidfrd — authorship preserved. Adapter relocated to
plugins/platforms/slack/and wired into the third (reconnect) init site since the PR was authored.Infographic