fix(image): bound image-generation provider response reads#96893
fix(image): bound image-generation provider response reads#96893Alix-007 wants to merge 1 commit into
Conversation
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close as superseded: the same image-generation bounded-read work is now covered by a broader open canonical PR with maintainer follow-up, proof-sufficient status, and the same affected files plus additional image providers. Root-cause cluster Members:
Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything. Canonical path: Close this duplicate branch and concentrate review on #96495 as the canonical bounded image-generation response-read landing path. So I’m closing this here and keeping the remaining discussion on #96495. Review detailsBest possible solution: Close this duplicate branch and concentrate review on #96495 as the canonical bounded image-generation response-read landing path. Do we have a high-confidence way to reproduce the issue? Yes, at source level: current main still has raw success-path response.json() reads on the three provider-controlled image response bodies. I did not execute a live oversized-response repro in this read-only review. Is this the best way to solve the issue? No for this PR as the landing branch: the bounded-reader direction is sound, but #96495 already covers the same files with broader image-provider coverage, maintainer follow-up, and proof-sufficient status. Security review: Security review cleared: The diff reduces untrusted provider-response memory exposure and does not add dependency, workflow, lockfile, permission, script, credential, or publishing changes. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against c05d0d5bbfb0. |
Replace unbounded success-path `await response.json()` reads with the shared `readProviderJsonResponse` helper (cancels the stream on overflow) in three image-generation providers: - src/image-generation/openai-compatible-image-provider.ts (shared factory backing DeepInfra/LiteLLM/xAI) - extensions/openai/image-generation-provider.ts (OpenAI direct JSON) - extensions/microsoft-foundry/image-generation-provider.ts (MAI image JSON) A hostile or malfunctioning provider could otherwise stream an arbitrarily large untrusted body (image responses carry base64 payloads) and force the runtime to buffer the whole thing before parsing. The error path already used the bounded reader; this brings the success path in line. Pass an image-sized 64 MiB cap (`maxBytes`) instead of the generic 16 MiB provider-JSON default. Inline `b64_json` image responses with multiple results / high resolution can legitimately exceed 16 MiB, so the generic default would have failed valid responses closed and regressed existing users. 64 MiB matches the OpenAI Codex image read budget (`MAX_CODEX_IMAGE_SSE_BYTES` / `MAX_CODEX_IMAGE_BASE64_CHARS`) in the same module. Update the affected provider tests: the closed provider-http mocks now pass through the real `readProviderJsonResponse` (importActual) and serve real streamed `Response` bodies, plus focused coverage for a valid large inline payload that parses under the cap and an oversized body that is rejected at the cap.
ca747a4 to
6d26977
Compare
|
Thanks for the fix and the clear proof. This is now superseded by #96495, which has merged to #96495 also carries the broader sibling-provider coverage and SDK surface updates, so closing this PR as superseded. |
What Problem This Solves
On the success path, three image-generation providers read the untrusted
provider response body with an unbounded
await response.json(). A hostile ormalfunctioning provider can stream an arbitrarily large body (image responses
routinely carry base64
b64_jsonpayloads), forcing the runtime to buffer thewhole thing into memory before parsing — an OOM / hang vector. The error path
already routes through a bounded reader; only the success path was unbounded.
The unbounded reads were:
src/image-generation/openai-compatible-image-provider.ts:272— the sharedOpenAI-compatible factory that backs the DeepInfra, LiteLLM, and xAI image
providers.
extensions/openai/image-generation-provider.ts:1015— OpenAI directgenerate/edit JSON response.
extensions/microsoft-foundry/image-generation-provider.ts:371— MicrosoftFoundry MAI image generate/edit JSON response.
(The OpenAI Codex SSE path at line 493 already caps its read and is left
untouched.)
Changes
Replace each success-path
await response.json()with the sharedreadProviderJsonResponsehelper (openclaw/plugin-sdk/provider-http), whichreads through
readResponseWithLimitunder the existing 16 MiB(
16777216-byte) cap and cancels the stream on overflow. No new abstraction isintroduced; the change is confined to the three provider modules and reuses the
same bounded reader already used by binary/error responses. Malformed-JSON and
empty-response handling is preserved.
Real behavior proof
body on three image-generation providers.
node:httpTCP server streaming aloopback server, invoke
generateImage, and assert the call rejects withexceeds 16777216 bytes, the server socket is aborted, and the bytes theserver managed to send are far below the full body. A negative control fetches
the same body with raw
response.json()to show it WOULD buffer the fullpayload. Happy-path checks send a small valid JSON image response and assert
it still parses.
aborted after ~16 MiB (
bytesSent=16842774,full=17825792); the negativecontrol buffered the full
17825792bytes; all three happy paths parsed asmall image normally. Full terminal output below.
Azure Foundry endpoints (the providers are exercised against a local TCP
server). The multipart edit branches share the same post-response bounded read
as the generate branches proven here.
Evidence
Label: security
AI-assisted.