099dda2013e7caffde097371fa68de467a2c5085
max
  Tue Jun 30 05:14:13 2026 -0700
hgBlat: fix bugs in showAliPlaces BLAT results page

- Fix inverted JS test in buildBigPslCt: 'if (!ct_blat !== "")' always
evaluated true (a boolean strict-compared to a string), so the
remove-custom-track vars were appended on every call. Should be
'if (ct_blat !== "")'.
- Escape trackName/trackDescription with javaScriptLiteralEncode before
interpolating them into single-quoted JS literals. They derive from the
user FASTA header (psl->qName), so a quote broke the script / allowed
injection.
- Remove a stray re-opened <TT><PRE> in the PSL-output branch that left
unbalanced tags.
- Free the leaked locusConn connection and subList in the hyperlink branch.

diff --git src/hg/hgBlat/hgBlat.c src/hg/hgBlat/hgBlat.c
index 2c58ecc9795..bbed0916c9e 100644
--- src/hg/hgBlat/hgBlat.c
+++ src/hg/hgBlat/hgBlat.c
@@ -539,31 +539,30 @@
 	cartWebEnd();
 	}
     }
 else if (pslOut)
     {
     if (!pslRawOut)
         printf("<TT><PRE>");
     if (!sameString(output, "psl no header"))
 	pslxWriteHead(stdout, qType, tType);
 
     for (psl = pslList; psl != NULL; psl = psl->next)
 	pslTabOut(psl, stdout);
 
     if (pslRawOut)
         exit(0);
-    printf("<TT><PRE>");
     printf("</PRE></TT>");
     }
 else if (jsonOut)  
     {
     webStartText();
     pslWriteAllJson(pslList, stdout, database, TRUE);
     exit(0);
     }
 else  // hyperlink
     {
     printf("<H2>BLAT Search Results</H2>");
     char* posStr = cartOptionalString(cart, "position");
     if (posStr != NULL)
         printf("<P>Go back to <A HREF=\"%s\">%s</A> on the Genome Browser.</P>\n", browserUrl, posStr);
 
@@ -636,58 +635,60 @@
 	    }
 
         if (!feelingLucky)
 	    {
 	    // REMOVE CT BUTTON FORM.
 	    printf("<div id=deleteCtForm style='display: none'>\n");
 	    printf("<FORM ACTION=\"%s?hgsid=%s&db=%s\" NAME=\"MAIN_FORM\" METHOD=%s>\n\n",
 		hgTracksName(), cartSessionId(cart), database, cartUsualString(cart, "formMethod", "POST"));
 	    cartSaveSession(cart);
 	    cgiMakeButton(CT_DO_REMOVE_VAR, "Delete Custom Track");
 	    cgiMakeHiddenVar(CT_SELECTED_TABLE_VAR, "FAKETRACKNAME");
 	    printf("</FORM>\n");
 	    printf("</div>\n");
 	    }
 
