Skip to content

Commit eb5fb2a

Browse files
authored
fix(microsoft-foundry): bound connection test error reads (#97812)
Co-authored-by: Pick-cat <[email protected]>
1 parent 2ec6708 commit eb5fb2a

2 files changed

Lines changed: 84 additions & 2 deletions

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Microsoft Foundry tests cover bounded connection-test error reads.
2+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3+
import * as cli from "./cli.js";
4+
import { testFoundryConnection } from "./onboard.js";
5+
import { DEFAULT_API } from "./shared.js";
6+
7+
const hoisted = vi.hoisted(() => ({
8+
fetchWithSsrFGuard: vi.fn(),
9+
}));
10+
11+
vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ({
12+
fetchWithSsrFGuard: hoisted.fetchWithSsrFGuard,
13+
}));
14+
15+
function cancelTrackedResponse(
16+
text: string,
17+
init: ResponseInit,
18+
): {
19+
response: Response;
20+
wasCanceled: () => boolean;
21+
} {
22+
let canceled = false;
23+
const stream = new ReadableStream<Uint8Array>({
24+
start(controller) {
25+
controller.enqueue(new TextEncoder().encode(text));
26+
},
27+
cancel() {
28+
canceled = true;
29+
},
30+
});
31+
return {
32+
response: new Response(stream, init),
33+
wasCanceled: () => canceled,
34+
};
35+
}
36+
37+
describe("testFoundryConnection", () => {
38+
beforeEach(() => {
39+
vi.spyOn(cli, "getAccessTokenResult").mockReturnValue({ accessToken: "token" });
40+
});
41+
42+
afterEach(() => {
43+
vi.restoreAllMocks();
44+
hoisted.fetchWithSsrFGuard.mockReset();
45+
});
46+
47+
it("bounds connection-test error bodies without using response.text()", async () => {
48+
const note = vi.fn();
49+
const tracked = cancelTrackedResponse(`${"foundry failure ".repeat(1024)}tail`, {
50+
status: 503,
51+
headers: { "content-type": "text/plain" },
52+
});
53+
const textSpy = vi.spyOn(tracked.response, "text").mockRejectedValue(new Error("unbounded"));
54+
hoisted.fetchWithSsrFGuard.mockResolvedValue({
55+
response: tracked.response,
56+
release: async () => {},
57+
});
58+
59+
await testFoundryConnection({
60+
ctx: { prompter: { note } } as never,
61+
endpoint: "https://example.openai.azure.com",
62+
modelId: "gpt-4o",
63+
api: DEFAULT_API,
64+
});
65+
66+
expect(textSpy).not.toHaveBeenCalled();
67+
expect(tracked.wasCanceled()).toBe(true);
68+
expect(note).toHaveBeenCalledWith(
69+
expect.stringContaining("Warning: test request returned 503"),
70+
"Connection Test",
71+
);
72+
});
73+
});

extensions/microsoft-foundry/onboard.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Microsoft Foundry setup module handles plugin onboarding behavior.
22
import type { ProviderAuthContext } from "openclaw/plugin-sdk/core";
33
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
4+
import { readResponseTextLimited } from "openclaw/plugin-sdk/provider-http";
45
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime";
56
import {
67
normalizeOptionalString,
@@ -32,6 +33,8 @@ import {
3233
usesFoundryResponsesByDefault,
3334
} from "./shared.js";
3435

36+
const FOUNDRY_CONNECTION_TEST_ERROR_BODY_LIMIT_BYTES = 8 * 1024;
37+
3538
export { listSubscriptions } from "./cli.js";
3639

3740
function listFoundryResources(subscriptionId?: string): FoundryResourceOption[] {
@@ -605,13 +608,19 @@ export async function testFoundryConnection(params: {
605608
});
606609
try {
607610
if (res.status === 400) {
608-
const body = await res.text().catch(() => "");
611+
const body = await readResponseTextLimited(
612+
res,
613+
FOUNDRY_CONNECTION_TEST_ERROR_BODY_LIMIT_BYTES,
614+
).catch(() => "");
609615
await params.ctx.prompter.note(
610616
`Endpoint is reachable but returned 400 Bad Request - check your deployment name and API version.\n${body.slice(0, 200)}`,
611617
"Connection Test",
612618
);
613619
} else if (!res.ok) {
614-
const body = await res.text().catch(() => "");
620+
const body = await readResponseTextLimited(
621+
res,
622+
FOUNDRY_CONNECTION_TEST_ERROR_BODY_LIMIT_BYTES,
623+
).catch(() => "");
615624
await params.ctx.prompter.note(
616625
`Warning: test request returned ${res.status}. ${body.slice(0, 200)}\nProceeding anyway - you can fix the endpoint later.`,
617626
"Connection Test",

0 commit comments

Comments
 (0)