CVE-2026-55665
Grist < 1.7.15 XSS in hrefs allows an editor to escalate to owner access.
- CVSS 8.5
- CWE-79
- Input Validation and Sanitization
- Remote
Grist is spreadsheet software using Python as its formula language. Prior to 1.7.15, Grist contained two cross-site scripting vulnerabilities where an attacker-controlled value reached a link's href without scheme validation, so a javascript URL could run in a victim's Grist origin on a single click. On the account-selection page, /welcome/select-account used its next query parameter as the account buttons' link target. In document tours, the GristDocTour table's Link_URL column became a clickable button, allowing an editor of a shared document to store a javascript URL there that ran when another user opened the document and clicked the tour link. Because the script runs in the victim's authenticated session, it can call Grist APIs as the victim, reading or modifying data and changing sharing settings and access rules. A document editor could therefore escalate to owner-level access. This issue is fixed in version 1.7.15.
- CWE
- CWE-79
- CVSS base score
- 8.5
- Published
- 2026-07-10
- 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
- Privilege Escalation
- Affected component
- Grist
Solution
Upgrade Grist to version 1.7.15 or later.
Vulnerable code sample
from flask import Flask, request, render_template_string
app = Flask(__name__)
# This code simulates the vulnerable '/welcome/select-account' endpoint from Grist
# prior to version 1.7.15. It takes the 'next' query parameter and uses it
# directly in a link's href attribute without any validation of the URL scheme.
# An attacker could supply a 'javascript:' URL to execute arbitrary code.
@app.route('/welcome/select-account')
def select_account():
# The 'next' parameter is fetched directly from the user-controlled query string.
# Example malicious URL: /welcome/select-account?next=javascript:alert('XSS')
next_url = request.args.get('next', '/')
# The template directly injects the unvalidated 'next_url' into the href
# attribute of the account buttons. This is the source of the XSS vulnerability.
template = """
<!DOCTYPE html>
<html>
<head>
<title>Select Account</title>
</head>
<body>
<h1>Select an Account</h1>
<a href="{{ link_target }}">
<button>Personal Account</button>
</a>
<br><br>
<a href="{{ link_target }}">
<button>Work Account</button>
</a>
</body>
</html>
"""
return render_template_string(template, link_target=next_url)Patched code sample
import urllib.parse
def sanitize_url_for_href(url):
"""
Represents the fix for CVE-2026-55665 by validating a URL's scheme
before it is used in a link's href attribute.
This prevents Cross-Site Scripting (XSS) by rejecting URLs with unsafe
schemes like 'javascript:'. If the URL is unsafe, it returns a safe
fallback value '#'.
Args:
url: The user-provided URL string to validate.
Returns:
The original url if it has a safe scheme (http, https) or is a
valid relative path starting with '/', otherwise returns '#'.
"""
if not isinstance(url, str):
return '#'
try:
# Use urlparse to reliably extract the scheme from the URL.
parsed_url = urllib.parse.urlparse(url)
# Define a whitelist of schemes considered safe for use in an href.
# An empty scheme ('') is included to allow for relative paths.
safe_schemes = ('http', 'https', '')
if parsed_url.scheme in safe_schemes:
# If the URL is a relative path (scheme is ''), ensure it starts
# with '/' to prevent misinterpretation by the browser.
if parsed_url.scheme == '' and not url.startswith('/'):
return '#'
# If the scheme is safe or it's a valid relative path, return the URL.
return url
# If the scheme is not in the whitelist (e.g., 'javascript', 'data'),
# it is considered unsafe.
return '#'
except ValueError:
# In case of any parsing errors, default to a safe value.
return '#'Payload
javascript:alert('XSS')
Cite this entry
@misc{vaitp:cve202655665,
title = {{Grist < 1.7.15 XSS in hrefs allows an editor to escalate to owner access.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-55665},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-55665/}}
}
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 ::