+        char *trackNameJs = javaScriptLiteralEncode(trackName);
+        char *trackDescriptionJs = javaScriptLiteralEncode(trackDescription);
         jsInlineF(
 
 	    "var ct_blat = '';\n"
 	    "\n"
 	    "function buildBigPslCtSuccess (content, status)\n"
 	    "{ // Finishes the successful creation of blat ct bigPsl.  Called by ajax return.\n"
 	    "  // saves the ct name so it can be used later for rename or delete.\n"
 	    "\n"
 	    "var matchWord = '&table=';\n"
 	    "var ct_blatPos = content.indexOf(matchWord) + matchWord.length;\n"
 	    "\n"
 	    "if (ct_blatPos >= 0)\n"
 	    "    {\n"
 	    "    var ct_blatPosEnd = content.indexOf('\"', ct_blatPos);\n"
 	    "    ct_blat = content.slice(ct_blatPos, ct_blatPosEnd);\n"
 	    "    if (luckyLocation == '')\n"
 	    "        {\n"
 	    "        $('input[name=\""CT_SELECTED_TABLE_VAR"\"]')[0].value = ct_blat;\n"
 	    "        $('input[name=\""CT_SELECTED_TABLE_VAR"\"]')[1].value = ct_blat;\n"
 	    "        }\n"
 	    "    }\n"
 	    "}\n"
 	    "\n"
 	    "function buildBigPslCt (url, trackName, trackDescription)\n"
 	    "{ // call hgc to buildBigPsl from blat result.\n"
 	    "\n"
 	    "var cgiVars = 'trackName='+encodeURIComponent(trackName)+'&trackDescription='+encodeURIComponent(trackDescription);\n"
-	    "if (!ct_blat !== '')\n"
+	    "if (ct_blat !== '')\n"
 	    "    {\n"  	
 	    "    cgiVars += '&"CT_DO_REMOVE_VAR"='+encodeURIComponent('Remove Custom Track');\n"
 	    "    cgiVars += '&"CT_SELECTED_TABLE_VAR"='+encodeURIComponent(ct_blat);\n"
 	    "    }\n"  	
 	    "\n"
 	    "$.ajax({\n"
 	    "    type: 'GET',\n"
 	    "    url: url,\n"
 	    "    data: cgiVars,\n"
 	    "    dataType: 'html',\n"
 	    "    trueSuccess: buildBigPslCtSuccess,\n"
 	    "    success: catchErrorOrDispatch,\n"
 	    "    error: errorHandler,\n"
 	    "    cache: false,\n"
 	    "    async: false\n"
@@ -697,31 +698,33 @@
 	    "var url='%s';\n"
 	    "var trackName='%s';\n"
 	    "var trackDescription='%s';\n"
             "$(document).ready(function() {\n"
 	    "\n"
 	    "buildBigPslCt(url, trackName, trackDescription);\n"
 	    "if (luckyLocation !== '')\n"
 	    "    {\n"
             "    location.replace(luckyLocation);\n"
 	    "    }\n"
 	    "else\n"
 	    "    {\n"
 	    "    $('#renameFormItem')[0].style.display = 'block';\n"   
 	    "    $('#deleteCtForm')[0].style.display = 'block';\n"   
 	    "    }\n"
-            "});\n", url->string, trackName, trackDescription);
+            "});\n", url->string, trackNameJs, trackDescriptionJs);
+        freeMem(trackNameJs);
+        freeMem(trackDescriptionJs);
 
 
             // RENAME CT JS CODE
 	    if (!feelingLucky)
 		jsInline("$('#showRenameForm').click(function(){\n"
 		    "  $('#renameForm')[0].style.display = 'block';\n"
 		    "  $('#renameFormItem')[0].style.display = 'none';\n"
 		    "  $('#showRenameForm')[0].style.display = 'none';\n"
 		    "  $('input[name=\"trackName\"]')[0].value = trackName;\n"
 		    "  $('input[name=\"trackDescription\"]')[0].value = trackDescription;\n"
 		    "return false;\n"
 		    "});\n");
 
             // RENAME CT JS CODE
 	    if (!feelingLucky)
@@ -904,30 +907,33 @@
 	    psl->tEnd - psl->tStart);
 
         // if you modify this, also modify hgPcr.c:doQuery, which implements a similar feature
         char *seq = psl->tName;
         if (endsWith(seq, "_fix"))
             printf("   <A target=_blank HREF=\"../FAQ/FAQdownloads.html#downloadFix\">What is chrom_fix?</A>");
         else if (endsWith(seq, "_alt"))
             printf("   <A target=_blank HREF=\"../FAQ/FAQdownloads.html#downloadAlt\">What is chrom_alt?</A>");
         else if (endsWith(seq, "_random"))
             printf("   <A target=_blank HREF=\"../FAQ/FAQdownloads.html#download10\">What is chrom_random?</A>");
         else if (startsWith(seq, "chrUn"))
             printf("   <A target=_blank HREF=\"../FAQ/FAQdownloads.html#download11\">What is a chrUn sequence?</A>");
         printf("\n");
 	}
     printf("</PRE>\n");
+    if (locusConn != NULL)
+        hFreeConn(&locusConn);
+    subTextFreeList(&subList);
     webNewSection("Help");
     puts("<P style=\"text-align:left\"><A target=_blank HREF=\"../FAQ/FAQblat.html#blat1b\">Missing a match?</A><br>");
     puts("<A target=_blank HREF=\"../FAQ/FAQblat.html#blat1c\">What is chr_alt & chr_fix?</A></P>\n");
     puts("</DIV>\n");
     }
 pslFreeList(&pslList);
 
 }
 
 void trimUniq(bioSeq *seqList)
 /* Check that all seq's in list have a unique name.  Try and
  * abbreviate longer sequence names. */
 {
 struct hash *hash = newHash(0);
 bioSeq *seq;