f247b79b61ca172b3146284594b97c848db4c8bf
braney
  Wed Sep 2 11:20:43 2026 -0700
hgSession, hgGenome: tighten handling of an uploaded file's name, refs #38172

Both CGIs print the name that arrives with an upload straight into the page.
hgSession prints it in four of its load messages, so encode it once where it
is read; the sibling URL message three lines above already did the same for
the URL.  hgGenome puts it in a quoted attribute, which wants
attributeEncode.

Raised in the v503 Preview II code review.

diff --git src/hg/hgSession/hgSession.c src/hg/hgSession/hgSession.c
index 11a3538c8d8..31227944837 100644
--- src/hg/hgSession/hgSession.c
+++ src/hg/hgSession/hgSession.c
@@ -1693,30 +1693,34 @@
 		 "previously saved browser settings, and then click "
 		 "\"submit\" again.");
     if (!startsWith("http://",url) && !startsWith("https://",url) && !startsWith("ftp://",url))
         errAbort("Unsupported protocol for loading a file via URL.  Please use http, https, or ftp");
     lf = netLineFileOpen(url);
     dyStringPrintf(dyMessage, "Loaded settings from URL %s .  %s %s",
 		   htmlEncode(url), getUrlLink(url), getUrlEmailLink(url));
     }
 else
     {
     char *filePlainContents = cartOptionalString(cart, hgsLoadLocalFileName);
     char *fileBinaryCoords = cartOptionalString(cart,
 					hgsLoadLocalFileName "__binary");
     char *fileName = cartOptionalString(cart,
 					hgsLoadLocalFileName "__filename");
+    /* The name arrives with the upload and is printed in four of the messages below,
+     * so encode it once here rather than at each one. */
+    if (isNotEmpty(fileName))
+	fileName = htmlEncode(fileName);
     if (isNotEmpty(filePlainContents))
 	{
 	char *settings = trimSpaces(filePlainContents);
 	dyStringAppend(dyMessage, "Loaded settings from local file ");
 	if (isNotEmpty(fileName))
 	    dyStringPrintf(dyMessage, "<B>%s</B> ", fileName);
 	dyStringPrintf(dyMessage, "(%lu bytes).",
 		       (unsigned long)strlen(settings));
 	lf = lineFileOnString("settingsFromFile", TRUE, cloneString(settings));
 	}
     else if (isNotEmpty(fileBinaryCoords))
 	{
 	/* The cart holds the address and size of the uploaded bytes, but any
 	 * request can set that variable, so only use a block cheapcgi
 	 * handed out. */