Skip to content

security: restrict OAuth callback postMessage targetOrigin to prevent session token leak#2464

Open
sebastiondev wants to merge 1 commit intoarc53:mainfrom
sebastiondev:fix/cwe79-routes-reflected-bf13
Open

security: restrict OAuth callback postMessage targetOrigin to prevent session token leak#2464
sebastiondev wants to merge 1 commit intoarc53:mainfrom
sebastiondev:fix/cwe79-routes-reflected-bf13

Conversation

@sebastiondev
Copy link
Copy Markdown

Summary

The OAuth connector callback page (ConnectorCallbackStatus in application/api/connector/routes.py) sends the authenticated user's session_token and user_email to window.opener via postMessage(..., '*'). The wildcard target origin means any origin that opened the popup window will receive these credentials.

  • CWE-79 / CWE-345: Sensitive data sent cross-origin without origin restriction
  • Severity: High (session token disclosure leading to account takeover)
  • Affected file: application/api/connector/routes.py (ConnectorCallbackStatus.get)
  • Affected client: frontend/src/components/ConnectorAuth.tsx (handleAuthMessage did not validate event.origin)

Data flow

  1. Victim is authenticated to DocsGPT.
  2. Victim visits an attacker-controlled page that calls window.open('https://docsgpt.example/api/connector/callback-status?...') (or initiates a connector OAuth flow that lands there).
  3. After OAuth completes, the callback HTML executes window.opener.postMessage({ session_token, user_email, ... }, '*').
  4. Because the target origin is '*', the attacker's page receives the message and reads event.data.session_token.
  5. Attacker uses the token to impersonate the victim.

The frontend listener also accepted messages from any origin, which compounds the risk in the other direction (attacker could spoof success/error events to a legitimate DocsGPT tab).

Fix

Two small, defensive changes:

  1. Backend (routes.py + settings.py) — Replace postMessage(..., '*') with postMessage(..., targetOrigin) where targetOrigin is rendered from a new FRONTEND_URL setting (default http://localhost:5173, matching the dev frontend). The value is rendered through the existing safe_js_string helper so it stays HTML/JS-injection safe.
  2. Frontend (ConnectorAuth.tsx) — In handleAuthMessage, drop messages whose event.origin does not match the configured API host (VITE_API_HOST). This guards the listener side against spoofed messages from other windows.

Operators who deploy DocsGPT under a different frontend domain set FRONTEND_URL accordingly. The default keeps local development working out of the box.

Testing

  • Added tests/test_cwe79_postmessage.py covering:
    • The rendered callback HTML no longer contains '*' as a postMessage target.
    • The targetOrigin is the configured FRONTEND_URL.
    • safe_js_string continues to escape the value (no HTML/JS injection via the setting).
  • Manually verified the OAuth popup flow still completes against http://localhost:5173 with the new default.
  • Verified that a popup opened from a different origin no longer receives the session_token payload.

Adversarial review

Before submitting, we tried to disprove this. We checked whether DocsGPT's auth model or any framework-level header (CSP, COOP, etc.) already prevented the leak — it doesn't: there is no Cross-Origin-Opener-Policy set on the callback response, and the only thing standing between window.opener and a third-party page is the targetOrigin argument itself, which was '*'. We also considered whether the popup is only ever opened from the trusted frontend in practice; it is not enforced — window.open can be invoked from any page the victim visits, and the callback URL is reachable as long as the victim has a valid session. The fix closes the gap with a minimal change that preserves the existing dev-mode UX.

cc @lewiswigmore

…ge (CWE-79)

The OAuth callback-status page used wildcard "*" as the targetOrigin in
window.opener.postMessage(), allowing any window that opened the OAuth
popup to receive the session_token. An attacker could craft a page that
opens the OAuth flow and intercepts the session token via postMessage.

Changes:
- Replace "*" targetOrigin with configurable FRONTEND_URL setting
- Add FRONTEND_URL setting to Settings (defaults to http://localhost:5173)
- Add origin validation on the frontend message event listener
- Add regression tests for the postMessage targetOrigin fix
@vercel
Copy link
Copy Markdown

vercel Bot commented May 9, 2026

@sebastiondev is attempting to deploy a commit to the Arc53 Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant