fix(plugin-sdk): bound live model catalog success body#95246
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 21, 2026, 7:41 AM ET / 11:41 UTC. Summary PR surface: Source +24, Tests +85. Total +109 across 2 files. Reproducibility: yes. Current main calls Review metrics: 1 noteworthy metric.
Stored data model Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the bounded success-body read after maintainers accept 4 MiB as the SDK live-catalog limit, or adjust/document the cap before merge if they want a different public contract. Do we have a high-confidence way to reproduce the issue? Yes. Current main calls Is this the best way to solve the issue? Yes, with a maintainer-owned compatibility choice. Reusing AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against d1cbe29f3d52. Label changesLabel justifications:
Evidence reviewedPR surface: Source +24, Tests +85. Total +109 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
|
|
Land-ready verification for Maintainer improvements:
Proof:
Known proof gaps: none. |
8748987 to
80805ad
Compare
Dependency graph guard clearedThis PR no longer has blocked dependency graph changes. A future dependency graph change requires a fresh
|
|
Closed via the maintainer replacement #95827, landed in The original fork branch could not be safely brought forward after its large branch-sync operation failed, so I recreated the reviewed patch as one maintainer commit and preserved @Alix-007 with a Thank you, @Alix-007. For future PRs, keeping Allow edits by maintainers enabled helps us apply review fixups directly when GitHub's branch synchronization path permits it. Canonical landed PR: #95827 |
|
Thanks for the heads-up, @steipete! Confirmed — all my open PRs now have Allow edits by maintainers enabled, so you can apply review fixups directly from here on. Appreciate you landing this one cleanly and recreating it 🙏 |
Summary
fetchLiveProviderModelRowsreads a live model catalog from a provider-controlled, runtime-fetched endpoint — the response body is untrusted just like an error body. The error path was already hardened (cancelUnreadResponseBodyonly runs on!response.ok), but the success path at the JSON read didawait response.json()with no ceiling. A faulty or hostile provider can stream an effectively unbounded JSON document, so a single discovery call can exhaust process memory. This is the symmetric counterpart to the bounded-stream hardening landed for Anthropic error streams in #95108.This patch reads the success body through the canonical byte-cap reader (
readResponseWithLimit) beforeJSON.parse, cancelling the stream on overflow or idle stall.Changes
src/plugin-sdk/provider-catalog-live-runtime.tsreadResponseWithLimitfrom@openclaw/media-core/read-response-with-limit(same helper used byprovider-http-errors,clawhub, media fetch).LIVE_MODEL_CATALOG_BODY_MAX_BYTES = 4 * 1024 * 1024with a comment explaining the untrusted-runtime-body rationale.readLiveModelCatalogJson(response, timeoutMs)that reads the body under the byte cap (throwing a clear overflow error) and idle-stall timeout (reusing the call's existingtimeoutMs), thenJSON.parses the bounded buffer.await response.json()toawait readLiveModelCatalogJson(response, timeoutMs).src/plugin-sdk/provider-catalog-live-runtime.test.tsCap rationale (why 4 MiB, not the 8 KiB used in #95108): 8 KiB is an error-snippet budget — correct for tiny provider error messages, but it would silently break legitimate catalogs (OpenRouter's live
/v1/modelsis already >100 KB and grows; OpenAI's is tens of KB). The repo's existing model-catalog precedent caps at 1 MiB (QA_CHILD_STDOUT_MAX_BYTES); 4 MiB gives generous headroom for the largest known catalogs while still bounding memory, which is the actual vulnerability being closed. An overflow degrades gracefully — direct callers (buildLiveModelProviderConfigand the per-provider discovery wrappers) alreadytry/catchand fall back to static catalogs.Real behavior proof
Label: success-path catalog body bounding
response.json(); the success stream was never cancelled, so a provider streaming an unbounded JSON document could exhaust process memory.node --import tsxdriving the real exportedfetchLiveProviderModelRowsread path against aResponsewrapping aReadableStreamthat emits a valid JSON array prefix then an effectively endless run of 64 KiB padding chunks; the stream is instrumented to count bytes actually pulled and whether itscancel()fires. Plusviteston the file andoxlint/tsc.fetchLiveProviderModelRowswith the oversized stream and areleasespy.await response.json()and re-run the same harness.node scripts/run-vitest.mjs src/plugin-sdk/provider-catalog-live-runtime.test.ts --runnpx oxlint <changed files>andtsc -p tsconfig.json --noEmit.await response.json()):Test Files 1 passed (1) / Tests 9 passed (9)(7 pre-existing + 2 new). oxlint exit 0; tsc reports noerror TSfor the changed file.timeoutMs.AI-assisted: implemented and verified with AI assistance; the bounded-stream approach mirrors the symmetric error-body hardening in #95108 and reuses the canonical
readResponseWithLimithelper rather than introducing a new abstraction.