Skip to content

fix(google): bound OAuth project and token JSON response reads#97587

Merged
sallyom merged 1 commit into
openclaw:mainfrom
hugenshen:fix/bound-google-oauth-json-responses
Jul 6, 2026
Merged

fix(google): bound OAuth project and token JSON response reads#97587
sallyom merged 1 commit into
openclaw:mainfrom
hugenshen:fix/bound-google-oauth-json-responses

Conversation

@hugenshen

@hugenshen hugenshen commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

extensions/google/oauth.project.ts and extensions/google/oauth.token.ts contained six
unbounded await response.json() calls on the Gemini CLI OAuth onboarding and token-exchange
paths (getUserEmail, pollOperation, loadCodeAssist success + error, onboardUser,
requestTokenGrant).

After fetchWithTimeout materialises the response body, response.json() can trigger a
second large allocation during JSON.parse. Replacing these calls with
readProviderJsonResponse caps the JSON parse step at 16 MiB with labeled errors at each
OAuth call site.

This complements the shared fetch boundary landed in #97628
(readResponseWithLimit inside fetchWithTimeout). Together the two layers give defense in
depth:

  1. Fetch boundary (fix(google): bound google OAuth fetchWithTimeout arrayBuffer at 16 MiB #97628 on main): bounds upstream stream reads before buffering.
  2. Parse boundary (this PR): bounds JSON.parse on the buffered Response objects that
    OAuth call sites consume.

Changes

  • extensions/google/oauth.project.ts:

    • imports readProviderJsonResponse from openclaw/plugin-sdk/provider-http
    • replaces six response.json() reads with labeled readProviderJsonResponse(...) calls
      (google.userinfo, google.poll-operation, google.load-code-assist,
      google.onboard-user)
  • extensions/google/oauth.token.ts:

    • imports readProviderJsonResponse from openclaw/plugin-sdk/provider-http
    • replaces await response.json()readProviderJsonResponse(response, "google.token")
      in requestTokenGrant
  • extensions/google/oauth.test.ts:

    • adds four regression tests:
      • rejects an oversized token exchange response body (end-to-end via
        exchangeCodeForTokens; fetch cap fires first after fix(google): bound google OAuth fetchWithTimeout arrayBuffer at 16 MiB #97628)
      • rejects an oversized token body at the JSON parse boundary (defense-in-depth when
        fetchWithTimeout already returned a buffered oversized body)
      • rejects an oversized loadCodeAssist success response body (via
        resolveGoogleOAuthIdentity)
      • swallows bound error on oversized userinfo body and returns undefined email

Rebased onto current main; the stale plugin SDK surface budget commit was dropped because
#97628 and other main commits already carry those values.

Real behavior proof

Behavior addressed: Oversized or hostile Google OAuth JSON responses fail closed instead
of triggering unbounded JSON.parse allocation on buffered bodies returned by
fetchWithTimeout.

Real environment tested: Node v22, macOS 24.3.0, node:http streaming server on
127.0.0.1, built helpers from dist/plugin-sdk/provider-http.js and
dist/plugin-sdk/response-limit-runtime.js.

Exact steps:

  1. Stream an ~18 MiB OAuth token JSON body from a local HTTP server (no Content-Length).
  2. Drive readResponseWithLimit at the fetch boundary → assert labeled overflow error.
  3. Drive readProviderJsonResponse on the same streamed body → assert labeled parse cap.
  4. Negative control: unbounded response.body reader consumes the full ~18 MiB stream.
  5. Verify post-fetchWithTimeout buffered Response shape rejects at parse boundary.
  6. Verify normal token / userinfo / loadCodeAssist bodies still parse.
  7. Verify VPC-SC error-path .catch(() => null) swallows oversized error bodies.
  8. Run OAuth module regression tests through exchangeCodeForTokens and
    resolveGoogleOAuthIdentity.

Observed result:

  • Fetch boundary: google HTTP fetch: body exceeds 16777216 bytes (got 16817603)
  • Parse boundary (stream): google.token: JSON response exceeds 16777216 bytes
  • Negative control: unbounded reader consumed 18 874 405 bytes (~18 MiB)
  • Buffered oversized body (16 778 297 bytes) rejected before JSON.parse
  • Normal token / userinfo / loadCodeAssist responses parse correctly
  • VPC-SC error-path .catch(() => null) returns null on oversized body
  • ALL PROOF ASSERTIONS PASSED

What was not tested: live Google OAuth API calls.

Evidence

=== Google OAuth response bound proof (path-level) ===
Layers: (1) fetchWithTimeout readResponseWithLimit [#97628 on main]
        (2) readProviderJsonResponse on OAuth call sites [#97587]

--- Case 1: oversized streamed token body (fetch boundary) ---
  ok: readResponseWithLimit rejects oversized streamed OAuth body
  ok: fetch-boundary error is labeled (got: google HTTP fetch: body exceeds 16777216 bytes (got 16817603))
  ok: negative control: unbounded reader consumed full ~18 MiB stream (18874405 bytes)

--- Case 2: oversized streamed body rejected at JSON parse boundary ---
  ok: readProviderJsonResponse rejects oversized streamed body
  ok: parse-boundary label present (got: google.token: JSON response exceeds 16777216 bytes)

--- Case 3: oversized buffered OAuth token body (post-fetch shape) ---
  ok: buffered oversized body rejected before JSON.parse
  ok: body size 16778297 bytes exceeds 16777216 cap

--- Case 4: normal OAuth JSON responses still parse ---
  ok: token body parses
  ok: userinfo body parses
  ok: loadCodeAssist body parses

--- Case 5: loadCodeAssist error path .catch(() => null) ---
  ok: oversized VPC-SC probe body returns null via .catch

--- Case 6: OAuth module path-level regression tests ---
  Tests  4 passed | 27 skipped (31)
  ok: vitest OAuth bound regression tests passed

ALL PROOF ASSERTIONS PASSED
OPENCLAW_VITEST_MAX_WORKERS=1 node scripts/run-vitest.mjs extensions/google/oauth.test.ts \
  -t "rejects an oversized|swallows bound"
# Tests  4 passed | 27 skipped (31)

New bound regression tests (4 added):

  • loginGeminiCliOAuth > rejects an oversized token exchange response body
  • loginGeminiCliOAuth > rejects an oversized token body at the JSON parse boundary
  • loginGeminiCliOAuth > rejects an oversized loadCodeAssist success response body
  • loginGeminiCliOAuth > swallows bound error on oversized userinfo body and returns undefined email

@clawsweeper

clawsweeper Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 4, 2026, 10:13 PM ET / 02:13 UTC.

Summary
Replaces Google Gemini CLI OAuth project/token JSON response parsing with readProviderJsonResponse and adds oversized-response regression coverage.

PR surface: Source +7, Tests +134. Total +141 across 3 files.

Reproducibility: yes. Source inspection shows raw response.json() parser sites on current main, and the PR body/diff includes oversized Response paths plus focused regression tests; I did not execute tests because this review is read-only.

Review metrics: 1 noteworthy metric.

  • Google OAuth JSON parser call sites: 6 replaced with bounded helper. Each changed reader sits on token, identity, project discovery, or onboarding auth-provider paths, so maintainers should explicitly accept the parser-boundary behavior change.

Root-cause cluster
Relationship: canonical
Canonical: #97587
Summary: This PR is the viable current landing path for Gemini CLI Google OAuth parser-boundary hardening; related items cover adjacent transport/error caps or broader overlapping 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 Google OAuth token/project JSON failure behavior: oversized provider JSON now fails through labeled bounded-helper errors, while userinfo continues to swallow that failure and return no email.
  • [P1] Several overlapping Google OAuth hardening PRs remain open, so maintainers should choose whether this narrow parser-boundary branch or a broader bundle is the canonical landing path.

Maintainer options:

  1. Accept bounded parser failures (recommended)
    Maintainers can accept the new labeled fail-closed JSON parser behavior as intended Google OAuth hardening and land this branch after normal required checks.
  2. Pause for a broader Google OAuth bundle
    If maintainers want Gemini CLI OAuth and Vertex ADC hardening reviewed together, pause this PR and choose a consolidated branch with equivalent parser coverage and proof.

Next step before merge

  • No automated repair is needed; the remaining action is maintainer acceptance of the auth-provider parser failure behavior and canonical choice among overlapping PRs.

Security
Cleared: No concrete security or supply-chain regression is visible; the diff uses existing SDK bounded-read helpers and does not change dependencies, workflows, permissions, or secret handling.

Review details

Best possible solution:

Land this narrow parser-boundary hardening once maintainers accept the auth-provider fail-closed behavior, and review broader Vertex ADC hardening separately unless they choose a consolidated Google OAuth bundle.

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

Yes. Source inspection shows raw response.json() parser sites on current main, and the PR body/diff includes oversized Response paths plus focused regression tests; I did not execute tests because this review is read-only.

Is this the best way to solve the issue?

Yes. With the shared fetch cap and token-error cap already on main, replacing the remaining parser sites with the existing SDK bounded JSON helper is the narrowest maintainable owner-boundary fix.

AGENTS.md: found and applied where relevant.

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

Label changes

Label justifications:

  • P2: This is a focused Google OAuth hardening fix with limited blast radius and no evidence of an active user outage.
  • merge-risk: 🚨 auth-provider: The diff changes Google OAuth token/project JSON parsing and oversized or malformed response failure behavior on auth and onboarding paths.
  • 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 proof with a local streaming HTTP server, bounded helper checks, a negative control, normal-response controls, and focused OAuth regression-test output.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes after-fix terminal proof with a local streaming HTTP server, bounded helper checks, a negative control, normal-response controls, and focused OAuth regression-test output.
Evidence reviewed

PR surface:

Source +7, Tests +134. Total +141 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 2 17 10 +7
Tests 1 134 0 +134
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 151 10 +141

What I checked:

Likely related people:

  • steipete: The current split Google OAuth project/token/http module structure appears to come from refactor(google): split oauth flow modules. (role: original OAuth module contributor; confidence: high; commits: 92e765cdee15; files: extensions/google/oauth.project.ts, extensions/google/oauth.token.ts, extensions/google/oauth.http.ts)
  • wangmiao0668000666: Authored the merged shared Google OAuth fetchWithTimeout response-body cap that this parser-boundary PR complements. (role: recent adjacent fetch-boundary contributor; confidence: high; commits: a6aaba76ac66; files: extensions/google/oauth.http.ts, extensions/google/oauth.http.test.ts)
  • mushuiyu886: Authored the recently merged bounded token error-body change in the same Google OAuth token helper and tests. (role: recent adjacent token-error contributor; confidence: medium; commits: 6207a4e75bae; files: extensions/google/oauth.token.ts, extensions/google/oauth.test.ts)
  • vincentkoc: Prior merged work added Gemini CLI personal OAuth support in the same project/token OAuth surface. (role: adjacent OAuth behavior contributor; confidence: medium; commits: f02e43518826; files: extensions/google/oauth.project.ts, extensions/google/oauth.token.ts, extensions/google/oauth.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.
Review history (4 earlier review cycles)
  • reviewed 2026-07-02T15:08:04.372Z sha 09fef5a :: needs maintainer review before merge. :: none
  • reviewed 2026-07-04T14:10:37.598Z sha d7da56d :: needs maintainer review before merge. :: none
  • reviewed 2026-07-04T16:27:43.013Z sha 63570bb :: needs maintainer review before merge. :: none
  • reviewed 2026-07-04T16:33:39.025Z sha 63570bb :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jun 28, 2026
@clawsweeper clawsweeper Bot added the merge-risk: 🚨 auth-provider 🚨 May break OAuth, tokens, provider routing, model choice, or credentials. label Jun 28, 2026
@hugenshen
hugenshen force-pushed the fix/bound-google-oauth-json-responses branch from 1a7d1ff to 0bb0e96 Compare June 29, 2026 00:45
@openclaw-barnacle openclaw-barnacle Bot added the scripts Repository scripts label Jun 29, 2026
@clawsweeper clawsweeper Bot added merge-risk: 🚨 automation 🚨 May affect CI, automerge, proof capture, label sync, or maintainer automation. and removed merge-risk: 🚨 automation 🚨 May affect CI, automerge, proof capture, label sync, or maintainer automation. labels Jun 29, 2026
@hugenshen
hugenshen force-pushed the fix/bound-google-oauth-json-responses branch from 0bb0e96 to 09fef5a Compare July 2, 2026 14:47
@openclaw-barnacle openclaw-barnacle Bot removed the scripts Repository scripts label Jul 2, 2026
@hugenshen

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 2, 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 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. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jul 2, 2026
@hugenshen
hugenshen force-pushed the fix/bound-google-oauth-json-responses branch from 09fef5a to d7da56d Compare July 4, 2026 14:03
@hugenshen
hugenshen force-pushed the fix/bound-google-oauth-json-responses branch from d7da56d to 63570bb Compare July 4, 2026 16:23
@hugenshen

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Jul 5, 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.

@sallyom sallyom self-assigned this Jul 6, 2026
@sallyom

sallyom commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Merge-ready from local maintainer review. Autoreview is clean at head SHA 63570bb, CI is green, and I found no compatibility concerns or breaking changes. This is the best narrow fix for the remaining Google OAuth JSON parse-boundary gap: it uses the existing 16 MiB provider JSON cap at the owning call sites while preserving normal OAuth behavior.

@sallyom
sallyom merged commit 2e967ea into openclaw:main Jul 6, 2026
88 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 7, 2026
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

extensions: google 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