From b71790bb7e27e56f8890882246ffaa7797c99df9 Mon Sep 17 00:00:00 2001 From: Adam <835217250@qq.com> Date: Sat, 18 Jul 2026 22:34:56 +0800 Subject: [PATCH] fix(providers): accept m365.cloud.microsoft for copilot-m365-web token (#7078) (#7166) * fix(providers): accept m365.cloud.microsoft for copilot-m365-web token (#7078) * test: regression for #7078 m365.cloud.microsoft token extraction * fix(7078): match on url.hostname and anchor path with startsWith * test(7078): cover explicit :443 port via url.hostname --- src/lib/providers/validation/webProvidersB.ts | 19 ++++++-- .../m365-web-token-extraction-7078.test.ts | 48 +++++++++++++++++++ 2 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 tests/unit/m365-web-token-extraction-7078.test.ts diff --git a/src/lib/providers/validation/webProvidersB.ts b/src/lib/providers/validation/webProvidersB.ts index 3d9ddd84d4..6f1069f9ff 100644 --- a/src/lib/providers/validation/webProvidersB.ts +++ b/src/lib/providers/validation/webProvidersB.ts @@ -292,7 +292,7 @@ export async function validateCopilotWebProvider({ apiKey, providerSpecificData } } -function extractM365CredentialParts(raw: string, providerSpecificData: Record) { +export function extractM365CredentialParts(raw: string, providerSpecificData: Record) { const text = raw.trim(); const parts: Record = {}; @@ -304,13 +304,22 @@ function extractM365CredentialParts(raw: string, providerSpecificData: Record, + // so extraction is unchanged. (OmniRoute issue #7078) + if (/^wss:\/\//i.test(text)) { try { const url = new URL(text); - parts.access_token ||= url.searchParams.get("access_token") || ""; - parts.chathubPath ||= decodeURIComponent( - url.pathname.split("/m365Copilot/Chathub/")[1] || "" + const hostOk = /^(?:[\w-]+\.)*(?:m365\.cloud\.microsoft|copilot\.microsoft\.com|substrate\.office\.com)$/i.test( + url.hostname ); + if (hostOk && url.pathname.startsWith("/m365Copilot/Chathub/")) { + parts.access_token ||= url.searchParams.get("access_token") || ""; + parts.chathubPath ||= decodeURIComponent( + url.pathname.split("/m365Copilot/Chathub/")[1] || "" + ); + } } catch { // Fall through to the structured key/value parser result. } diff --git a/tests/unit/m365-web-token-extraction-7078.test.ts b/tests/unit/m365-web-token-extraction-7078.test.ts new file mode 100644 index 0000000000..68ed08b7fc --- /dev/null +++ b/tests/unit/m365-web-token-extraction-7078.test.ts @@ -0,0 +1,48 @@ +// Issue #7078 — Microsoft 365 Copilot web token extraction must accept the current +// m365.cloud.microsoft endpoint (and legacy substrate.office.com / copilot.microsoft.com), +// not just the old substrate.office.com WS host. Verifies access_token + chathubPath parse. +import test from "node:test"; +import assert from "node:assert/strict"; + +const B = await import("../../src/lib/providers/validation/webProvidersB.ts"); +const extract = (raw: string) => + (B as Record).extractM365CredentialParts(raw, {}); + +test("#7078 m365.cloud.microsoft wss URL extracts access_token + chathubPath", () => { + const raw = + "wss://m365.cloud.microsoft/m365Copilot/Chathub/user@tenant.example.com?access_token=TOKEN123"; + const parts = extract(raw); + assert.equal(parts.accessToken, "TOKEN123"); + assert.equal(parts.chathubPath, "user@tenant.example.com"); +}); + +test("#7078 regional subdomain m365.cloud.microsoft also accepted", () => { + const raw = + "wss://eu.m365.cloud.microsoft/m365Copilot/Chathub/user@tenant?access_token=TOKEN456"; + const parts = extract(raw); + assert.equal(parts.accessToken, "TOKEN456"); + assert.equal(parts.chathubPath, "user@tenant"); +}); + +test("#7078 legacy substrate.office.com still works (no regression)", () => { + const raw = + "wss://substrate.office.com/m365Copilot/Chathub/user@tenant?access_token=LEGACY"; + const parts = extract(raw); + assert.equal(parts.accessToken, "LEGACY"); + assert.equal(parts.chathubPath, "user@tenant"); +}); + +test("#7078 key/value string form still parsed", () => { + const raw = "access_token=KV;chathubPath=user@tenant"; + const parts = extract(raw); + assert.equal(parts.accessToken, "KV"); + assert.equal(parts.chathubPath, "user@tenant"); +}); + +test("#7078 m365.cloud.microsoft with explicit :443 port still extracts (hostname, not host)", () => { + const raw = + "wss://m365.cloud.microsoft:443/m365Copilot/Chathub/user@tenant?access_token=TOKENPORT"; + const parts = extract(raw); + assert.equal(parts.accessToken, "TOKENPORT"); + assert.equal(parts.chathubPath, "user@tenant"); +});