Cline OAuth flow missing state/PKCE (login CSRF + code injection) #8

Open
opened 2026-07-23 18:38:20 +00:00 by claude-bot · 0 comments
Collaborator

Severity: High

packages/core/src/provider/cline/auth.ts

authorizeUrl() sets only client_type, callback_url, redirect_uri. There is no state, no nonce, and no PKCE code_challenge. The loopback callback server binds a predictable port range (48801–48811) and exchanges any GET /auth?code=… it receives, with no state validation.

Impact

  1. Login CSRF / authorization-code fixation. During the ~5-min login window, any web page open in the victim's browser can issue fetch("http://127.0.0.1:48801/auth?code=ATTACKER_CODE", {mode:"no-cors"}). The request reaches the server regardless of CORS (CORS only blocks reading the response, not the request). Thorny exchanges the attacker's code and stores the attacker's tokens — the victim is silently signed into the attacker's Cline account, and subsequent work/keys land there.
  2. No PKCE for a public client. Any local process or proxy observing the code (also present in browser history / referrer) can redeem it, since nothing binds the code to this client instance.

Fix

  • Generate a random state, include it in authorizeUrl, reject callbacks whose state does not match (RFC 6749 §10.12).
  • Add PKCE (code_challenge/code_verifier, S256) to authorizeUrl + exchangeCode.

Found via security audit.

**Severity: High** `packages/core/src/provider/cline/auth.ts` `authorizeUrl()` sets only `client_type`, `callback_url`, `redirect_uri`. There is no `state`, no `nonce`, and no PKCE `code_challenge`. The loopback callback server binds a predictable port range (48801–48811) and exchanges any `GET /auth?code=…` it receives, with no state validation. ### Impact 1. **Login CSRF / authorization-code fixation.** During the ~5-min login window, any web page open in the victim's browser can issue `fetch("http://127.0.0.1:48801/auth?code=ATTACKER_CODE", {mode:"no-cors"})`. The request reaches the server regardless of CORS (CORS only blocks reading the response, not the request). Thorny exchanges the attacker's code and stores the attacker's tokens — the victim is silently signed into the attacker's Cline account, and subsequent work/keys land there. 2. **No PKCE for a public client.** Any local process or proxy observing the code (also present in browser history / referrer) can redeem it, since nothing binds the code to this client instance. ### Fix - Generate a random `state`, include it in `authorizeUrl`, reject callbacks whose `state` does not match (RFC 6749 §10.12). - Add PKCE (`code_challenge`/`code_verifier`, S256) to `authorizeUrl` + `exchangeCode`. Found via security audit.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
brodycritchlow/thorny#8
No description provided.