mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 03:15:46 -06:00
fix(signal): bound GitHub release info JSON response with readProviderJsonResponse (#97536)
Replace bare `await response.json()` in `installSignalCliFromRelease` with
`readProviderJsonResponse` (16 MiB cap, stream cancel on overflow). The
external GitHub Releases endpoint can include a large `body` changelog field;
the error path was already guarded but the success path was unbounded.
The existing inner catch continues to convert overflow errors into the
graceful `{ ok: false, error: "Failed to parse signal-cli release info." }` path.
Adds a regression test verifying the stream is cancelled before all chunks are
read on an oversized 20 MiB streaming response.
Co-authored-by: NIO <nocodet@mail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
(cherry picked from commit 51064bda4d)
This commit is contained in:
@@ -277,6 +277,38 @@ describe("installSignalCliFromRelease", () => {
|
||||
expect(fetchResult.release).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("bounds oversized GitHub release metadata and cancels the stream", async () => {
|
||||
const chunkSize = 1024 * 1024;
|
||||
const chunkCount = 20; // 20 MiB — over the 16 MiB cap
|
||||
let readCount = 0;
|
||||
let canceled = false;
|
||||
const oversized = new Response(
|
||||
new ReadableStream<Uint8Array>({
|
||||
pull(controller) {
|
||||
if (readCount >= chunkCount) {
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
readCount += 1;
|
||||
controller.enqueue(new Uint8Array(chunkSize));
|
||||
},
|
||||
cancel() {
|
||||
canceled = true;
|
||||
},
|
||||
}),
|
||||
{ status: 200, headers: { "Content-Type": "application/json" } },
|
||||
);
|
||||
const releaseMock = vi.fn().mockResolvedValue(undefined);
|
||||
fetchWithSsrFGuardMock.mockResolvedValue({ response: oversized, release: releaseMock });
|
||||
|
||||
const result = await installSignalCliFromRelease({ log: vi.fn() } as unknown as RuntimeEnv);
|
||||
|
||||
expect(result).toEqual({ ok: false, error: "Failed to parse signal-cli release info." });
|
||||
expect(canceled).toBe(true);
|
||||
expect(readCount).toBeLessThan(chunkCount);
|
||||
expect(releaseMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("bounds the release metadata request with an explicit timeout", async () => {
|
||||
const fetchResult = okDownloadResponse(JSON.stringify({ tag_name: "v0.14.3", assets: [] }), {
|
||||
headers: { "content-type": "application/json" },
|
||||
|
||||
@@ -5,6 +5,7 @@ import path from "node:path";
|
||||
import { Readable, Transform } from "node:stream";
|
||||
import { pipeline } from "node:stream/promises";
|
||||
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
|
||||
import { readProviderJsonResponse } from "openclaw/plugin-sdk/provider-http";
|
||||
import { runPluginCommandWithTimeout } from "openclaw/plugin-sdk/run-command";
|
||||
import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
|
||||
import { CONFIG_DIR, extractArchive, resolveBrewExecutable } from "openclaw/plugin-sdk/setup-tools";
|
||||
@@ -303,7 +304,7 @@ export async function installSignalCliFromRelease(
|
||||
};
|
||||
}
|
||||
try {
|
||||
payload = (await response.json()) as ReleaseResponse;
|
||||
payload = await readProviderJsonResponse<ReleaseResponse>(response, "signal.release-info");
|
||||
} catch {
|
||||
return {
|
||||
ok: false,
|
||||
|
||||
Reference in New Issue
Block a user