Skip to content

fix(events): show event descriptions for keys with special characters - #7855

Merged
LaNinja06 merged 4 commits into
release.24.05from
fix/event-description-special-char-keys-2405
Jul 30, 2026
Merged

fix(events): show event descriptions for keys with special characters#7855
LaNinja06 merged 4 commits into
release.24.05from
fix/event-description-special-char-keys-2405

Conversation

@LaNinja06

Copy link
Copy Markdown
Contributor

Problem

Event descriptions set in Data Manager render blank on the Events page for events whose key contains . $ \ & < > " '. Plain keys work, which made the issue look intermittent.

Editing such an event also had a second symptom: an event whose key contains & would be saved back as a &amp; b, forking a second drill_meta / events.map entry that shows up as an undeletable duplicate ("ghost") row in the Data Manager events table.

Root cause

Two independent escaping bugs that happen to produce the same blank-description symptom:

  1. . $ \ — the Events page unicode-escaped the event key (.., $$) before looking it up, but events.map is keyed raw, exactly as the key appears in events.list. The lookup missed.

  2. & < > " 'common.returnOutputescape_html(str, true) HTML-escapes every string key and value in every API response. The events store decoded res.list but not the keys of res.map, so map[selectedEventName] compared a raw key against an escaped map. escape_html only touches charCodes 34/38/39/60/62, so dots and dashes passed through untouched — hence the "only some events" appearance.

The ghost rows come from the same escaping: the event detail page's Edit event button and the bulk visibility action read values straight out of the store (i.e. still API-escaped) and submitted them back as-is.

Changes

frontend/express/public/core/events/javascripts/countly.details.models.js

  • getEventLongName / getLabels: look the key up raw instead of unicode-escaping it.
  • setAllEventsData: normalize list and the map keys together. This is the one point both fetch paths commit through, so it also fixes fetchRefreshAllEventsData never decoding list at all. The now-redundant decode in fetchAllEventsData is removed.

plugins/data-manager/frontend/public/javascripts/countly.views.js

  • handleEdit: decode key / e / name / description / categoryName and segment names before opening the drawer, and deep-copy segments (previously assigned by reference, so drawer edits mutated the store).
  • handleChangeVisibility: decode row.key before submitting.

The decode set in handleEdit deliberately mirrors the file's existing, already-correct dm-open-edit-event-drawer handler, which is the path the events table uses to open the very same drawer. The bug was that the detail page's button did not do the same thing.

Verification

Two harnesses that lift the real shipped functions out of the files and run them against a payload built by modelling escape_html, so before/after is measured on actual code rather than a re-implementation.

Read path (description resolution, 18 keys incl. dots, $, backslash, &, &&, quotes, apostrophe, angle brackets, %, #, emoji, accents):

before after
synthetic set 12 passed, 6 failed 18 passed, 0 failed
dump from a real dev app 6 passed, 0 failed

The 6 baseline failures were exactly the & < > " ' family.

Write path (ghost rows), 10 keys:

before after
key submitted intact by handleEdit 4/10 10/10
name/description/category intact 0/10 10/10
key submitted intact by handleChangeVisibility 4/10 10/10

node --check and eslint clean on both changed files.

Notes

  • EventsGroupsTabView has the same raw-row.key pattern but is not affected: event group ids are [CLY]_group_ + an md5 hex digest, which cannot contain the escaped characters.
  • An additional events.map write in the enterprise /i/data-manager/event/edit handler was investigated and dropped — the drawer fires core /i/events/edit_map first and that already persists name/description, so the extra branches were unreachable and added a second whole-map read-modify-write with clobber risk. No enterprise-plugins change is needed for this case.

LaNinja06 and others added 3 commits July 29, 2026 10:42
Event descriptions (and custom names / count-sum-dur labels) added via
Data Manager did not render on the Events page for events whose key
contains a special character (. $ \ & < > " '). The map lookup escaped
the key with an ad-hoc unicode scheme (.->., $->$) before
looking it up in events.map. But events.map is keyed by the raw event
key (the Data Manager write path stores it verbatim), so the escaped
lookup key never matched and the description came back blank. Look the
key up raw, exactly as it is stored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… and edit paths

common.returnOutput HTML-escapes every string key and value on the way out
(escape_html(str, true), which escapes & < > " '). Two paths did not account
for it, which made both bugs look intermittent: keys containing only dots or
dashes were unaffected.

On the read path, the events store decoded res.list but not the keys of
res.map, so every map[selectedEventName] lookup compared a raw key against
an escaped map and missed - descriptions came back blank on the Events page
for keys containing & < > " '. Both are now normalised in the
setAllEventsData mutation, the single point both fetch paths commit through,
which also fixes fetchRefreshAllEventsData never decoding list at all.

On the write path, the event detail page's "Edit event" button passed
API-escaped values straight into the edit drawer, so "a & b" was saved back
as "a &amp; b" and forked a second drill_meta / events.map entry with no key
and no segments - an undeletable ghost row in the Data Manager events table.
The events table's own edit action already decoded; handleEdit and
handleChangeVisibility now do the same. handleEdit also deep-copies segments,
which were previously passed into the drawer by reference, so decoding in
place would have mutated the Vuex store.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Event keys are arbitrary user-supplied strings. Assigning a "__proto__"
key on a plain object reparents the map rather than adding an own
property, which would let unrelated key lookups resolve through the
assigned value. Object.create(null) removes that side effect.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@LaNinja06
LaNinja06 requested a review from kanwarujjaval July 29, 2026 18:40
@LaNinja06
LaNinja06 merged commit 746eef4 into release.24.05 Jul 30, 2026
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants