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/lib/trackHub.c src/hg/lib/trackHub.c
index 550425a9a12..ba8f9aaf229 100644
--- src/hg/lib/trackHub.c
+++ src/hg/lib/trackHub.c
@@ -1597,31 +1597,31 @@
 if (isEmpty(s))
     return NULL;
 char *sp = skipToSpaces(s);
 int len = (sp != NULL) ? (sp - s) : (int)strlen(s);
 return cloneStringZ(s, len);
 }
 
 boolean quickLiftHubRemoveTrack(struct cart *cart, char *sourceDb, char *trackName)
 /* Remove a track stanza from the quickLift hub file for sourceDb, along with
  * any descendant stanzas (transitively) whose parent is being removed.
  * Returns TRUE if at least one stanza was removed. */
 {
 char buffer[4096];
 safef(buffer, sizeof buffer, "%s-%s", quickLiftCartName, sourceDb);
 char *filename = cartOptionalString(cart, buffer);
-if (filename == NULL)
+if (filename == NULL || !isServerUserFilePath(filename))
     return FALSE;
 
 struct lineFile *lf = lineFileMayOpen(filename, TRUE);
 if (lf == NULL)
     return FALSE;
 
 char *bareName = trackHubSkipHubName(trackName);
 struct dyString *header = dyStringNew(0);
 struct quickLiftStanza *stanzaList = NULL;
 struct quickLiftStanza *cur = NULL;
 char *line;
 int lineSize;
 
 /* Pass 1: read the file into a header + list of stanzas, recording each
  * stanza's name and (if any) parent. */