fix(msteams): trust service URLs before replies
This commit is contained in:
@@ -434,6 +434,38 @@ async def test_handle_activity_denied_sender_does_not_store_ref(make_channel, tm
|
||||
assert not (tmp_path / "state" / "msteams_conversations.json").exists()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_activity_rejects_untrusted_service_url(make_channel, tmp_path):
|
||||
ch = make_channel(validateInboundAuth=False, allowFrom=["*"])
|
||||
|
||||
activity = {
|
||||
"type": "message",
|
||||
"id": "activity-poison",
|
||||
"text": "Hello from forged Teams activity",
|
||||
"serviceUrl": "https://attacker.example/collect",
|
||||
"channelId": "msteams",
|
||||
"conversation": {
|
||||
"id": "conv-poison",
|
||||
"conversationType": "personal",
|
||||
},
|
||||
"from": {
|
||||
"id": "29:attacker-user-id",
|
||||
"aadObjectId": "attacker-user-id",
|
||||
"name": "Attacker",
|
||||
},
|
||||
"recipient": {
|
||||
"id": "28:bot-id",
|
||||
"name": "nanobot",
|
||||
},
|
||||
}
|
||||
|
||||
await ch._handle_activity(activity)
|
||||
|
||||
assert ch.bus.inbound == []
|
||||
assert ch._conversation_refs == {}
|
||||
assert not (tmp_path / "state" / "msteams_conversations.json").exists()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_handle_activity_mention_only_uses_default_response(make_channel):
|
||||
ch = make_channel()
|
||||
@@ -739,6 +771,25 @@ async def test_send_raises_when_conversation_ref_missing(make_channel):
|
||||
await ch.send(OutboundMessage(channel="msteams", chat_id="missing", content="Reply text"))
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_rejects_untrusted_service_url_before_bearer_post(make_channel):
|
||||
ch = make_channel()
|
||||
fake_http = FakeHttpClient()
|
||||
ch._http = fake_http
|
||||
ch._token = "tok"
|
||||
ch._token_expires_at = 9999999999
|
||||
ch._conversation_refs["conv-poison"] = ConversationRef(
|
||||
service_url="https://attacker.example/collect",
|
||||
conversation_id="conv-poison",
|
||||
activity_id="activity-poison",
|
||||
)
|
||||
|
||||
with pytest.raises(RuntimeError, match="untrusted service_url"):
|
||||
await ch.send(OutboundMessage(channel="msteams", chat_id="conv-poison", content="Reply text"))
|
||||
|
||||
assert fake_http.calls == []
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_raises_delivery_failures_for_retry(make_channel):
|
||||
ch = make_channel()
|
||||
|
||||
Reference in New Issue
Block a user