fix(msteams): wrap malformed api json · openclaw/openclaw@c98459d
vincentkoc
·
2026-05-15
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -85,6 +85,7 @@ Docs: https://docs.openclaw.ai
|
85 | 85 | - Brave Search: report malformed web and LLM-context API JSON with provider-owned errors instead of leaking raw parser failures. |
86 | 86 | - xAI tools: report malformed web search, X search, and code execution JSON with provider-owned errors instead of leaking raw parser failures. |
87 | 87 | - 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. |
88 | 89 | - Twilio voice-call: report malformed successful API JSON responses with provider-owned errors instead of leaking raw parser failures. |
89 | 90 | - Voice-call provider APIs: report malformed successful guarded JSON responses with provider-prefixed errors instead of leaking raw parser failures. |
90 | 91 | - Realtime transcription: report malformed provider websocket JSON frames with owned parser errors instead of leaking raw `SyntaxError` objects. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -193,6 +193,19 @@ describe("msteams graph helpers", () => {
|
193 | 193 | }), |
194 | 194 | "Graph /teams/team-1/channels failed (403): forbidden", |
195 | 195 | ); |
| 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 | +); |
196 | 209 | }); |
197 | 210 | |
198 | 211 | it("posts Graph JSON to v1 and beta roots and treats empty mutation responses as undefined", async () => { |
|
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http"; |
1 | 2 | import { fetchWithSsrFGuard, type MSTeamsConfig } from "../runtime-api.js"; |
2 | 3 | import { GRAPH_ROOT } from "./attachments/shared.js"; |
3 | 4 | |
@@ -118,7 +119,7 @@ export async function fetchGraphAbsoluteUrl<T>(params: {
|
118 | 119 | `Graph ${params.url} failed (${response.status}): ${text || "unknown error"}`, |
119 | 120 | ); |
120 | 121 | } |
121 | | -return (await response.json()) as T; |
| 122 | +return await readProviderJsonResponse<T>(response, `Graph ${params.url} failed`); |
122 | 123 | } finally { |
123 | 124 | await release(); |
124 | 125 | } |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -228,6 +228,25 @@ describe("exchangeMSTeamsCodeForTokens", () => {
|
228 | 228 | }), |
229 | 229 | ).rejects.toThrow(/MSTeams token exchange failed \(400\)/); |
230 | 230 | }); |
| 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 | +}); |
231 | 250 | }); |
232 | 251 | |
233 | 252 | describe("refreshMSTeamsDelegatedTokens", () => { |
@@ -310,4 +329,22 @@ describe("refreshMSTeamsDelegatedTokens", () => {
|
310 | 329 | }), |
311 | 330 | ).rejects.toThrow(/MSTeams token refresh failed \(401\)/); |
312 | 331 | }); |
| 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 | +}); |
313 | 350 | }); |
| Original file line number | Diff line number | Diff line change |
|---|
|
| 1 | +import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http"; |
1 | 2 | import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime"; |
2 | 3 | import { |
3 | 4 | MSTEAMS_DEFAULT_DELEGATED_SCOPES, |
@@ -65,7 +66,10 @@ async function fetchMSTeamsTokens(params: {
|
65 | 66 | const errorText = await response.text(); |
66 | 67 | throw new Error(`MSTeams ${params.failureLabel} failed (${response.status}): ${errorText}`); |
67 | 68 | } |
68 | | -return (await response.json()) as MSTeamsTokenResponse; |
| 69 | +return await readProviderJsonResponse<MSTeamsTokenResponse>( |
| 70 | +response, |
| 71 | +`MSTeams ${params.failureLabel} failed`, |
| 72 | +); |
69 | 73 | } finally { |
70 | 74 | await release(); |
71 | 75 | } |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。