From 214108a4664dace8dd8738e856b2cc9b09a5b15c Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Aug 2026 23:59:29 +0300 Subject: [PATCH] security(core): escape "<" in the res.expose script island (stored XSS via app name) Backport of #7949 to release.24.05. The dashboard serialises the exposed countlyGlobal object into an inline ", but the HTML tokeniser also ends a script element at "", "" and other whitespace/slash spellings, so an application name containing one broke out of the script block. An app admin of a single app could store such a name; any global admin who then loaded the dashboard (which lists every app) executed the attacker's markup in their own session, escalating an app-admin account to global-admin control. Escape every "<" as < in both serialisation paths (string values and object keys). < parses back to "<", so every value read from the exposed object is unchanged; verified by an eval round-trip that reproduces the input object identically and matches the previous serialiser output. Also render the active-app name with .text() instead of .html() in countly.template.js, an independent DOM sink for the same value. Reported through the security bug bounty programme (received 2026-08-17). Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 3 +++ frontend/express/libs/express-expose.js | 13 +++++++++---- .../public/javascripts/countly/countly.template.js | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7703fab7885..204b9526f2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ Fixes: Enterprise Fixes: - [data-manager] Fixed editing an event whose key contains `&` creating undeletable duplicate rows in the events table +Security Fixes: +- [core] The dashboard escapes `<` when serializing the exposed `countlyGlobal` object into the inline page script, so an application name (or any exposed value) containing a `` end tag in any spelling can no longer break out of the script block and run in another user's session; the active-app name is now rendered with `.text()` instead of `.html()` + ## Version 24.05.51 Fixes: diff --git a/frontend/express/libs/express-expose.js b/frontend/express/libs/express-expose.js index 34d662e58e3..5a160207e23 100644 --- a/frontend/express/libs/express-expose.js +++ b/frontend/express/libs/express-expose.js @@ -186,9 +186,13 @@ function string(obj) { else { obj = JSON.stringify(obj); if (obj) { - // Only escape things that could break out of script context - obj = obj.replace(/<\/script>/ig, ''); - obj = obj.replace(/