VAITP Dataset

← Back to the dataset

CVE-2025-66040

XSS in Spotipy's OAuth callback via the unsanitized `error` parameter.

  • CVSS 3.6
  • CWE-79
  • Input Validation and Sanitization
  • Remote

Spotipy is a Python library for the Spotify Web API. Prior to version 2.25.2, there is a cross-site scripting (XSS) vulnerability in the OAuth callback server that allows for JavaScript injection through the unsanitized error parameter. Attackers can execute arbitrary JavaScript in the user's browser during OAuth authentication. This issue has been patched in version 2.25.2.

CVSS base score
3.6
Published
2025-11-27
OWASP
A03 Injection
Orthogonal defect classification
Checking
Code defect classification
Missing Check
Category
Input Validation and Sanitization
Subcategory
Cross-Site Scripting (XSS)
Accessibility scope
Remote
Impact
Arbitrary Code Execution
Affected component
Spotipy
Fixed by upgrading
Yes

Solution

Upgrade Spotipy to version 2.25.2 or later.

Vulnerable code sample

import http.server
import socketserver
from urllib.parse import urlparse, parse_qs

class VulnerableAuthHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        query_components = parse_qs(urlparse(self.path).query)
        error = query_components.get("error", [None])[0]

        self.send_response(200)
        self.send_header("Content-type", "text/html")
        self.end_headers()

        html_response = "<html><body><h1>Authentication Status</h1>"
        if error:
            # VULNERABLE LINE: Unsanitized 'error' parameter is injected into HTML
            html_response += f"<p>An error occurred: {error}</p>"
        else:
            html_response += "<p>Authentication successful. You can close this window.</p>"
        
        html_response += "</body></html>"
        
        self.wfile.write(html_response.encode("utf-8"))
        
        # In a real scenario, the server would be shut down after handling the request.
        # We add this to make the example self-contained and stoppable.
        self.server.shutdown_request = True


def start_vulnerable_oauth_server(port=8888):
    with socketserver.TCPServer(("", port), VulnerableAuthHandler) as httpd:
        httpd.shutdown_request = False
        while not httpd.shutdown_request:
            httpd.handle_request()

if __name__ == '__main__':
    # This simulates the local server part of the Spotipy OAuth flow.
    # To demonstrate the vulnerability, run this script and then visit a URL like:
    # http://127.0.0.1:8888/?error=<script>alert('XSS vulnerability demonstrated')</script>
    start_vulnerable_oauth_server()

Patched code sample

import html
from http.server import BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs

class PatchedOAuthCallbackHandler(BaseHTTPRequestHandler):
    """
    A simulated OAuth callback handler demonstrating the fix for an XSS vulnerability.
    The original vulnerability allowed for script injection via the 'error' URL parameter.
    """

    def do_GET(self):
        """Handles the GET request to the callback URL."""
        query_components = parse_qs(urlparse(self.path).query)
        error = query_components.get("error", [None])[0]

        self.send_response(200)
        self.send_header("Content-Type", "text/html")
        self.end_headers()

        # The vulnerable code would have been something like this:
        #
        # if error:
        #     # VULNERABLE: The 'error' string is injected directly into the HTML.
        #     # A URL like "...?error=<script>alert('XSS')</script>"
        #     # would execute the script in the user's browser.
        #     response_html = f"<h1>Error</h1><p>{error}</p>"
        #
        # The fix is to sanitize the input before embedding it.

        if error:
            # FIXED: The 'error' parameter from the URL is sanitized using
            # html.escape(). This converts special characters like '<' and '>'
            # into their HTML entities ('<' and '>'), preventing the
            # browser from interpreting them as HTML tags.
            sanitized_error = html.escape(error)
            response_html = f"""
            <html>
                <head><title>Authentication Failed</title></head>
                <body>
                    <h1>Authentication Failed</h1>
                    <p>An error was returned by the server:</p>
                    <pre style="background-color:#eee;padding:10px;">{sanitized_error}</pre>
                    <p>You may now close this browser window.</p>
                </body>
            </html>
            """
        else:
            # This would be the path for a successful authentication.
            response_html = """
            <html>
                <head><title>Authentication Successful</title></head>
                <body>
                    <h1>Authentication Successful</h1>
                    <p>You may now close this browser window.</p>
                </body>
            </html>
            """

        self.wfile.write(response_html.encode("utf-8"))

Payload

<script>alert('XSS')</script>

Cite this entry

@misc{vaitp:cve202566040,
  title        = {{XSS in Spotipy's OAuth callback via the unsanitized `error` parameter.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2025-66040},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-66040/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.

The supreme art of war is to subdue the enemy without fighting.

Sun Tzu – “The Art of War”

:: Shaping the future through research and ingenuity ::