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/sessionData.c src/hg/lib/sessionData.c
index b0ebd403aa4..97d1895b279 100644
--- src/hg/lib/sessionData.c
+++ src/hg/lib/sessionData.c
@@ -3,36 +3,30 @@
  * Copyright (C) 2019-2024 The Regents of the University of California
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "cart.h"
 #include "cheapcgi.h"
 #include "customComposite.h"
 #include "customTrack.h"
 #include "hdb.h"
 #include "hgConfig.h"
 #include "md5.h"
 #include "trashDir.h"
 #include "sessionData.h"
 #include "quickLift.h"
 
-INLINE boolean isTrashPath(char *path)
-/* Return TRUE if path starts with trashDir. */
-{
-return startsWith(trashDir(), path);
-}
-
 static char *sessionDataPathFromTrash(char *trashPath, char *sessionDir)
 /* Make a new path from a trash path -- replace "../trash" with safe location. */
 {
 if (!isTrashPath(trashPath))
     errAbort("sessionDataPathFromTrash: input is non-trash path '%s'", trashPath);
 return replaceChars(trashPath, trashDir(), sessionDir);
 }
 
 static char *maybeReadlink(char *path)
 /* If path is a symbolic link, then alloc & return the link target, otherwise NULL. */
 {
 char *linkTarget = NULL;
 struct stat stat;
 if (lstat(path, &stat) != 0)
     // expired file