diff --git a/plugins/platforms/slack/adapter.py b/plugins/platforms/slack/adapter.py index dab05f8bfd4e..830e2bdedd61 100644 --- a/plugins/platforms/slack/adapter.py +++ b/plugins/platforms/slack/adapter.py @@ -3942,22 +3942,25 @@ class SlackAdapter(BasePlatformAdapter): # both as DM-style persistent conversations. is_one_to_one_dm = channel_type == "im" - # Early auth check: reject unauthorized users before any API calls - # (thread context fetch, user name resolution, file downloads). - # Pattern matches Telegram fix #54164 — gate at the adapter level - # BEFORE event construction consumes resources. - if user_id: + # 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_id=channel_id, + chat_name="", chat_type="dm" if is_dm else "group", - user_id=user_id, user_name="", + user_id=user_id, + user_name="", ) - _runner = getattr(getattr(self, "_message_handler", None), "__self__", None) - _auth_fn = getattr(_runner, "_is_user_authorized", None) - if callable(_auth_fn) and not _auth_fn(_source): + if not _auth_fn(_source): logger.warning( "[Slack] Early reject of unauthorized user %s in channel %s", - user_id, channel_id, + user_id, + channel_id, ) return diff --git a/tests/gateway/test_slack.py b/tests/gateway/test_slack.py index 65e92a10516a..240fbac10067 100644 --- a/tests/gateway/test_slack.py +++ b/tests/gateway/test_slack.py @@ -1911,6 +1911,49 @@ class TestIncomingDocumentHandling: 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