fix(qwen): bound video description success response reads#96604
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 24, 2026, 11:20 PM ET / 03:20 UTC. Summary PR surface: Source +8, Tests +71. Total +79 across 2 files. Reproducibility: yes. source inspection gives a high-confidence path: current main and Review metrics: 1 noteworthy metric.
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 plugin-local bounded-reader change if maintainers accept the shared 16 MiB provider JSON boundary; otherwise adjust the Qwen limit with focused tests before merge. Do we have a high-confidence way to reproduce the issue? Yes, source inspection gives a high-confidence path: current main and Is this the best way to solve the issue? Yes, this is the best code shape if maintainers accept the cap policy: reusing AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 643410c1f3c0. Label changesLabel justifications:
Evidence reviewedPR surface: Source +8, Tests +71. Total +79 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
|
|
@clawsweeper review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
bd4fd98 to
81da8da
Compare
|
@clawsweeper review |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
Maintainer local review: merge-ready provided green CI, reviewed as a batch:
Autoreview is clean, the 16 MiB cap is acceptable for these provider JSON/status/error/usage bodies, and the changes follow the established shared bounded-reader pattern. This is the best narrow fix for these call sites: no new SDK/API/config surface, no upgrade/backward-compatibility concern beyond the intended oversized-body failure mode, and no expected breaking behavior for normal provider responses. |
What Problem This Solves
On the success path,
describeQwenVideo(
extensions/qwen/media-understanding-provider.ts) read the video-descriptionresponse with an unbounded
await res.json().res.json()buffers the entirebody into memory before parsing — no byte ceiling, with or without a
Content-Lengthheader. The Qwen OpenAI-compatible endpoint is untrusted externalinput: a hostile, misconfigured, or SSRF-reachable
baseUrlcan stream anarbitrarily large or never-ending JSON body and force the runtime to buffer it
all, causing memory pressure or a hang on the media-understanding path. This is
the success-JSON-side companion to the
#95103/#95108response-limit campaign(and to the already-merged
#95218/#96027/#96038bounded-reader PRs).Changes
readProviderJsonResponse(16 MiB cap, re-exported viaopenclaw/plugin-sdk/provider-http), thenJSON.parsethe decoded prefix —the exact pattern merged in
#96027/#96038. Plugin-scoped, no newabstraction.
(
Qwen video description failed: JSON response exceeds 16777216 bytes).Qwen video description failed: malformed JSON response(previously a rawSyntaxError); the OpenAI-compatible payload extraction is unchanged.Real behavior proof
with no
Content-Lengthand an oversized / never-ending body must not bebuffered whole; the read must stop at the 16 MiB 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 1 MiB chunks with noContent-Lengthheader (would total ~64 MiB, 4× the cap), driving the realexported
describeQwenVideoover a real loopback socket — through thereal
postJsonRequest/ SSRF-guarded undici dispatcher and the realreadProviderJsonResponsereader. Loopback is allowed via the provider's ownrequest: { allowPrivateNetwork: true }override (the same mechanism Ollamauses via
buildOllamaBaseUrlSsrFPolicy). Node v22.22.0. No mock fetch, noin-process
ReadableStreamfixture — the bytes cross a real socket.node --import tsx __qwen_bound_proof.mts— start thestreaming server → drive the real
describeQwenVideoagainst it and assert itrejects with the bounded error + the server observed an early socket abort
(stream cancelled) with bytes-on-wire near the cap → run a negative control
(old unbounded
fetch().arrayBuffer()of the same endpoint) → drive the realfunction against a second server returning a small valid payload.
Qwen video description failed: JSON response exceeds 16777216 bytes; the server saw the socket aborted after 18,874,369 bytes(≈18 MiB, ≪ the ~64 MiB it would have sent), confirming the stream was
cancelled, not drained.
67,108,866 bytes (>3.5× the bounded read), proving the cap is load-bearing
(without it the body keeps accumulating past 16 MiB).
would not reproduce a hostile oversized body); the proof reproduces the exact
untrusted-body transport shape with a local streaming server over a real
socket. The proof drives the bytes-on-wire + early-socket-close signal (the
load-bearing signal) rather than driving the runtime to actual OOM. The proof
script is not committed.
Evidence
Real
node:httpterminal proof (real loopback socket, real exporteddescribeQwenVideo, real SSRF dispatcher +readProviderJsonResponse):In-repo Vitest suite (bounded-overflow streaming regression + malformed-JSON +
the unchanged happy-path payload test) passes 3/3:
Checks on the changed files:
node scripts/run-vitest.mjs extensions/qwen/media-understanding-provider.test.ts --run→ 3/3 passedtsgo(extensions test project) → clean onextensions/qwen. This revisionalso fixes the two new
describeQwenVideotest calls that were missing therequired
fileName/timeoutMsfields ofVideoDescriptionRequest(
error TS2739), which would otherwise failcheck-test-types.oxlint extensions/qwen/media-understanding-provider.ts extensions/qwen/media-understanding-provider.test.ts→ exit 0, cleanLabel: security
AI-assisted.