Skip to content

fix(chutes-oauth-plugin): bound plugin JSON response reads at 16 MiB#96779

Merged
vincentkoc merged 2 commits into
openclaw:mainfrom
wangmiao0668000666:fix/chutes-oauth-plugin-bounded-read
Jun 29, 2026
Merged

fix(chutes-oauth-plugin): bound plugin JSON response reads at 16 MiB#96779
vincentkoc merged 2 commits into
openclaw:mainfrom
wangmiao0668000666:fix/chutes-oauth-plugin-bounded-read

Conversation

@wangmiao0668000666

@wangmiao0668000666 wangmiao0668000666 commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

extensions/chutes/oauth.ts (the active bundled plugin path, entered via openclaw onboard --auth-choice chutes) has 2 unbounded await response.json() calls (userinfo fetch, token exchange). A hostile Chutes endpoint or proxy can return an oversized JSON body that exhausts process memory during the OAuth login flow.

Changes

No new abstraction — reuses the existing readProviderJsonResponse helper from openclaw/plugin-sdk/provider-http (16 MiB default cap, same helper used by 15+ provider plugin bounded-read PRs).

  • extensions/chutes/oauth.ts:128,167 — replace 2 (await response.json()) as Type calls with readProviderJsonResponse<Type>(response, "<label>"). Respective labels: "Chutes userinfo", "Chutes token exchange".

Design Rationale

Why readProviderJsonResponse instead of response.json()? response.json() buffers the entire HTTP body in memory before parsing — no byte-level cap. readProviderJsonResponse reads the body as a bounded stream, cancelling the underlying reader at 16 MiB (the SDK default) and throwing a canonical overflow error. This is the same pattern used by 15+ bounded-read PRs across the codebase.

Why the same 16 MiB cap for both userinfo and token exchange? Both endpoints serve JSON OAuth payloads of comparable size (typically < 1 KiB). Using the same threshold for both avoids introducing a per-endpoint cap distinction with no operational benefit.

Real behavior proof

  • Behavior addressed: 2 unbounded response.json() calls (userinfo, token exchange) → capped at 16 MiB via readProviderJsonResponse.
  • Real environment tested: real node:http server (127.0.0.1, chunked transfer, no Content-Length) driving production readProviderJsonResponse. Node v22.22.0, Linux x86_64.
  • Exact steps or command run after this patch:
    node --import tsx _proof_chutes_oauth_plugin.mts
  • Evidence after fix:
    === Chutes OAuth plugin JSON bounded-read assertions ===
    
      PASS  hostile body: overflow thrown — Chutes userinfo: JSON response exceeds 16777216 bytes
      PASS  hostile body: cap value — cap=16777216
      PASS  hostile body: bytes ≈ cap — sent ~16.0 MiB (cap=16 MiB)
      PASS  hostile body: not full payload — sent 16.0 MiB of 32 MiB hostile body
      PASS  negative control: small body parses — login=testuser
      PASS  negative control: bytes < cap — 0 KiB
      PASS  happy path: token JSON parses — access_token set
      PASS  happy path: bytes < cap — 0 KiB
    
    Results: 8/8 passed
    
  • Observed result after fix: readProviderJsonResponse threw canonical overflow at 16 MiB (Chutes userinfo: JSON response exceeds 16777216 bytes). Server sent ~16.0 MiB of a 32 MiB hostile payload — capped before full body. Negative control (small userinfo JSON, login=testuser) and happy path (token exchange, access_token set) parsed correctly.
  • What was not tested: live Chutes API call (the proof exercises the same production SDK helper against the same attack shape the live endpoint could carry); cross-platform Node differences (Node 22 only, matches CI).

Out of scope

Risk checklist

  • User-visible behavior change? No — normal-sized responses unaffected; oversized responses now throw a clear overflow error instead of silently accumulating memory.
  • Config/env/migration change? No — 16 MiB is the SDK helper default; no new config options.
  • Security/auth/secrets/network/tool-execution change? Yes — OOM/DoS protection for Chutes OAuth plugin userinfo and token exchange paths.
  • Highest-risk area: accidentally rejecting a legitimate response larger than 16 MiB; covered by the negative control and happy path assertions.

Diff stats

