Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/api/agentops/auth/templates/auth_callback.html
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
}
}

window.location.replace('{{ dashboard_url }}');
window.location.replace({{ dashboard_url }});
{# dashboard_url is a json.dumps()-encoded JS string literal — no outer quotes needed #}
})();
</script>
</body>
Expand Down
17 changes: 14 additions & 3 deletions app/api/agentops/auth/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@
from pathlib import Path
import urllib.parse

import json
import re

import pydantic
from fastapi import Request, Response
from fastapi.exceptions import HTTPException
Expand Down Expand Up @@ -69,7 +72,7 @@
from .session import Session

TEMPLATE_DIR = Path(__file__).parent / "templates"
templates = Environment(loader=FileSystemLoader(TEMPLATE_DIR))
templates = Environment(loader=FileSystemLoader(TEMPLATE_DIR)) # JS encoding applied explicitly via json.dumps


_all__ = [
Expand Down Expand Up @@ -277,7 +280,11 @@ async def auth_callback(request: Request) -> HTMLResponse:
if not redirect_to:
invite_org_id = request.query_params.get('invite')
if invite_org_id:
redirect_to = f"{APP_URL}/settings/organization?invite={invite_org_id}"
# Restrict invite to safe org-ID characters before embedding in URL
if re.match(r'^[a-zA-Z0-9_-]+$', invite_org_id):
redirect_to = f"{APP_URL}/settings/organization?invite={invite_org_id}"
else:
redirect_to = DASHBOARD_URL
else:
redirect_to = DASHBOARD_URL

Expand All @@ -286,8 +293,12 @@ async def auth_callback(request: Request) -> HTMLResponse:
redirect_to = DASHBOARD_URL

template = templates.get_template('auth_callback.html')
# json.dumps produces a JS-safe quoted string (e.g. "\"https://...\""), preventing
# injection of JS-special characters such as ' ; ) into the <script> context.
content = template.render(
nonce=nonce, auth_session_url=reverse_path('auth_session'), dashboard_url=redirect_to
nonce=nonce,
auth_session_url=reverse_path('auth_session'),
dashboard_url=json.dumps(str(redirect_to)),
)

return HTMLResponse(content=content, headers=headers)
Expand Down