Skip to content
Closed
Show file tree
Hide file tree
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
22 changes: 22 additions & 0 deletions plugins/platforms/slack/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2572,6 +2572,28 @@ async def _handle_slack_message(self, event: dict) -> None:
channel_type = "im"
is_dm = channel_type in {"im", "mpim"} # Both 1:1 and group DMs

# Reject unauthorized users before thread lookups, name resolution,
# or file downloads. The final gateway runner auth check happens
# after MessageEvent construction, so adapter-side media fetches need
# the same auth chain up front.
_runner = getattr(getattr(self, "_message_handler", None), "__self__", None)
_auth_fn = getattr(_runner, "_is_user_authorized", None)
if user_id and callable(_auth_fn):
_source = self.build_source(
chat_id=channel_id,
chat_name="",
chat_type="dm" if is_dm else "group",
user_id=user_id,
user_name="",
)
if not _auth_fn(_source):
logger.warning(
"[Slack] Early reject of unauthorized user %s in channel %s",
user_id,
channel_id,
)
return

# Build thread_ts for session keying.
# In channels: fall back to ts so each top-level @mention starts a
# new thread/session (the bot always replies in a thread).
Expand Down
43 changes: 43 additions & 0 deletions tests/gateway/test_slack.py
Original file line number Diff line number Diff line change
Expand Up @@ -1525,6 +1525,49 @@ async def test_file_shared_video_fallback_fetches_file_info(self, adapter):
assert os.path.exists(msg_event.media_urls[0])
assert msg_event.media_types == [SUPPORTED_VIDEO_TYPES[".mp4"]]

@pytest.mark.asyncio
async def test_unauthorized_message_does_not_fetch_file_info(
self,
adapter,
monkeypatch,
):
"""Global gateway auth must run before Slack file metadata fetches."""
monkeypatch.delenv("SLACK_ALLOW_ALL_USERS", raising=False)
monkeypatch.delenv("GATEWAY_ALLOW_ALL_USERS", raising=False)
monkeypatch.delenv("SLACK_ALLOWED_USERS", raising=False)
monkeypatch.setenv("GATEWAY_ALLOWED_USERS", "U_ALLOWED")

class Runner:
def _is_user_authorized(self, source):
return source.user_id == "U_ALLOWED"

async def handle(self, _event):
raise AssertionError("gateway handler should not run")

adapter._message_handler = Runner().handle
adapter._app.client.files_info = AsyncMock()

await adapter._handle_slack_message(
{
"type": "message",
"channel": "D123",
"channel_type": "im",
"user": "U_INTRUDER",
"text": "please read this",
"ts": "1234567890.000001",
"files": [
{
"id": "FSECRET",
"mimetype": "text/plain",
"name": "secret.txt",
}
],
}
)

adapter._app.client.files_info.assert_not_awaited()
adapter.handle_message.assert_not_called()

@pytest.mark.asyncio
async def test_download_failure_is_surfaced_in_message_text(self, adapter):
"""Attachment download failures (401/403/HTML-body/etc.) should be
Expand Down
Loading