CVE-2026-55659 Overview
CVE-2026-55659 is a stored and reflected cross-site scripting (XSS) vulnerability [CWE-79] affecting Grist, a spreadsheet application that uses Python as its formula language. Versions prior to 1.7.15 embedded user-controlled values into server-rendered pages and inline scripts without complete escaping. Attackers can inject script that executes in the victim's Grist origin under the authenticated session. A document editor can escalate to owner-level access by reading or modifying data, altering sharing settings, and changing access rules. The issue is fixed in Grist version 1.7.15.
Critical Impact
Authenticated document editors can escalate privileges to document owner by injecting scripts through document names, descriptions, or the OAuth2 openerOrigin parameter, hijacking other users' sessions within the Grist origin.
Affected Products
- Grist Labs grist-core versions prior to 1.7.15
- Grist main application page (document name and description rendering)
- Grist OAuth2 end-of-flow page (openerOrigin request parameter reflection)
Discovery Timeline
- 2026-07-10 - CVE-2026-55659 published to NVD
- 2026-07-13 - Last updated in NVD database
Technical Details for CVE-2026-55659
Vulnerability Analysis
The vulnerability originates in Grist's server-side page rendering pipeline. Multiple server-rendered pages interpolate user-controlled values into HTML markup and inline <script> blocks without applying context-appropriate escaping. Because the injected content executes inside inline scripts, standard HTML entity escaping alone is insufficient. Script payloads reach a JavaScript execution context, where they inherit the authenticated user's session cookies and CSRF context.
Two injection surfaces are documented. First, a document's name or description, controllable by any user with editor rights, is rendered into the main application page loaded by every user who opens the document. Second, the OAuth2 end-of-flow handler reflects the openerOrigin request parameter directly into the served page.
Root Cause
The root cause is unsafe value interpolation in the sendAppPage helper and in the OAuth2Clients module. String templating placed untrusted values into JavaScript literals without escaping characters that terminate string context or introduce new script syntax. The fix in commit 4ced8064b7ea0e1763d5a6a2588b22774ce7efbc introduces a hardened replaceLiterals helper backed by lodash/escapeRegExp to safely interpolate values.
Attack Vector
Exploitation requires low-privileged authentication and user interaction. A document editor sets a malicious payload as the document name or description. When another authenticated user opens the document, the payload executes in that user's browser under the Grist origin. Alternatively, an attacker crafts an OAuth2 flow URL containing a payload in openerOrigin and lures a signed-in user to complete the flow. The resulting scope change (S:C) allows access to resources beyond the attacker's own permission level, including owner-only actions.
// Patch excerpt: app/common/gutil.ts
UseCBOwner,
} from "grainjs";
import { Observable as KoObservable } from "knockout";
+import escapeRegExp from "lodash/escapeRegExp";
import identity from "lodash/identity";
// Some definitions have moved to be used by plugin API.
// Patch excerpt: app/server/lib/OAuth2Clients.ts
* https://datatracker.ietf.org/doc/html/draft-ietf-oauth-browser-based-apps#name-backend-for-frontend-bff.
*/
import { ApiError } from "app/common/ApiError";
-import { safeJsonParse } from "app/common/gutil";
+import { replaceLiterals, safeJsonParse } from "app/common/gutil";
import { User } from "app/common/User";
import { appSettings } from "app/server/lib/AppSettings";
import { getAuthorizedUserId, RequestWithLogin } from "app/server/lib/Authorizer";
Source: GitHub commit 4ced806
Detection Methods for CVE-2026-55659
Indicators of Compromise
- Document names or descriptions containing HTML tags, <script>, backticks, ${...} template literals, or quote-terminating characters (", ', </).
- Requests to the OAuth2 end-of-flow endpoint with openerOrigin values containing JavaScript syntax or non-origin characters.
- Unexpected changes to sharing settings, access rules, or document ownership performed under a user session that did not initiate them.
Detection Strategies
- Review Grist audit logs for access-rule and sharing modifications correlated to document-open events from editor accounts.
- Inspect HTTP access logs for OAuth2 callback requests containing suspicious openerOrigin query parameter values.
- Search stored document metadata for HTML or JavaScript syntax that should never appear in legitimate names or descriptions.
Monitoring Recommendations
- Enable and centralize Grist server request logging, including query strings for OAuth2 endpoints.
- Alert on privilege changes where the initiating actor is a document editor rather than an owner.
- Deploy a Content Security Policy (CSP) that restricts inline scripts and monitor CSP violation reports for evidence of injected payloads.
How to Mitigate CVE-2026-55659
Immediate Actions Required
- Upgrade grist-core to version 1.7.15 or later, which applies the hardened value interpolation fix.
- Audit all documents for unusual names or descriptions containing HTML or script syntax and sanitize them.
- Review recent access-rule and sharing changes on sensitive documents and revert unauthorized modifications.
Patch Information
The fix is available in Grist release v1.7.15. It introduces a replaceLiterals helper that safely interpolates values into server-rendered pages and inline scripts. Full patch details are documented in GHSA-6qrq-h2h6-cw54 and the source commit.
Workarounds
- Restrict document editor permissions to trusted users until the upgrade is applied.
- Deploy a strict Content Security Policy that blocks inline script execution to limit the impact of injected payloads.
- Place a reverse proxy or WAF in front of Grist to filter OAuth2 callback requests containing suspicious openerOrigin parameter values.
# Upgrade grist-core to the patched release
docker pull gristlabs/grist:1.7.15
docker stop grist && docker rm grist
docker run -d --name grist -p 8484:8484 \
-v /srv/grist:/persist \
gristlabs/grist:1.7.15
# Verify the running version
curl -s http://localhost:8484/status | grep -i version
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

