Skip to content

Commit c98459d

Browse files
committed
fix(msteams): wrap malformed api json
1 parent 376a792 commit c98459d

5 files changed

Lines changed: 58 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Docs: https://docs.openclaw.ai
8585
- Brave Search: report malformed web and LLM-context API JSON with provider-owned errors instead of leaking raw parser failures.
8686
- xAI tools: report malformed web search, X search, and code execution JSON with provider-owned errors instead of leaking raw parser failures.
8787
- Nextcloud Talk: report malformed room-info and bot-admin JSON with channel-owned errors instead of leaking raw parser failures.
88+
- Microsoft Teams: report malformed Graph and delegated OAuth JSON with channel-owned errors instead of leaking raw parser failures.
8889
- Twilio voice-call: report malformed successful API JSON responses with provider-owned errors instead of leaking raw parser failures.
8990
- Voice-call provider APIs: report malformed successful guarded JSON responses with provider-prefixed errors instead of leaking raw parser failures.
9091
- Realtime transcription: report malformed provider websocket JSON frames with owned parser errors instead of leaking raw `SyntaxError` objects.

extensions/msteams/src/graph.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,19 @@ describe("msteams graph helpers", () => {
193193
}),
194194
"Graph /teams/team-1/channels failed (403): forbidden",
195195
);
196+
197+
mockTextFetchResponse("{ nope", {
198+
status: 200,
199+
headers: { "content-type": "application/json" },
200+
});
201+
202+
await expectRejectsToThrow(
203+
fetchGraphJson({
204+
token: graphToken,
205+
path: "/teams/team-1/channels",
206+
}),
207+
"Graph /teams/team-1/channels failed: malformed JSON response",
208+
);
196209
});
197210

198211
it("posts Graph JSON to v1 and beta roots and treats empty mutation responses as undefined", async () => {

extensions/msteams/src/graph.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
12
import { fetchWithSsrFGuard, type MSTeamsConfig } from "../runtime-api.js";
23
import { GRAPH_ROOT } from "./attachments/shared.js";
34

@@ -118,7 +119,7 @@ export async function fetchGraphAbsoluteUrl<T>(params: {
118119
`Graph ${params.url} failed (${response.status}): ${text || "unknown error"}`,
119120
);
120121
}
121-
return (await response.json()) as T;
122+
return await readProviderJsonResponse<T>(response, `Graph ${params.url} failed`);
122123
} finally {
123124
await release();
124125
}

extensions/msteams/src/oauth.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,25 @@ describe("exchangeMSTeamsCodeForTokens", () => {
228228
}),
229229
).rejects.toThrow(/MSTeams token exchange failed \(400\)/);
230230
});
231+
232+
it("reports malformed token exchange JSON with a stable OAuth error", async () => {
233+
fetchSpy.mockResolvedValueOnce(
234+
new Response("{ nope", {
235+
status: 200,
236+
headers: { "Content-Type": "application/json" },
237+
}),
238+
);
239+
240+
await expect(
241+
exchangeMSTeamsCodeForTokens({
242+
tenantId: "t",
243+
clientId: "c",
244+
clientSecret: "s", // pragma: allowlist secret
245+
code: "bad-json",
246+
verifier: "v",
247+
}),
248+
).rejects.toThrow("MSTeams token exchange failed: malformed JSON response");
249+
});
231250
});
232251

233252
describe("refreshMSTeamsDelegatedTokens", () => {
@@ -310,4 +329,22 @@ describe("refreshMSTeamsDelegatedTokens", () => {
310329
}),
311330
).rejects.toThrow(/MSTeams token refresh failed \(401\)/);
312331
});
332+
333+
it("reports malformed token refresh JSON with a stable OAuth error", async () => {
334+
fetchSpy.mockResolvedValueOnce(
335+
new Response("{ nope", {
336+
status: 200,
337+
headers: { "Content-Type": "application/json" },
338+
}),
339+
);
340+
341+
await expect(
342+
refreshMSTeamsDelegatedTokens({
343+
tenantId: "t",
344+
clientId: "c",
345+
clientSecret: "s", // pragma: allowlist secret
346+
refreshToken: "bad-json",
347+
}),
348+
).rejects.toThrow("MSTeams token refresh failed: malformed JSON response");
349+
});
313350
});

extensions/msteams/src/oauth.token.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
12
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
23
import {
34
MSTEAMS_DEFAULT_DELEGATED_SCOPES,
@@ -65,7 +66,10 @@ async function fetchMSTeamsTokens(params: {
6566
const errorText = await response.text();
6667
throw new Error(`MSTeams ${params.failureLabel} failed (${response.status}): ${errorText}`);
6768
}
68-
return (await response.json()) as MSTeamsTokenResponse;
69+
return await readProviderJsonResponse<MSTeamsTokenResponse>(
70+
response,
71+
`MSTeams ${params.failureLabel} failed`,
72+
);
6973
} finally {
7074
await release();
7175
}

0 commit comments

Comments
 (0)