81d7cff6cb6665feb772b1339b6298273a3c49be max Thu Aug 6 08:24:38 2026 -0700 hgBlat new results page: table redesign, rename modal, share link, and an XSS fix Iterates on the new hgBlat "table" results page (rendered by hg/js/hgBlat.js from JSON emitted by hgBlat.c) per feedback on #37893: - Columns: "Open in Genome Browser" (position link + new-tab icon), "Show" (base alignment), Query coverage moved before Locus, score-scaled bar in the Score column, Identity as plain %, comma-formatted Span. Locus is plain text, clipped with a CSS ellipsis (full value in title). - Tooltips on every action link and every column header (via the existing convertTitleTagsToMouseovers mechanism). - "Rename BLAT Track": a real modal dialog (replaces the old inline toggle form), reusing the existing hgc buildBigPsl call via a new window.blatRenameCt() helper; it no longer depends on a generic page-global. New cfg fields canRename / trackName / trackDescription drive it. - "Share a link": trash-backed stable URL toggle, with a share-nodes icon. - Security: cgiEncode the query name in the htcUserAli detailsUrl (its sibling already did), and htmlEncode every URL before it goes into an href in hgBlat.js, so a crafted query/sequence name can't break out of the attribute (XSS). - Shared htmlEncode() moved into hg/js/utils.js for reuse instead of a per-file escaper. - hg.conf: blatNewPageBanner (invite banner, default off), blatOldTracks (keep/hide/delete previous BLAT tracks at creation) documented in ex.hg.conf; hgc.c tags BLAT tracks with blatResult=on and clears prior ones per blatOldTracks. refs #37893 diff --git src/hg/js/utils.js src/hg/js/utils.js index 5668209cd37..72bbd6f4408 100644 --- src/hg/js/utils.js +++ src/hg/js/utils.js @@ -4305,30 +4305,37 @@ /* Actually hides the tooltip text */ ele.classList.remove("isShown"); ele.style.opacity = "0"; ele.style.visibility = "hidden"; } function titleTagToMouseover(mapEl) { /* for a given area tag, extract the title text into a div that can be positioned * like a standard tooltip mouseover next to the item */ if (mapEl.dataset.tooltip) addMouseover(mapEl, mapEl.dataset.tooltip); else addMouseover(mapEl, mapEl.title); } +function htmlEncode(s) { + /* HTML-escape a value (&, <, >, ", ') so it is safe to insert as text or into an attribute + * value in a string of HTML. Shared helper: prefer this over rolling a per-file escaper. + * Uses the browser's own text->markup conversion via a detached element (jQuery required). */ + return $('<div>').text(s === null || s === undefined ? '' : String(s)).html(); +} + function convertTitleTagsToMouseovers() { /* make all the title tags in the document have mouseovers */ document.querySelectorAll("[title],[data-tooltip]").forEach(function(a, i) { if (a.id !== "" && (a.id === "hotkeyHelp" || a.id.endsWith("Dialog") || a.id.endsWith("Popup"))) { // these divs are populated by ui-dialog, they should not have tooltips return; } if (a.title !== undefined && (a.title.startsWith("click & drag to scroll") || a.title.startsWith("drag select or click to zoom"))) a.title = ""; else if ((a.title !== undefined && a.title.length > 0) || a.getAttribute("data-tooltip") !== null) { if (a.title && a.title.startsWith("Click to alter the display density")) { // these tooltips have a longer delay: a.setAttribute("tooltipDelay", "delayed"); }