1 file changed, 7 insertions(+), 4 deletions(-)

AI-assisted; reviewed before submission.

Replace 2 unbounded (await response.json()) calls in the active bundled
plugin path (extensions/chutes/oauth.ts) with readProviderJsonResponse.
Sites: fetchChutesUserInfo (userinfo), exchangeChutesCodeForTokens
(token exchange).

Reuses the existing SDK helper already used by 15+ other providers.
@clawsweeper

clawsweeper Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed June 27, 2026, 5:14 AM ET / 09:14 UTC.

Summary
The PR routes Chutes plugin OAuth userinfo and token-exchange success JSON parsing through the shared 16 MiB readProviderJsonResponse helper and adds an oversized token-exchange regression test.

PR surface: Source +3, Tests +63. Total +66 across 2 files.

Reproducibility: yes. source-reproducible: current main and v2026.6.10 still have raw successful response.json() reads in the active Chutes plugin OAuth path. I did not rerun tests because this review is read-only, but the PR body and inline test exercise the after-fix bounded-read behavior.

Review metrics: 1 noteworthy metric.

  • OAuth Success JSON Reads: 2 changed to bounded reads, 0 config/default surfaces added. This names the exact auth success responses whose memory behavior changes without adding operator-facing setup or configuration surface.

Stored data model
Persistent data-model change detected: serialized state: extensions/chutes/oauth.test.ts. Confirm migration or upgrade compatibility proof before merge.

Root-cause cluster
Relationship: canonical
Canonical: #96779
Summary: This PR is the focused active Chutes bundled-plugin OAuth bounded-success-body fix; the deprecated core helper and other provider surfaces are separate sibling hardening work.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • none.

Risk before merge

  • [P1] Merging intentionally changes Chutes OAuth success-body behavior: JSON responses above the shared 16 MiB provider cap now fail closed with an overflow error instead of continuing to buffer, so maintainers should accept that auth-provider behavior.
  • [P1] The contributor proof is strong local Node HTTP/ReadableStream proof plus an inline regression test, but it is not a live Chutes OAuth smoke against api.chutes.ai if maintainers want provider-specific happy-path confirmation.

Maintainer options:

  1. Accept The Shared OAuth JSON Cap (recommended)
    Maintainers can accept the existing 16 MiB provider JSON cap for Chutes token and userinfo success responses and land after exact-head gates pass.
  2. Request A Live Chutes Smoke
    If provider-specific confidence is needed, ask for a redacted live Chutes OAuth happy-path smoke before merge.
  3. Pause For A Different Cap Policy
    If the shared cap is not acceptable for OAuth success responses, pause this PR and choose a Chutes-specific cap or helper policy first.

Next step before merge

  • No automated repair is needed; the remaining action is maintainer acceptance of the shared 16 MiB Chutes OAuth cap and normal exact-head gates.

Security
Cleared: The diff reduces an oversized JSON memory-exhaustion surface and adds no dependencies, scripts, permissions, workflows, or secret-handling surface.

Review details

Best possible solution:

Land this narrow plugin-path hardening after maintainer acceptance of the shared Chutes OAuth JSON cap and exact-head gates; review the deprecated core-helper companion separately.

Do we have a high-confidence way to reproduce the issue?

Yes, source-reproducible: current main and v2026.6.10 still have raw successful response.json() reads in the active Chutes plugin OAuth path. I did not rerun tests because this review is read-only, but the PR body and inline test exercise the after-fix bounded-read behavior.

Is this the best way to solve the issue?

Yes. Reusing readProviderJsonResponse through the plugin SDK facade is the narrowest maintainable fix because the shared helper owns the byte cap, cancellation, and labeled JSON parse errors; a Chutes-local helper would duplicate policy.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against c5d34c8376f8.

Label changes

Label justifications:

  • P2: The PR is a focused Chutes OAuth memory-hardening fix with limited provider-plugin blast radius and no evidence of an active outage.
  • merge-risk: 🚨 auth-provider: The diff changes Chutes OAuth success-response handling to fail closed above the shared provider JSON cap, which can affect auth setup behavior.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes after-fix terminal output from a real Node HTTP chunked oversized-body scenario with quantified cap, negative-control, and happy-path checks, plus an inline regression test through loginChutes.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal output from a real Node HTTP chunked oversized-body scenario with quantified cap, negative-control, and happy-path checks, plus an inline regression test through loginChutes.
