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/inc/trashDir.h src/hg/inc/trashDir.h
index b4f19424456..c752ac3e694 100644
--- src/hg/inc/trashDir.h
+++ src/hg/inc/trashDir.h
@@ -11,16 +11,46 @@
 void trashDirFile(struct tempName *tn, char *dirName, char *base, char *suffix);
 /*	obtain a trash file name trash/dirName/base*.suffix */
 
 void trashDirDateFile(struct tempName *tn, char *dirName, char *base, char *suffix);
 /*	obtain a trash file name trash/dirName.dayOfYear/base*.suffix */
 
 boolean trashDirReusableFile(struct tempName *tn, char *dirName, char *base, char *suffix);
 /*      obtain a resusable trash file name as trash/dirName/base.suffix
  *      returns TRUE if already exists. */
 
 void copyFileToTrash(char **pFileName, char *dirName, char *base, char *suffix);
 /* If *pFileName is not NULL and exists, then create a new file in the
  * given dirName of trash/ with the given base and suffix, copy *pFileName's
  * contents to it, and set *pFileName to the new filename. */
 
+boolean isTrashPath(char *path);
+/* Return TRUE if path names a file inside the trash directory. */
+
+boolean isTrashOrSessionDataPath(char *path);
+/* Return TRUE if path is under the trash directory, or under one of the durable session-data
+ * directories that trash files are moved to when a session is saved.  This is the allow-list
+ * isValidBigDataUrl() uses for a bigDataUrl that names a local file. */
+
+boolean isServerUserFilePath(char *path);
+/* Return TRUE if path is under one of the directories where the server keeps files it made
+ * for a user: the trash directory, the session-data directories, or a per-feature data
+ * directory such as myVariantsDataDir.
+ *
+ * Use this on any file name that comes back out of the cart before opening, reading, writing
+ * or deleting it.  Cart values are not ours: they arrive from CGI parameters, from an uploaded
+ * or fetched hgSession file, and from another user's shared session.  Note that a trash file
+ * may itself be a symbolic link pointing at session storage, so a realpath() check is not
+ * usable here -- sessionDataSaveTrashFile() creates exactly those links on purpose. */
+
+boolean isRemoteUrl(char *path);
+/* Return TRUE if path is a URL fetched over the network rather than a file name. */
+
+boolean isServerUserFileOrUrl(char *path);
+/* Return TRUE if path is either a remote URL or a file the server made for a user.
+ *
+ * Use this instead of isServerUserFilePath() on the cart variables that legitimately hold
+ * either one, such as multiRegionsBedUrl and the hgSession load-from-URL name.  Both are read
+ * by code that decides between the two by looking for a protocol and falls through to opening
+ * a local file, so a value with no protocol has to be one of ours. */
+
 #endif	/*	TRASHDIR_H	*/