9ad04e0a0b06ec3c4f09ef1b6c3ce6be79b61c68
braney
  Sun Aug 16 11:56:56 2026 -0700
cart: validate file names read back out of the cart

Several cart variables hold the name of a file the server created for a user.
Route them through one shared check, isServerUserFilePath(), which accepts the
trash directory, the session-data directories and myVariantsDataDir, and apply
it both where values enter the cart and where the file names are used.

A few of these variables may instead hold a remote URL.  Those get their own
list and isServerUserFileOrUrl(), because the code that reads them chooses
between a fetch and a local open by looking for a protocol.

Consolidates two hand-rolled copies of the same test in blatShare.c and
customFactory.c, and drops the weaker private copy in sessionData.c.

Adds hg/utils/cartFileVarCatalog, a registry that scans the tree for a cart
value reaching a file call and reconciles what it finds against the lists in
cart.c, so a new one of these cannot be added without somebody noticing.  Its
--reconcile is quiet enough for the nightly cron the other catalogs use, and it
is what turned up seven of the names now on those lists.

refs #37623

diff --git src/hg/hgBlat/hgBlat.c src/hg/hgBlat/hgBlat.c
index bbde8684329..846a2e42cfd 100644
--- src/hg/hgBlat/hgBlat.c
+++ src/hg/hgBlat/hgBlat.c
@@ -2865,31 +2865,33 @@
 char *pslFile = cartOptionalString(cart, "blatPslFile");
 char *faFile = cartOptionalString(cart, "blatFaFile");
 /* Use the db/organism the saved search ran against, not the cart's current db (which may have
  * drifted): the trash PSLs carry that assembly's chrom names, so rendering under any other db
  * gives broken position and "View alignment" links (hgc "bad input variables"). */
 char *savedDb = cartOptionalString(cart, "blatDb");
 if (isNotEmpty(savedDb))
     {
     database = savedDb;
     char *savedOrg = cartOptionalString(cart, "blatOrganism");
     if (isNotEmpty(savedOrg))
         organism = savedOrg;
     }
 cartWebStart(cart, database, "%s (%s) BLAT Results",
     trackHubSkipHubName(organism), trackHubSkipHubName(database));
-if (pslFile == NULL || faFile == NULL || !fileExists(pslFile))
+if (pslFile == NULL || faFile == NULL ||
+    !isServerUserFilePath(pslFile) || !isServerUserFilePath(faFile) ||
+    !fileExists(pslFile))
     printf("<p>These BLAT results are no longer available. Please run a new "
            "<a href=\"hgBlat\">BLAT search</a>.</p>\n");
 else
     showAliPlaces(pslFile, faFile, NULL, database, gftDna, gftDna, organism, FALSE);
 cartWebEnd();
 }
 
 static boolean blatSafeTrashId(char *s)
 /* TRUE only if s is a bare trash-file basename token (letters, digits, underscore).  Because it can
  * contain no '/', '.' or '..', a path built from it cannot escape the trash directory. */
 {
 if (isEmpty(s))
     return FALSE;
 char *p;
 for (p = s; *p != '\0'; ++p)