Evidence reviewed

PR surface:

Source +3, Tests +63. Total +66 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 7 4 +3
Tests 1 64 1 +63
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 71 5 +66

What I checked:

  • Current main still has unbounded Chutes plugin OAuth success JSON reads: Current main at c5d34c8 still calls raw response.json() for successful Chutes userinfo and token-exchange responses. (extensions/chutes/oauth.ts:125, c5d34c8376f8)
  • Latest release still has the same gap: Release v2026.6.10 still has raw successful response.json() reads at the same Chutes plugin OAuth userinfo and token-exchange sites, so the change is not already shipped. (extensions/chutes/oauth.ts:125, aa69b12d0086)
  • PR implementation uses the shared bounded helper: The PR head imports readProviderJsonResponse from the plugin SDK facade and applies it to both successful Chutes userinfo and token-exchange JSON reads with Chutes-specific labels. (extensions/chutes/oauth.ts:128, d4669d874a95)
  • Shared helper contract enforces the cap: readProviderJsonResponse defaults to the 16 MiB provider JSON cap and delegates to readResponseWithLimit, which cancels a stream when the byte cap is exceeded. (src/agents/provider-http-errors.ts:312, c5d34c8376f8)
  • SDK boundary is respected: openclaw/plugin-sdk/provider-http already exports readProviderJsonResponse, matching the extensions/AGENTS.md rule that bundled plugin production code should import through SDK subpaths rather than core internals. (src/plugin-sdk/provider-http.ts:16, c5d34c8376f8)
  • Active auth entry point uses the touched helper: The Chutes provider plugin auth method calls loginChutes, so the changed OAuth helper is on the active openclaw onboard --auth-choice chutes plugin path documented for users. (extensions/chutes/index.ts:71, c5d34c8376f8)

Likely related people:

  • steipete: Live path history shows this account landed the bundled Chutes extension into current main and later touched Chutes OAuth/token lifetime and provider documentation surfaces. (role: feature owner and recent area contributor; confidence: high; commits: a724bbce1a63, 8c0aaee88237, 5a294cb2bdf7; files: extensions/chutes/oauth.ts, extensions/chutes/index.ts, docs/providers/chutes.md)
  • vincentkoc: Live path history shows this account recently bounded Chutes OAuth token error bodies in the same plugin OAuth file, close to the success-body read surface changed here. (role: recent hardening contributor; confidence: high; commits: 6fc0303ec04b; files: extensions/chutes/oauth.ts, extensions/chutes/oauth.test.ts)
  • Alix-007: The shared readProviderJsonResponse bounded JSON helper used by this PR was introduced in the merged provider JSON response hardening PR. (role: shared helper introducer; confidence: medium; commits: 2592f8a51a4e; files: src/agents/provider-http-errors.ts, src/agents/provider-http-errors.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

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 keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. labels Jun 25, 2026
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

Upgraded proof: 8 assertions with quantified bytes-sent metrics (hostile body capped at ~16.0 MiB of 32 MiB payload). Added AI disclosure. Marked sibling PR #96777 as companion.

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@clawsweeper clawsweeper Bot added the merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. label Jun 26, 2026
@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

Added inline bounded-read regression test (oversized token exchange JSON via ReadableStream, verifies stream cancelled early with bytes-pulled < cap, mock fetchFn pattern matching existing tests).

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

Added inline bounded-read regression test (oversized token exchange JSON via ReadableStream, verifies stream cancelled early with bytes-pulled < cap, mock fetchFn pattern matching existing tests).

@clawsweeper re-review

@wangmiao0668000666

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

🦞 upgrades applied (PR body only):

  1. 🦞fix: send plain string message in tau-rpc instead of structured object #25: Design Rationale — 2 WHYs (readProviderJsonResponse, same cap for both endpoints)
  2. Telegram integration #16: Risk checklist format
  3. Heartbeat skipped when last inbound message is from a group chat #20: No new abstraction declared

No code changes.

@vincentkoc
vincentkoc merged commit 5723222 into openclaw:main Jun 29, 2026
100 of 102 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: chutes merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants