fix(copilot): redact OAuth error response body in fetchJson error messages#102953
Conversation
…sages Replace raw response body text with bounded, redacted structured error detail extracted via extractProviderErrorDetail so OAuth error responses containing tokens, device codes, or other sensitive fields are not leaked into Error messages and downstream logs.
|
Codex review: needs maintainer review before merge. Reviewed July 9, 2026, 12:57 PM ET / 16:57 UTC. Summary PR surface: Source +2, Tests +86. Total +88 across 2 files. Reproducibility: yes. for source-level review: current main embeds non-OK Copilot OAuth response text in the thrown error, and the PR body includes wrapper-level local-server proof through 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. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Land the focused helper swap after ordinary maintainer review and exact-head gates, keeping the shared provider error-detail helper as the single Copilot OAuth non-OK error formatter. Do we have a high-confidence way to reproduce the issue? Yes for source-level review: current main embeds non-OK Copilot OAuth response text in the thrown error, and the PR body includes wrapper-level local-server proof through Is this the best way to solve the issue? Yes: reusing AGENTS.md: found and applied where relevant. Codex review notes: model internal, reasoning high; reviewed against 372b527da4a1. Label changesLabel justifications:
Evidence reviewedPR surface: Source +2, Tests +86. Total +88 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
Review history (4 earlier review cycles)
|
Add regression tests covering the device code flow and non-JSON error bodies alongside the existing token refresh coverage. Also include live proof output showing real Response object redaction via extractProviderErrorDetail. Ref: openclaw#102953
|
@clawsweeper re-review Added live proof via
Added 3 new regression tests: device code flow, non-JSON body handling, and token refresh path. Total 17 tests pass. |
|
🦞🧹 I asked ClawSweeper to review this item again. |
|
@clawsweeper re-review Added wrapper-level live proof: local HTTP server + |
|
Maintainer fixup and land-ready proof complete for exact head
No production credential was used; GitHub's documented device-flow JSON/error contract plus the real oversized loopback responses cover the failure boundary deterministically. |
|
Merged via squash.
|
Replace raw response body and URL in postJson and refreshAnthropicToken error messages with bounded, redacted structured error detail via extractProviderErrorDetail. Remove TOKEN_URL from the outer catch to prevent OAuth parameter leakage in logs. Follow-up to openclaw#102953
…rror Replace manual response.text() + throw Error with the canonical assertOkOrThrowProviderError which provides bounded reads, redaction, and structured ProviderHttpError metadata. Ref: openclaw#102953
Replace raw response body and URL in postJson and refreshAnthropicToken error messages with bounded, redacted structured error detail via extractProviderErrorDetail. Remove TOKEN_URL from the outer catch to prevent OAuth parameter leakage in logs. Follow-up to openclaw#102953
Replace raw response.text() + throw Error with createProviderHttpError for non-404 batch create errors, providing bounded reads and redaction. Preserve the helpful 404 diagnostic message. Ref: openclaw#102953
…sages (openclaw#102953) * fix(copilot): redact OAuth error response body in fetchJson error messages Replace raw response body text with bounded, redacted structured error detail extracted via extractProviderErrorDetail so OAuth error responses containing tokens, device codes, or other sensitive fields are not leaked into Error messages and downstream logs. * test(copilot): add device-code and non-JSON error body redaction cases Add regression tests covering the device code flow and non-JSON error bodies alongside the existing token refresh coverage. Also include live proof output showing real Response object redaction via extractProviderErrorDetail. Ref: openclaw#102953 * fix(copilot): normalize OAuth HTTP failures --------- Co-authored-by: Peter Steinberger <[email protected]>
…ponse body Move the non-OK check before readResponseWithLimit so the provider error extractor can read the response body. Previously extractProviderErrorDetail ran after the body was consumed, silently losing all structured error metadata. Ref: openclaw#102953
…efresh Replace readResponseTextLimited + raw body in error message with extractProviderErrorDetail for bounded, redacted structured error detail. Ref: openclaw#102953
Replace raw response.text() + throw Error with createProviderHttpError for non-404 batch create errors, providing bounded reads and redaction. Preserve the helpful 404 diagnostic message. Ref: openclaw#102953
…efresh Replace readResponseTextLimited + raw body in error message with extractProviderErrorDetail for bounded, redacted structured error detail. Ref: openclaw#102953
…efresh Replace readResponseTextLimited + raw body in error message with extractProviderErrorDetail for bounded, redacted structured error detail. Ref: openclaw#102953
* fix(chutes): redact OAuth error response body in token exchange and refresh Replace readResponseTextLimited + raw body in error message with extractProviderErrorDetail for bounded, redacted structured error detail. Ref: #102953 * fix(chutes): update test assertions for extractProviderErrorDetail message format * fix(chutes): remove unused readResponseTextLimited and CHUTES_OAUTH_ERROR_BODY_LIMIT_BYTES * fix(chutes): normalize OAuth HTTP failures * test(chutes): assert redaction without fixed mask --------- Co-authored-by: Peter Steinberger <[email protected]>
…rror Replace manual response.text() + throw Error with the canonical assertOkOrThrowProviderError which provides bounded reads, redaction, and structured ProviderHttpError metadata. Ref: openclaw#102953
Replace raw response.text() with extractProviderErrorDetail for bounded, redacted structured error detail in the token exchange non-OK path. Ref: openclaw#102953
Replace raw response.text() with extractProviderErrorDetail for bounded, redacted structured error detail in the token refresh non-OK path. Ref: openclaw#102953
Replace raw response body and URL in postJson and refreshAnthropicToken error messages with bounded, redacted structured error detail via extractProviderErrorDetail. Remove TOKEN_URL from the outer catch to prevent OAuth parameter leakage in logs. Follow-up to openclaw#102953
…ponse body Move the non-OK check before readResponseWithLimit so the provider error extractor can read the response body. Previously extractProviderErrorDetail ran after the body was consumed, silently losing all structured error metadata. Ref: openclaw#102953
Replace raw response.text() with extractProviderErrorDetail for bounded, redacted structured error detail in the token refresh non-OK path. Ref: openclaw#102953
* fix(chutes): redact OAuth error response body in token exchange and refresh Replace readResponseTextLimited + raw body in error message with extractProviderErrorDetail for bounded, redacted structured error detail. Ref: openclaw#102953 * fix(chutes): update test assertions for extractProviderErrorDetail message format * fix(chutes): remove unused readResponseTextLimited and CHUTES_OAUTH_ERROR_BODY_LIMIT_BYTES * fix(chutes): normalize OAuth HTTP failures * test(chutes): assert redaction without fixed mask --------- Co-authored-by: Peter Steinberger <[email protected]>
…sages (openclaw#102953) * fix(copilot): redact OAuth error response body in fetchJson error messages Replace raw response body text with bounded, redacted structured error detail extracted via extractProviderErrorDetail so OAuth error responses containing tokens, device codes, or other sensitive fields are not leaked into Error messages and downstream logs. * test(copilot): add device-code and non-JSON error body redaction cases Add regression tests covering the device code flow and non-JSON error bodies alongside the existing token refresh coverage. Also include live proof output showing real Response object redaction via extractProviderErrorDetail. Ref: openclaw#102953 * fix(copilot): normalize OAuth HTTP failures --------- Co-authored-by: Peter Steinberger <[email protected]> (cherry picked from commit 6b90610)
What Problem This Solves
GitHub Copilot's shared OAuth JSON request helper embedded the complete non-2xx response body in an
Error. A hostile or unexpectedly verbose OAuth endpoint could therefore retain up to 16 MiB in an error/log path and expose credentials returned in JSON fields.The affected helper owns four operations: device-code creation, device-token polling, Copilot-token exchange, and optional model-list discovery.
Why This Change Was Made
The final implementation routes the shared
fetchJsonnon-OK branch through the existingassertOkOrThrowProviderErrorboundary. That canonical helper already owns bounded error-prefix reads, overflow cancellation, secret redaction, structured code/type/request-id extraction, and theProviderHttpErrorshape.Successful response parsing is unchanged. Optional model-list failures still return the static fallback, and the separate model-policy request remains unchanged because it never formats the response body and already cancels it.
Thanks @ZOOWH for identifying the leak and supplying the initial patch.
User Impact
Copilot login and token-refresh failures retain actionable HTTP status, provider code, and request-id metadata without embedding the full raw response or credential values. Oversized error bodies are cancelled after a small diagnostic prefix instead of being buffered to the successful-response limit.
Evidence
Exact reviewed head:
66fa0e5209b16f1749a368d28bea6aea0dded652tbx_01kx4vws97n2fs1esghbxmxg29:src/llm/utils/oauth/github-copilot.test.ts, 17/17 passed.ProviderHttpError, preserved status/code/request-id, and omitted the full synthetic credential from bothmessageanderrorBody.check:changedpassed thecoreandcoreTestslanes on the same Testbox, including production/test tsgo and oxlint.origin/main: clean, correctness 0.88.No production GitHub credential was needed for the failure proof; the loopback server deterministically exercised oversized and secret-bearing responses without exposing a real account token.