Skip to content

Commit b3eb350

Browse files
cxbAsDevvincentkoc
authored andcommitted
fix(cli): bound docs search API response reads with committed test
1 parent 6cb82ea commit b3eb350

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

src/commands/docs.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,33 @@ describe("docsSearchCommand", () => {
9494
expect(runtime.exit).not.toHaveBeenCalled();
9595
expect(runtime.log).toHaveBeenCalled();
9696
});
97+
98+
it("rejects oversized docs search responses", async () => {
99+
const ONE_MIB = 1024 * 1024;
100+
const cancel = vi.fn();
101+
const stream = new ReadableStream<Uint8Array>({
102+
cancel,
103+
start(controller) {
104+
for (let i = 0; i < 10; i++) {
105+
controller.enqueue(new Uint8Array(ONE_MIB));
106+
}
107+
controller.close();
108+
},
109+
});
110+
fetchMock.mockResolvedValueOnce(
111+
new Response(stream, {
112+
status: 200,
113+
headers: { "Content-Type": "application/json" },
114+
}),
115+
);
116+
const runtime = makeRuntime();
117+
118+
await docsSearchCommand(["oversized"], runtime);
119+
120+
expect(runtime.error).toHaveBeenCalledWith(
121+
expect.stringContaining("Docs search response exceeds"),
122+
);
123+
expect(runtime.exit).toHaveBeenCalledWith(1);
124+
expect(cancel).toHaveBeenCalledOnce();
125+
});
97126
});

src/commands/docs.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
// Implements docs link/search output for `openclaw docs`.
2+
import { readResponseWithLimit } from "@openclaw/media-core/read-response-with-limit";
23
import { formatDocsLink } from "../../packages/terminal-core/src/links.js";
34
import { isRich, theme } from "../../packages/terminal-core/src/theme.js";
45
import { formatCliCommand } from "../cli/command-format.js";
56
import type { RuntimeEnv } from "../runtime.js";
67

78
const SEARCH_API = "https://docs.openclaw.ai/api/search";
89
const SEARCH_TIMEOUT_MS = 30_000;
10+
const DOCS_SEARCH_RESPONSE_MAX_BYTES = 8 * 1024 * 1024;
911

1012
type DocResult = {
1113
title: string;
@@ -75,7 +77,10 @@ async function fetchDocsSearch(query: string): Promise<DocResult[]> {
7577
if (!response.ok) {
7678
throw new Error(`HTTP ${response.status}`);
7779
}
78-
const payload = (await response.json()) as DocsSearchResponse;
80+
const bytes = await readResponseWithLimit(response, DOCS_SEARCH_RESPONSE_MAX_BYTES, {
81+
onOverflow: ({ maxBytes }) => new Error(`Docs search response exceeds ${maxBytes} bytes`),
82+
});
83+
const payload = JSON.parse(new TextDecoder().decode(bytes)) as DocsSearchResponse;
7984
return parseDocsSearchResults(payload.results);
8085
} finally {
8186
clearTimeout(timeout);

0 commit comments

Comments
 (0)