diff --git a/gateway/platforms/qqbot/adapter.py b/gateway/platforms/qqbot/adapter.py index 9532662131dff..230f941d4adad 100644 --- a/gateway/platforms/qqbot/adapter.py +++ b/gateway/platforms/qqbot/adapter.py @@ -1092,7 +1092,7 @@ def _is_authorized_interaction_for_session( chat_type = parsed.get("chat_type", "") chat_id = parsed.get("chat_id", "") - if chat_type == "c2c": + if chat_type in {"c2c", "dm"}: return bool(chat_id) and operator == chat_id if chat_type in {"group", "guild"}: diff --git a/tests/gateway/test_qqbot.py b/tests/gateway/test_qqbot.py index 816bb5f16017c..5c47b5487f6e0 100644 --- a/tests/gateway/test_qqbot.py +++ b/tests/gateway/test_qqbot.py @@ -1576,6 +1576,59 @@ def fake_resolve(session_key, choice, resolve_all=False): assert resolve_calls == [("agent:main:qqbot:c2c:u-42", "once", False)] + @pytest.mark.asyncio + async def test_dm_approval_click_once_maps_to_once(self): + """DM session approval clicks authorize when operator matches chat id.""" + adapter = self._make_adapter() + + resolve_calls = [] + + def fake_resolve(session_key, choice, resolve_all=False): + resolve_calls.append((session_key, choice, resolve_all)) + return 1 + + import tools.approval + orig = tools.approval.resolve_gateway_approval + tools.approval.resolve_gateway_approval = fake_resolve + try: + from gateway.platforms.qqbot.keyboards import parse_interaction_event + event = parse_interaction_event({ + "id": "i", + "chat_type": 2, + "user_openid": "u-42", + "data": {"resolved": {"button_data": "approve:agent:main:qqbot:dm:u-42:allow-once"}}, + }) + await adapter._default_interaction_dispatch(event) + finally: + tools.approval.resolve_gateway_approval = orig + + assert resolve_calls == [("agent:main:qqbot:dm:u-42", "once", False)] + + @pytest.mark.asyncio + async def test_dm_approval_click_rejects_mismatched_operator(self): + """DM session approval clicks still require operator == chat_id.""" + adapter = self._make_adapter() + resolve_calls = [] + + def fake_resolve(session_key, choice, resolve_all=False): + resolve_calls.append((session_key, choice, resolve_all)) + return 1 + + import tools.approval + orig = tools.approval.resolve_gateway_approval + tools.approval.resolve_gateway_approval = fake_resolve + try: + from gateway.platforms.qqbot.keyboards import parse_interaction_event + event = parse_interaction_event({ + "id": "i", "chat_type": 2, "user_openid": "attacker", + "data": {"resolved": {"button_data": "approve:agent:main:qqbot:dm:u-42:allow-once"}}, + }) + await adapter._default_interaction_dispatch(event) + finally: + tools.approval.resolve_gateway_approval = orig + + assert resolve_calls == [] + @pytest.mark.asyncio async def test_approval_click_always_maps_to_always(self): adapter = self._make_adapter()