fix(cli): bound docs search API response reads with committed test (#98188)

This commit is contained in:
cxbAsDev
2026-07-01 01:00:17 +08:00
committed by GitHub
parent a75431c586
commit f284ce3b4d
2 changed files with 35 additions and 1 deletions
+29
View File
@@ -94,4 +94,33 @@ describe("docsSearchCommand", () => {
expect(runtime.exit).not.toHaveBeenCalled();
expect(runtime.log).toHaveBeenCalled();
});
it("rejects oversized docs search responses", async () => {
const ONE_MIB = 1024 * 1024;
const cancel = vi.fn();
const stream = new ReadableStream<Uint8Array>({
cancel,
start(controller) {
for (let i = 0; i < 10; i++) {
controller.enqueue(new Uint8Array(ONE_MIB));
}
controller.close();
},
});
fetchMock.mockResolvedValueOnce(
new Response(stream, {
status: 200,
headers: { "Content-Type": "application/json" },
}),
);
const runtime = makeRuntime();
await docsSearchCommand(["oversized"], runtime);
expect(runtime.error).toHaveBeenCalledWith(
expect.stringContaining("Docs search response exceeds"),
);
expect(runtime.exit).toHaveBeenCalledWith(1);
expect(cancel).toHaveBeenCalledOnce();
});
});
+6 -1
View File
@@ -1,4 +1,5 @@
// Implements docs link/search output for `openclaw docs`.
import { readResponseWithLimit } from "@openclaw/media-core/read-response-with-limit";
import { formatDocsLink } from "../../packages/terminal-core/src/links.js";
import { isRich, theme } from "../../packages/terminal-core/src/theme.js";
import { formatCliCommand } from "../cli/command-format.js";
@@ -6,6 +7,7 @@ import type { RuntimeEnv } from "../runtime.js";
const SEARCH_API = "https://docs.openclaw.ai/api/search";
const SEARCH_TIMEOUT_MS = 30_000;
const DOCS_SEARCH_RESPONSE_MAX_BYTES = 8 * 1024 * 1024;
type DocResult = {
title: string;
@@ -75,7 +77,10 @@ async function fetchDocsSearch(query: string): Promise<DocResult[]> {
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const payload = (await response.json()) as DocsSearchResponse;
const bytes = await readResponseWithLimit(response, DOCS_SEARCH_RESPONSE_MAX_BYTES, {
onOverflow: ({ maxBytes }) => new Error(`Docs search response exceeds ${maxBytes} bytes`),
});
const payload = JSON.parse(new TextDecoder().decode(bytes)) as DocsSearchResponse;
return parseDocsSearchResults(payload.results);
} finally {
clearTimeout(timeout);