diff --git a/extensions/ollama/src/setup.test.ts b/extensions/ollama/src/setup.test.ts index a5438e2636a5..54beabc9b506 100644 --- a/extensions/ollama/src/setup.test.ts +++ b/extensions/ollama/src/setup.test.ts @@ -5,6 +5,7 @@ import { jsonResponse, requestBodyText, requestUrl } from "openclaw/plugin-sdk/t import { afterEach, describe, expect, it, vi } from "vitest"; import { resetOllamaModelShowInfoCacheForTest } from "./provider-models.js"; import { + checkOllamaCloudAuth, configureOllamaNonInteractive, ensureOllamaModelPulled, promptAndConfigureOllama, @@ -780,3 +781,47 @@ describe("ollama setup", () => { expect(result).toBe(nextConfig); }); }); + +describe("checkOllamaCloudAuth", () => { + afterEach(() => { + fetchWithSsrFGuardMock.mockClear(); + }); + + it("bounds oversized 401 body and cancels the stream", async () => { + const chunk = new Uint8Array(1024 * 1024); // 1 MiB chunk + let readCount = 0; + let canceled = false; + // 64 chunks × 1 MiB = 64 MiB — exceeds the 16 MiB cap + const oversizedBody = new ReadableStream({ + pull(controller) { + if (readCount >= 64) { + controller.close(); + return; + } + readCount += 1; + controller.enqueue(chunk); + }, + cancel() { + canceled = true; + }, + }); + + fetchWithSsrFGuardMock.mockResolvedValueOnce({ + response: new Response(oversizedBody, { + status: 401, + headers: { "Content-Type": "application/json" }, + }), + finalUrl: "https://ollama.com/api/me", + release: async () => {}, + }); + + await expect(checkOllamaCloudAuth("https://ollama.com")).resolves.toEqual({ + signedIn: false, + signinUrl: undefined, + }); + + // Stream must be cancelled before all 64 MiB are consumed + expect(readCount).toBeLessThan(64); + expect(canceled).toBe(true); + }); +}); diff --git a/extensions/ollama/src/setup.ts b/extensions/ollama/src/setup.ts index 6e366d9f4930..5ece2480a598 100644 --- a/extensions/ollama/src/setup.ts +++ b/extensions/ollama/src/setup.ts @@ -13,6 +13,7 @@ import { upsertAuthProfileWithLock, validateApiKeyInput, } from "openclaw/plugin-sdk/provider-auth"; +import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http"; import { applyAgentDefaultModelPrimary } from "openclaw/plugin-sdk/provider-onboard"; import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime"; import { WizardCancelledError, type WizardPrompter } from "openclaw/plugin-sdk/setup"; @@ -148,7 +149,10 @@ export async function checkOllamaCloudAuth( }); try { if (response.status === 401) { - const data = (await response.json()) as { signin_url?: string }; + const data = await readProviderJsonResponse<{ signin_url?: string }>( + response, + "ollama.cloud-auth", + ); return { signedIn: false, signinUrl: data.signin_url }; } if (!response.ok) {