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.
- CWE
- CWE-79
- 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_htmlPayload
<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 ::
