b03051dec6a7b9d132995e809f378bc3277a987a
max
  Thu Aug 27 01:18:06 2026 -0700
escape search terms and uploaded sequence names (XSS), refs #38057

#Preview2 week - bugs introduced now will need a build patch to fix
#Preview2 week - bugs introduced now will need a build patch to fix
Follow-up to the QA review of dbd10edb43b.

hgFileSearch and hgTracks track search: the name/description/simple search
boxes echoed the search term into a value attribute unescaped.  The earlier
pass escaped the hgFileSearch simple box only, which sits inside a
USE_TABS block that is not compiled.

hgPhyloPlace: uploaded sequence names are echoed in the QC failure
messages from vcfFromFasta, in the subtree summary table, in the
placement and mutation-path headings, in the subtree download links and
in the no-reference-match list.  All now go through htmlEncode.

hgLiftOver: the failure file was read back while its FILE* was still
open and unflushed, so the "Failed input regions" block always came out
empty.  Close mapped and unmapped right after liftOver writes them.

diff --git src/hg/hgLiftOver/hgLiftOver.c src/hg/hgLiftOver/hgLiftOver.c
index 10d4ed4a3b1..7e09db2c00a 100644
--- src/hg/hgLiftOver/hgLiftOver.c
+++ src/hg/hgLiftOver/hgLiftOver.c
@@ -566,30 +566,34 @@
             errAbort("ERROR: Can't convert from %s to %s: no chain file loaded",
                                     fromDb, toDb);
 
         readLiftOverMap(chainFile, chainHash);
         lft = liftOverSniff(oldTn.forCgi);
         if (lft == bed)
         ct = liftOverBed(oldTn.forCgi, chainHash, 
                 minMatch, minBlocks, 0, minSizeQ,
                 minChainT, 0,
                             fudgeThick, mapped, unmapped, multiple, FALSE, NULL, &errCt, extraNameInfo);
         else if (lft == positions)
         ct = liftOverPositions(oldTn.forCgi, chainHash, 
                 minMatch, minBlocks, 0, minSizeQ,
                 minChainT, 0,
                 fudgeThick, mapped, unmapped, multiple, NULL, &errCt);
+        /* flush and close the output files before we read the failure file back below */
+        carefulClose(&mapped);
+        carefulClose(&unmapped);
+
         if (ct == -1)
             /* programming error */
             errAbort("ERROR: Unsupported data format.\n");
 
         webNewSection("Results");
         if (ct > 0)
             {
             /* some records succesfully converted */
             cgiParagraph("");
             printf("Successfully converted %d record", ct);
             printf("%s: ", ct > 1 ? "s" : "");
 	    printf("<a href='#' data-url='%s' class='link' id='viewLink'><BR>View conversions</a>\n", mappedTn.forCgi);
             printf("<A HREF=%s TARGET=_blank><BR>Download conversions</A>\n", mappedTn.forCgi);
             jsInlineF("document.getElementById('viewLink').addEventListener('click', function(ev) { "
                 "ev.preventDefault();"
@@ -614,32 +618,30 @@
                 // these are the user's own failed input regions, escape before echoing (XSS)
                 char *encoded = htmlEncode(line);
                 puts(encoded);
                 freeMem(encoded);
                 }
             lineFileClose(&errFile);
             puts("</PRE></BLOCKQUOTE>\n");
             }
         if ((multiple) && (lft == positions))
         {
         puts("<BLOCKQUOTE><PRE>\n");
         puts("Note: &quot;multiple&quot; option is not supported for position format.");
         puts("</PRE></BLOCKQUOTE>\n");
         }
         webParamsUsed(minMatch, multiple, minSizeQ, minChainT, minBlocks, fudgeThick);
-
-        carefulClose(&unmapped);
         }
     }
 errCatchEnd(errCatch);
 if (errCatch->gotError || errCatch->gotWarning)
     warn("%s", errCatch->message->string);
 errCatchFree(&errCatch);
 webDownloads();
 cartWebEnd();
 }
 
 /* Null terminated list of CGI Variables we don't want to save
  * permanently. */
 char *excludeVars[] = {"Submit", "submit", "SubmitFile",
                         HGLFT_USERDATA_VAR,
                         HGLFT_DATAFILE_VAR,