fix(dashboard): show chart tooltip labels as text, and decode keys like the api does - #7882
Open
ar2rsawseen wants to merge 1 commit into
Open
fix(dashboard): show chart tooltip labels as text, and decode keys like the api does#7882ar2rsawseen wants to merge 1 commit into
ar2rsawseen wants to merge 1 commit into
Conversation
…ke the api does Two related things, both about a value ending up on screen as what it actually is. sanitizeHtml, used for every value interpolated into a chart tooltip template, encoded its input and then immediately unescaped it. Those two are inverses, so it returned the input unchanged and a label containing markup went into the template as markup rather than being shown as text. That was not the original intent. The call sites escaped correctly until the unescape was added, to stop Countly's own key substitutions ($ for a leading $, . for dots) from being shown to the user as literal character references. Both goals hold if the value is normalised first and escaped last, so that is what it now does: undo the html escaping the api applies, undo the key substitutions, then escape once. A label reads as its real text and is never treated as markup. The second part came out of the first. There are two implementations of the key substitution pair, the api one that produces every value in the database and the dashboard one that consumes them, and they had drifted. The dashboard decoder undid only $ and . while the api encoder also substitutes NUL and the api decoder also accepts the url encoded forms, so ▯, &#36; and &#46; reached callers still encoded. Its own comment already promised the url encoded forms, so this looks like an incomplete copy rather than a decision. It went unnoticed because the result is usually interpolated into html, where the browser resolves the leftovers anyway; it shows through anywhere that is not html. All 25 callers of the dashboard decoder consume api-produced data, and the dashboard encoder has no callers at all, so the pair is now aligned with the api in both directions. One visible consequence: a value containing NUL now shows the character itself, which is invisible, where it previously showed the placeholder glyph the browser resolved ▯ into. NUL in a key or segment value is pathological, and the alternative was leaving one of the two decoders knowingly incomplete. Tests: 13 over the tooltip helper, 4 of which fail without this change, and 30 asserting the two substitution pairs agree on behaviour rather than on source text, 6 of which fail without it. The suite has no DOM, so encodeHtml, which is implemented with innerText, is substituted by its documented effect on element content; the composition order and the decode step are the real source, lifted out of the files. A key that already looks encoded is not round-trippable, which is a property of the scheme rather than of either implementation, so that is asserted as the shared behaviour it is instead of being asserted away. Full unit suite: 185 passing before, 228 after, same 2 pre-existing failures (Countly Request, network dependent). eslint clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two related things, both about a value ending up on screen as what it actually is.
sanitizeHtmlwas a no-opEvery value interpolated into a chart tooltip template goes through
sanitizeHtml, which encoded its input and then immediately unescaped it:Those two are inverses, so it returned the input unchanged, and a label containing markup went into the template as markup rather than being shown as text.
That was not the original intent. The call sites escaped correctly until the unescape was added, and
git logsays why: the escaping made Countly's own key substitutions ($for a leading$,.for dots) visible to users as literal character references, three days after the escaping first landed. So one goal was traded for the other.Both hold if the value is normalised first and escaped last, which is what it now does: undo the html escaping the api applies, undo the key substitutions, then escape once. A label reads as its real text and is never treated as markup.
Ordering is the whole point, and it's what the tests pin down.
The two key decoders had drifted
That came out of the first part. There are two implementations of the key substitution pair — the api one that produces every value in the database, and the dashboard one that consumes them:
$,., NUL)&#36;/&#46;)$,.)$,.)So
▯,&#36;and&#46;reached callers still encoded. The dashboard decoder's own comment already promised the url encoded forms, so this reads as an incomplete copy rather than a decision.It went unnoticed because the result is usually interpolated into html, where the browser resolves the leftovers as character references anyway. It shows through anywhere that isn't html.
Worth knowing before reviewing: all 25 callers of the dashboard decoder consume api-produced data, and the dashboard encoder has no callers at all, so the internal symmetry that justified the short rule set was never load-bearing. Both directions are now aligned with the api.
One visible consequence
A value containing NUL now shows the character itself, which is invisible, where it previously showed the placeholder glyph the browser resolved
▯into. NUL in a key or segment value is pathological, and the alternative was leaving one of the two decoders knowingly incomplete. Flagging it because it is a behaviour change, not a pure fix.Tests
Two notes on how they are written. The suite has no DOM, and
encodeHtmlis implemented withinnerText, so it is substituted by its documented effect on element content; the composition order and the decode step are the real source, lifted out of the files. And a key that already looks encoded is not round-trippable — that is a property of the substitution scheme rather than of either implementation, so it is asserted as the shared behaviour it is instead of asserted away.Full unit suite: 185 passing before, 228 after, same 2 pre-existing failures (
Countly Request, network dependent). Baseline captured by reverting and re-running. eslint clean.Notes
frontend/express/public/javascripts/min/is gitignored build output, so the stale copy of this code in the concat bundle regenerates and is not touched here.Going to
release.24.05and countly-platformmainnext; both carry identical copies of all three functions. countly-platformnew-dashboardis also identical, but it currently cannot receive anything frommain: the nightly sync stands down while a sync PR is open, and Countly/countly-platform#954 has been open and conflicting since 31 July, so that branch is 158 commits behind. Worth unblocking separately.🤖 Generated with Claude Code