fix(image-gen): bound image generation provider JSON response reads#96495
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 26, 2026, 6:48 AM ET / 10:48 UTC. Summary PR surface: Source +151, Tests +251, Generated 0, Other 0. Total +402 across 22 files. Reproducibility: yes. at source and proof level: current main uses raw Review metrics: 2 noteworthy metrics.
Stored data model 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 this branch or a maintainer-equivalent after accepting the image-sized cap policy, with overlapping fal and MiniMax follow-ups rebased or narrowed around the canonical response-read changes. Do we have a high-confidence way to reproduce the issue? Yes, at source and proof level: current main uses raw Is this the best way to solve the issue? Yes, this is the best implementation layer. Reusing AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against c05d0d5bbfb0. Label changesLabel justifications:
Evidence reviewedPR surface: Source +151, Tests +251, Generated 0, Other 0. Total +402 across 22 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
|
69f8599 to
cb5b4b2
Compare
|
Maintainer update: I pushed a signed-off follow-up commit and rebased this branch onto latest The follow-up keeps the bounded JSON reader, but uses an image-sized cap for inline base64 image JSON responses based on the existing image payload cap and provider output count. Fal/Vydra stay on the generic JSON cap because those responses are metadata/job URLs. I also updated the affected OpenAI/Google test fixtures to use real Local proof on the rebased branch:
CI should rerun on |
Route success JSON reads through readProviderJsonResponse (16 MiB cap) in openrouter, google, fal, minimax, openai, and vydra image generation providers to prevent OOM from oversized or hostile endpoint responses. Mirrors the response-limit campaign already applied to other provider paths. AI-assisted. Co-authored-by: Cursor <[email protected]>
cb5b4b2 to
0cd677c
Compare
Signed-off-by: sallyom <[email protected]>
0cd677c to
9490c8a
Compare
|
Merge-ready from my review: autoreview is clean, CI is green, and this is the best narrow fix for bounding inline image-generation JSON responses. The cap sizing is acceptable, and overlapping PRs should rebase to pick up the image-generation limits from this PR rather than duplicating the fix. |
…penclaw#96495) * fix(image-gen): bound image generation provider JSON response reads Route success JSON reads through readProviderJsonResponse (16 MiB cap) in openrouter, google, fal, minimax, openai, and vydra image generation providers to prevent OOM from oversized or hostile endpoint responses. Mirrors the response-limit campaign already applied to other provider paths. AI-assisted. Co-authored-by: Cursor <[email protected]> * fix(image-gen): size bounded JSON caps for inline image payloads Signed-off-by: sallyom <[email protected]> --------- Signed-off-by: sallyom <[email protected]> Co-authored-by: Cursor <[email protected]> Co-authored-by: sallyom <[email protected]>
…penclaw#96495) * fix(image-gen): bound image generation provider JSON response reads Route success JSON reads through readProviderJsonResponse (16 MiB cap) in openrouter, google, fal, minimax, openai, and vydra image generation providers to prevent OOM from oversized or hostile endpoint responses. Mirrors the response-limit campaign already applied to other provider paths. AI-assisted. Co-authored-by: Cursor <[email protected]> * fix(image-gen): size bounded JSON caps for inline image payloads Signed-off-by: sallyom <[email protected]> --------- Signed-off-by: sallyom <[email protected]> Co-authored-by: Cursor <[email protected]> Co-authored-by: sallyom <[email protected]>
… OOM Replace unbounded `response.json()` in `callUserTokenService` with `readProviderJsonResponse(response, "msteams.sso-user-token")` — the same shared bounded reader already deployed across the codebase (openclaw#95218, openclaw#96322, openclaw#96495, openclaw#96889). Enforces a 16 MiB default cap and cancels the underlying stream on overflow. Closes the last unbounded JSON read on the MSTeams SSO token-exchange path. Error responses were already bounded via `extractProviderErrorDetail`. Co-Authored-By: Claude <[email protected]>
What Problem This Solves
Six image generation providers parse their success responses with an unbounded
await response.json().response.json()reads the entire body into memorybefore parsing, with no byte ceiling and regardless of (or in the absence of) a
Content-Lengthheader. Image generation providers are external, untrustedsources: a misbehaving, compromised, or hostile endpoint (including a
self-hosted
baseUrloverride) can stream an arbitrarily large or never-endingJSON body and force the gateway/plugin to buffer it all, causing memory pressure
or a hang on the image generation code path.
The affected call sites are:
extensions/openrouter/image-generation-provider.ts— OpenRouter image generation resultextensions/google/image-generation-provider.ts— Google Imagen image generation resultextensions/fal/image-generation-provider.ts— fal image generation resultextensions/minimax/image-generation-provider.ts— MiniMax image generation resultextensions/openai/image-generation-provider.ts— OpenAI image generation/edit resultextensions/vydra/image-generation-provider.ts— Vydra image job submission resultThis change closes all six unbounded surfaces, making this the image-generation
companion to the
#95103/#95108/#95218response-limit campaign.Changes
through the shared bounded reader
readProviderJsonResponse(fromopenclaw/plugin-sdk/provider-http), which enforces a 16 MiB cap andcalls the shared
readResponseWithLimithelper internally.error (
<label>: JSON response exceeds <N> bytes); existingmalformed-JSON error messages are preserved for backward compatibility.
media-corebounded reader alreadyused across the codebase.
openaiandopenrouter) that declare a closedvi.mock("openclaw/plugin-sdk/provider-http")factory receive a pass-throughmock for
readProviderJsonResponseso existing response-fixture tests remainvalid; bounded-reader enforcement is covered by the existing
media-coreunittests.
Real behavior proof
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 helperreadProviderJsonResponsewraps internally) on Node v22.22.0.
GET /hugestreams an unbounded JSON object (~24 MiB) withno
Content-Length;GET /smallreturns one valid result.readResponseWithLimit(response, 1 MiB)against/hugeand assertit rejects + the server socket is closed early.
await response.json()against thesame
/hugebody and measure how many bytes the server pushed.readResponseWithLimit(response, 1 MiB)against/smalland assertthe result is parsed intact.
xai.video-generation: JSON response exceeds 1048576 bytes;server socket closed early (stream cancelled); only 1,072,370 bytes
reached the wire — near the 1 MiB cap, not the 24 MiB body.
response.json()read pulled the full25,165,824 bytes (>23x the bounded read), proving the cap is
load-bearing.
no truncation.
ALL PROOF ASSERTIONS PASSED. Plus the in-repo Vitestsuite for the two providers with closed mocks passes — 63/63 for openai
image-generation and 7/7 for openrouter image-generation — including
existing generation/edit fixture tests and the Azure OpenAI auth path.
oxlintis clean on changed files.reproduce a hostile oversized body). The 64-bit memory-exhaustion end state
was not driven to OOM — the proof instead measures bytes-on-wire and early
socket close, which is the load-bearing signal.
Evidence
Label: security
AI-assisted.