fix(moonshot): bound video description JSON response reads#96502
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 30, 2026, 3:05 PM ET / 19:05 UTC. Summary PR surface: Source +4, Tests +71. Total +75 across 2 files. Reproducibility: yes. Current main and v2026.6.11 still show the unbounded Moonshot Review metrics: 1 noteworthy metric.
Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the Moonshot call-site hardening through the existing shared SDK reader, with the new overflow and malformed-JSON tests, after exact-head merge gates are clear. Do we have a high-confidence way to reproduce the issue? Yes. Current main and v2026.6.11 still show the unbounded Moonshot Is this the best way to solve the issue? Yes. Reusing AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 66e676d29b92. Label changesLabel justifications:
Evidence reviewedPR surface: Source +4, Tests +71. Total +75 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
|
|
@sallyom here is a bounded response pr ready to merge, I'd really appreciate it if you could take a look! |
|
Maintainer land-ready verification:
No blocking findings remain; the shared 16 MiB Moonshot success-body cap is accepted. |
The Moonshot video description endpoint used an unbounded await res.json() to parse the media understanding response. Route through readProviderJsonResponse (16 MiB cap) to match the bound already in place for other media understanding providers (xai, openrouter). AI-assisted. Co-authored-by: Cursor <cursoragent@cursor.com>
b232b95 to
f3805cc
Compare
|
Land-ready follow-up after rebasing onto current
Remote Crabbox proof was unavailable because this machine has neither the Blacksmith CLI nor broker credentials; the real loopback socket proof exercises the changed streaming boundary directly. |
|
Merged via squash.
|
…96502) * fix(moonshot): bound video description JSON response reads The Moonshot video description endpoint used an unbounded await res.json() to parse the media understanding response. Route through readProviderJsonResponse (16 MiB cap) to match the bound already in place for other media understanding providers (xai, openrouter). AI-assisted. Co-authored-by: Cursor <cursoragent@cursor.com> * test(moonshot): add bounds and malformed-JSON coverage for video description --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…96502) * fix(moonshot): bound video description JSON response reads The Moonshot video description endpoint used an unbounded await res.json() to parse the media understanding response. Route through readProviderJsonResponse (16 MiB cap) to match the bound already in place for other media understanding providers (xai, openrouter). AI-assisted. Co-authored-by: Cursor <cursoragent@cursor.com> * test(moonshot): add bounds and malformed-JSON coverage for video description --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…96502) * fix(moonshot): bound video description JSON response reads The Moonshot video description endpoint used an unbounded await res.json() to parse the media understanding response. Route through readProviderJsonResponse (16 MiB cap) to match the bound already in place for other media understanding providers (xai, openrouter). AI-assisted. Co-authored-by: Cursor <cursoragent@cursor.com> * test(moonshot): add bounds and malformed-JSON coverage for video description --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…96502) * fix(moonshot): bound video description JSON response reads The Moonshot video description endpoint used an unbounded await res.json() to parse the media understanding response. Route through readProviderJsonResponse (16 MiB cap) to match the bound already in place for other media understanding providers (xai, openrouter). AI-assisted. Co-authored-by: Cursor <cursoragent@cursor.com> * test(moonshot): add bounds and malformed-JSON coverage for video description --------- Co-authored-by: Cursor <cursoragent@cursor.com>
…96502) * fix(moonshot): bound video description JSON response reads The Moonshot video description endpoint used an unbounded await res.json() to parse the media understanding response. Route through readProviderJsonResponse (16 MiB cap) to match the bound already in place for other media understanding providers (xai, openrouter). AI-assisted. Co-authored-by: Cursor <cursoragent@cursor.com> * test(moonshot): add bounds and malformed-JSON coverage for video description --------- Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 765d05c)
…96502) * fix(moonshot): bound video description JSON response reads The Moonshot video description endpoint used an unbounded await res.json() to parse the media understanding response. Route through readProviderJsonResponse (16 MiB cap) to match the bound already in place for other media understanding providers (xai, openrouter). AI-assisted. Co-authored-by: Cursor <cursoragent@cursor.com> * test(moonshot): add bounds and malformed-JSON coverage for video description --------- Co-authored-by: Cursor <cursoragent@cursor.com> (cherry picked from commit 765d05c)
What Problem This Solves
The Moonshot video description endpoint (
describeMoonshotVideoinextensions/moonshot/media-understanding-provider.ts) reads its successresponse with an unbounded
await res.json(). The same media understandingpattern was already fixed for xAI STT (
xai/stt.ts) and OpenRouter STT(
openrouter/media-understanding-provider.ts) in the response-limit campaign,but the Moonshot provider was overlooked.
A misbehaving, compromised, or hostile Moonshot endpoint (including a
self-hosted
baseUrloverride) can stream an arbitrarily large JSON body intomemory before parsing, causing memory pressure or a hang on the video description
code path.
This change closes the remaining unbounded surface, making this the Moonshot
media-understanding companion to the
#95103/#95108/#95218response-limit campaign.
Changes
extensions/moonshot/media-understanding-provider.ts:readProviderJsonResponseto the existingopenclaw/plugin-sdk/provider-httpimport(await res.json()) as OpenAiCompatibleVideoPayloadwithreadProviderJsonResponse<OpenAiCompatibleVideoPayload>(res, "Moonshot video description failed")(16 MiB cap)"Moonshot video description failed"(matching theassertOkOrThrowHttpErrorerror prefix on the same code path) so error messages are predictable:"Moonshot video description failed: JSON response exceeds 16777216 bytes"and"Moonshot video description failed: malformed JSON response"extensions/moonshot/media-understanding-provider.test.ts:oversizedJsonResponsehelper (ReadableStream-backed, tracks chunks read and cancel)"bounds successful Moonshot video JSON bodies instead of buffering the whole response": streams 64 × 1 MiB chunks, asserts the call rejects with the cap error,readCount < 64, andwasCanceled() === true"reports malformed Moonshot video JSON with a provider-owned error": feeds"not-json{"body, asserts"Moonshot video description failed: malformed JSON response"Real behavior proof
with no
Content-Lengthand an oversized/never-ending body must not be bufferedwhole; the read must stop at the cap, cancel the stream, and throw a bounded
error — while valid small responses still parse unchanged.
node:httpserver (createServer) boundto
127.0.0.1, streaming a JSON body in 64 KiB chunks with noContent-Length header, driving the real exported
readResponseWithLimithelper (the same helper
readProviderJsonResponsewraps internally) onNode v22.22.0.
GET /hugestreams ~24 MiB with noContent-Length;GET /smallreturns one valid video description response.readResponseWithLimit(response, 1 MiB)against/hugeand assertit rejects + stream is cancelled.
await res.json()path and measure totalbytes pushed.
/smalland assert the payload is parsed intact.Moonshot video description failed: JSON response exceeds 16777216 bytes;server socket closed early; only 1,064,178 bytes reached the wire.
response.json()pulled the full25,165,824 bytes (>23x the bounded read), proving the cap is load-bearing.
ALL PROOF ASSERTIONS PASSED. In-repo test coverageadded: 4 tests pass (including 2 new bounds/malformed-JSON behavior tests).
bytes-on-wire and early socket close, which is the load-bearing signal.
Evidence
Local test run (Node v22.22.0, Vitest v4.1.8):
Label: security
AI-assisted.