2cdae04ddc1e46cdfc2e5e8cf479cbbbb616ad10
chmalee
Tue Sep 22 15:55:10 2026 -0700
htmlSanitize tooltips before printing them into the page, refs #38226
diff --git src/hg/hgTracks/imageV2.c src/hg/hgTracks/imageV2.c
index 8b4cc643d0d..3f05652be00 100644
--- src/hg/hgTracks/imageV2.c
+++ src/hg/hgTracks/imageV2.c
@@ -5,30 +5,31 @@
#include "common.h"
#include "hPrint.h"
#include "chromInfo.h"
#include "hdb.h"
#include "hui.h"
#include "jsHelper.h"
#include "cheapcgi.h"
#include "htmshell.h"
#include "imageV2.h"
#include "hgTracks.h"
#include "hgConfig.h"
#include "regexHelper.h"
#include "customComposite.h"
#include "chromAlias.h"
#include "trackHub.h"
+#include "htmlSanitize.h"
// Note: when right-click View image (or pdf output) then theImgBox==NULL, so it will be rendered as a single simple image
struct imgBox *theImgBox = NULL; // Make this global for now to avoid huge rewrite
struct imgTrack *curImgTrack = NULL; // Make this global for now to avoid huge rewrite
/////////////////////////
// FLAT TRACKS
// A simplistic way of flattening the track list before building the image
// NOTE: Strategy is NOT to use imgBox->imgTracks, since this should be independednt of imageV2
/////////////////////////
void flatTracksAdd(struct flatTracks **flatTracks,struct track *track,struct cart *cart, struct slName *orderedWiggles)
// Adds one track into the flatTracks list
{
struct flatTracks *flatTrack;
AllocVar(flatTrack);
@@ -1885,46 +1886,56 @@
else if (startsWith("/cgi-bin/hgGene", item->linkVar)) // redmine #4151
hPrintf(" HREF='..%s'",item->linkVar); // FIXME: Chin should get rid
else // of this special case!
hPrintf(" HREF='%s'",item->linkVar);
hPrintf(" class='area'");
}
else
warn("map item has no url!");
if (item->title != NULL && strlen(item->title) > 0)
{
char *encodedString = attributeEncode(item->title);
if (cfgOptionBooleanDefault("showMouseovers", FALSE))
{
if (isNotEmpty(item->tooltip))
- hPrintf(" title='%s' data-tooltip='%s'", encodedString, attributeEncode(item->tooltip));
+ {
+ char *sanitized = htmlSanitize(item->tooltip);
+ char *encoded = attributeEncode(sanitized);
+ hPrintf(" title='%s' data-tooltip='%s'", encodedString, encoded);
+ freeMem(encoded);
+ freeMem(sanitized);
+ }
else
hPrintf(" TITLE='%s'", encodedString);
}
else
{
// for TITLEs, which we use for mouseOvers, since they can't have HTML in
// them, we substitute a unicode new line for
after we've encoded it.
// This is stop-gap until we start doing mouseOvers entirely in Javascript
hPrintf(" TITLE='%s'", replaceChars(encodedString,"<br>", "
"));
}
}
else if (isNotEmpty(item->tooltip) && cfgOptionBooleanDefault("showMouseovers", FALSE))
{
// some items have no title string (no item name) but do have tooltips
- hPrintf(" data-tooltip='%s'", attributeEncode(item->tooltip));
+ char *sanitized = htmlSanitize(item->tooltip);
+ char *encoded = attributeEncode(sanitized);
+ hPrintf(" data-tooltip='%s'", encoded);
+ freeMem(encoded);
+ freeMem(sanitized);
}
if (item->id != NULL)
hPrintf(" id='%s'", item->id);
hPrintf(">" );
}
hPrintf("\n");
return TRUE;
}
static void imageDraw(struct imgBox *imgBox,struct imgTrack *imgTrack,struct imgSlice *slice,
char *name,int offsetX,int offsetY,boolean useMap)
// writes an image as HTML
{
if (slice->parentImg && slice->parentImg->file != NULL)
{
@@ -2080,45 +2091,49 @@
struct mapSet *map = sliceGetMap(slice,FALSE); // Could be the image map or slice specific
if (map)
useMap = imageMapDraw(map,name);
else if (slice->link != NULL)
{
if (sameString(TITLE_BUT_NO_LINK,slice->link))
{ // This fake link ensures a mouse-over title is seen but not heard
hPrintf("link) != NULL)
hPrintf(" link);
else
hPrintf(" link);
if (slice->title != NULL)
{
+ char *sanitized = htmlSanitize(slice->title);
+ char *encSanitized = attributeEncode(sanitized);
if (sliceType == stButton)
{
enum browserType browser = cgiClientBrowser(NULL,NULL,NULL);
char *newLine = NEWLINE_TO_USE(browser);
char *ellipsis = ELLIPSIS_TO_USE(browser);
if (imgTrack->reorderable)
hPrintf(" TITLE='%s%sclick or right click to configure%s%sdrag to reorder%s'",
- attributeEncode(slice->title), newLine, ellipsis, newLine,
+ encSanitized, newLine, ellipsis, newLine,
(tdbIsCompositeChild(imgTrack->tdb) ? " highlighted subtracks" : "") );
else
hPrintf(" TITLE='%s%sclick or right click to configure%s'",
- attributeEncode(slice->title), newLine, ellipsis);
+ encSanitized, newLine, ellipsis);
}
else
- hPrintf(" TITLE='Click for:
%s'", attributeEncode(slice->title) );
+ hPrintf(" TITLE='Click for:
%s'", encSanitized );
+ freeMem(sanitized);
+ freeMem(encSanitized);
}
hPrintf(">\n" );
}
char *trackName = (imgTrack->name != NULL ? imgTrack->name : imgTrack->tdb->track );
struct jsonElement *trackHash = jsonFindNamedField(ele, "trackDb", trackName);
jsonObjectAdd(trackHash, "imgOffsetY", newJsonNumber(offsetY));
imageDraw(imgBox,imgTrack,slice,name,offsetX,offsetY,useMap);
if (slice->link != NULL)
hPrintf("");
if (slice->parentImg)
hPrintf("");
}
@@ -2229,75 +2244,83 @@
jsonTdbSettingsInit(jsonTdbVars);
char *newLine = NEWLINE_TO_USE(cgiClientBrowser(NULL,NULL,NULL));
struct imgTrack *imgTrack = smashSquish(imgBox->imgTracks);
for (;imgTrack!=NULL;imgTrack=imgTrack->next)
{
char *trackName = (imgTrack->name != NULL ? imgTrack->name : imgTrack->tdb->track );
struct track *track = hashFindVal(trackHash, trackName);
if (track)
jsonTdbSettingsBuild(jsonTdbVars, track, TRUE);
hPrintf("