VAITP Dataset

← Back to the dataset

CVE-2026-32112

ha-mcp: XSS in OAuth consent form due to improper HTML escaping.

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

ha-mcp is a Home Assistant MCP Server. Prior to 7.0.0, the ha-mcp OAuth consent form renders user-controlled parameters via Python f-strings with no HTML escaping. An attacker who can reach the OAuth endpoint and convince the server operator to follow a crafted authorization URL could execute JavaScript in the operator's browser. This affects only users running the beta OAuth mode (ha-mcp-oauth), which is not part of the standard setup and requires explicit configuration. This vulnerability is fixed in 7.0.0.

CVSS base score
4.7
Published
2026-03-11
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
ha-mcp
Fixed by upgrading
Yes

Solution

Upgrade to ha-mcp version 7.0.0.

Vulnerable code sample

from flask import Flask, request

# This code is a representation of the vulnerability in ha-mcp (prior to 7.0.0)
# as described in CVE-2026-32112. It is for educational purposes only.
# It simulates a server running the beta OAuth mode.

app = Flask(__name__)

@app.route('/oauth/authorize')
def vulnerable_oauth_consent():
    """
    This endpoint simulates the vulnerable OAuth consent form.
    An attacker can craft a URL with a malicious 'client_id' parameter.
    Example malicious URL:
    /oauth/authorize?client_id=<script>alert('XSS Executed')</script>
    """
    # Get the 'client_id' from the URL query parameters.
    # This is the user-controlled input.
    client_id = request.args.get('client_id', 'an unknown application')

    # VULNERABILITY: The 'client_id' parameter is rendered directly into the
    # HTML response using a Python f-string without any form of HTML escaping.
    # This allows for a Cross-Site Scripting (XSS) attack.
    html_page = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <title>OAuth Consent</title>
    </head>
    <body>
        <h1>Authorize Application</h1>
        <p>
            Do you authorize the application "<b>{client_id}</b>" to access your data?
        </p>
        <button>Authorize</button>
        <button>Deny</button>
    </body>
    </html>
    """

    return html_page

# To run this PoC:
# 1. Install Flask: pip install Flask
# 2. Run this script: python your_script_name.py
# 3. Open your browser and navigate to:
#    http://127.0.0.1:5000/oauth/authorize?client_id=<img src=x onerror=alert('Vulnerable-to-XSS')>

Patched code sample

import html
from flask import Flask, request

# This is a representative example using the Flask web framework.
# The actual ha-mcp code might use a different framework, but the
# principle of the vulnerability and the fix remains the same.

app = Flask(__name__)

@app.route('/oauth/authorize')
def oauth_consent_form():
    """
    Simulates the OAuth consent form endpoint described in the CVE.
    """
    # An attacker-controlled parameter from the URL query string.
    # Example: /oauth/authorize?client_id=<script>alert('XSS')</script>
    client_id = request.args.get('client_id', 'Unknown Application')

    # --- VULNERABLE CODE (Prior to 7.0.0) ---
    # The f-string directly includes the user-controlled 'client_id' without
    # any sanitization, leading to a Cross-Site Scripting (XSS) vulnerability.
    #
    # vulnerable_html = f"""
    # <h1>Authorize {client_id}</h1>
    # <p>Do you grant this application access to your account?</p>
    # """

    # --- FIXED CODE (Version 7.0.0 and later) ---
    # The fix involves using html.escape() to sanitize the user-controlled
    # parameter before it is rendered in the HTML response. This converts
    # special characters like '<', '>', and '&' into their corresponding
    # HTML-safe sequences (e.g., '<', '>', '&').
    escaped_client_id = html.escape(client_id)

    # The sanitized variable is now safely included in the f-string.
    # Any malicious script tags will be displayed as plain text to the user
    # instead of being executed by the browser.
    fixed_html = f"""
    <!DOCTYPE html>
    <html>
    <head>
        <title>OAuth Authorization</title>
    </head>
    <body>
        <h1>Authorize {escaped_client_id}</h1>
        <p>Do you grant this application access to your account?</p>
        <button>Authorize</button>
        <button>Deny</button>
    </body>
    </html>
    """
    return fixed_html

Payload

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

Cite this entry

@misc{vaitp:cve202632112,
  title        = {{ha-mcp: XSS in OAuth consent form due to improper HTML escaping.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2026},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2026-32112},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-32112/}}
}
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 ::