fix(signal): bound GitHub release info JSON response with readProviderJsonResponse#97536
Conversation
…rJsonResponse
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: Cursor <cursoragent@cursor.com>
|
Codex review: needs maintainer review before merge. Reviewed June 28, 2026, 1:27 PM ET / 17:27 UTC. Summary PR surface: Source +1, Tests +32. Total +33 across 2 files. Reproducibility: yes. Current main has a source-level reproduction path because a successful Signal release metadata response is parsed with unbounded Review metrics: none identified. Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Next step before merge
Security Review detailsBest possible solution: Merge this narrow Signal installer hardening after normal maintainer approval, keeping the shared provider HTTP bounded JSON reader as the canonical implementation. Do we have a high-confidence way to reproduce the issue? Yes. Current main has a source-level reproduction path because a successful Signal release metadata response is parsed with unbounded Is this the best way to solve the issue? Yes. Using the existing Plugin SDK bounded JSON reader at the exact success-path parser is the narrow owner-boundary fix; a Signal-local limiter would duplicate the shared response-limit contract. AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 891096926e27. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +1, Tests +32. Total +33 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics. How this review workflow works
|
…rJsonResponse (openclaw#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>
…rJsonResponse (openclaw#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>
…rJsonResponse (openclaw#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>
…rJsonResponse (openclaw#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 51064bd)
…rJsonResponse (openclaw#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>
…rJsonResponse (openclaw#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>
What Problem This Solves
installSignalCliFromReleasefetcheshttps://api.github.com/repos/AsamK/signal-cli/releases/latestand reads theresponse with an unbounded
await response.json()call. GitHub release objectsinclude a
bodyfield containing the full changelog text plus anassets[]array; a misbehaving or slow endpoint can return an arbitrarily large payload
that is buffered entirely into memory.
The error path already guarded against a bad HTTP status (
!response.ok), butthe success path had no size limit — a textbook asymmetric-bounding gap.
Why This Change Was Made
Replace the bare
response.json()withreadProviderJsonResponse, whichenforces a 16 MiB cap, cancels the underlying stream on overflow, and surfaces a
consistent labelled error (
signal.release-info: …). The existing innertry/catchcontinues to convert any parse or overflow error into the friendly{ ok: false, error: "Failed to parse signal-cli release info." }result,preserving the caller's graceful-degradation path.
User Impact
An operator cannot cause the Signal plugin setup to OOM the process by returning
an oversized release-info JSON body (whether from a misconfigured proxy or a
slow/malformed GitHub response).
Evidence
Proof script (
scripts/proof-signal-release-info-bound.mjs) — 4 assertions:Unit tests (
extensions/signal/src/install-signal-cli.test.ts) — 29/29pass, including new regression: