554396e44745165ee4baf5214aa771f2b17b3be6 braney Mon Aug 17 16:04:26 2026 -0700 hgPcr, cart: screen the PCR result file names read back out of the cart, refs #37623 The hgPcrResult_ cart variable holds two file names and an optional target name in one value. The cart.c arrays compare a whole value against isServerUserFilePath(), so none of them fit that shape. Add a fourth array for it and check the first two words. hgPcrResult_targetStyle shares the prefix and is a display setting, so it is excluded by name. Check both names where they are used as well, in pcrResultParseCart() and in hgPcr's append path, the way dupTrack.c already does. Two other things in writePcrResultTrack(). pcrFiles[2] was read without ever being set whenever the value held only two words, which is the usual case. And the saved-session test was a plain prefix compare that missed sessionDataDirOld; it now asks whether the file is in the trash instead. hg/utils/cartFileVarCatalog knows about the new array and has a row for hgPcrResult_ saying why its scan cannot see this one. diff --git src/hg/lib/cart.c src/hg/lib/cart.c index c0c990defee..e22a6f491ca 100644 --- src/hg/lib/cart.c +++ src/hg/lib/cart.c @@ -25,30 +25,31 @@ #include "wikiLink.h" #endif /* GBROWSE */ #include "hgMaf.h" #include "hui.h" #include "geoMirror.h" #include "hubConnect.h" #include "trackHub.h" #include "cgiApoptosis.h" #include "customComposite.h" #include "regexHelper.h" #include "windowsToAscii.h" #include "jsonWrite.h" #include "verbose.h" #include "genark.h" #include "quickLift.h" +#include "pcrResult.h" #include "botDelay.h" #include "curlWrap.h" #include "hubSpaceKeys.h" #include "myVariantsShare.h" #include "customTrack.h" #include "dupTrack.h" #include "myVariants.h" static char *sessionVar = "hgsid"; /* Name of cgi variable session is stored in. */ static char *positionCgiName = "position"; DbConnector cartDefaultConnector = hConnectCart; DbDisconnect cartDefaultDisconnector = hDisconnectCart; static boolean cartDidContentType = FALSE; @@ -235,53 +236,92 @@ MYVARIANTS_FILE_VAR_PREFIX, // mvCtfile_: myVariants custom track customCompositeCartName "-", // customComposite-: track collection hub quickLiftCartName "-", // hubQuickLift-: quickLift hub }; /* These two hold either a remote URL or the name of a file the server made, and the code that * reads them tells the cases apart by looking for a protocol. A value with no protocol falls * through to opening a local file, so it has to be one of ours; a real URL is fine. */ static char *urlOrFileNameCartVars[] = { "multiRegionsBedUrl", // hgTracks multi-region BED: a URL, or the trash file we wrote hgsLoadUrlName, // hgSession load settings from URL }; +/* This one does not hold a single file name. hgPcr writes two file names and an optional + * target name into it, separated by spaces: " []". + * Both file names are checked; the target name is not a path. hgPcr appends to both files + * when the user asks to add to an existing result, so a retargeted value is a write as well + * as a read. */ + +static char *fileNamePairCartVarPrefixes[] = +{ + PCR_RESULT_TRACK_NAME "_", // hgPcrResult_: in-silico PCR result files +}; + static boolean cartVarHoldsFileName(char *var) /* Return TRUE if var is one of the cart variables listed above. */ { int i; for (i = 0; i < ArraySize(fileNameCartVars); i++) if (sameString(var, fileNameCartVars[i])) return TRUE; for (i = 0; i < ArraySize(fileNameCartVarPrefixes); i++) if (startsWith(fileNameCartVarPrefixes[i], var)) return TRUE; return FALSE; } static boolean cartVarHoldsUrlOrFileName(char *var) /* Return TRUE if var is one of the cart variables that may hold either. */ { int i; for (i = 0; i < ArraySize(urlOrFileNameCartVars); i++) if (sameString(var, urlOrFileNameCartVars[i])) return TRUE; return FALSE; } +static boolean cartVarHoldsFileNamePair(char *var) +/* Return TRUE if var is one of the variables that hold a pair of file names. + * hgPcrResult_targetStyle is a display setting that shares the hgPcrResult_ prefix with the + * per-db result variables, so it is excluded by name. */ +{ +if (sameString(var, PCR_RESULT_TARGET_STYLE)) + return FALSE; +int i; +for (i = 0; i < ArraySize(fileNamePairCartVarPrefixes); i++) + if (startsWith(fileNamePairCartVarPrefixes[i], var)) + return TRUE; +return FALSE; +} + +static boolean fileNamePairIsAcceptable(char *val) +/* Return TRUE if the first two whitespace-separated words of val both name a file the server + * made for this user. Any other shape is refused: the code that reads this value errAborts + * on fewer than two words, and reads no more than three. */ +{ +char *dupe = cloneString(val); +char *words[4]; +int wordCount = chopByWhite(dupe, words, ArraySize(words)); +boolean ok = (wordCount == 2 || wordCount == 3) && + isServerUserFilePath(words[0]) && isServerUserFilePath(words[1]); +freeMem(dupe); +return ok; +} + static void logDroppedFileNameVar(char *var, char *why) /* Note the drop in the error log, so that a false positive can be spotted after release. * The variable name comes from the user, so copy out only characters that cannot forge a * log line of their own, and keep the copy short. */ { char clean[129]; int i; for (i = 0; i < (int)sizeof(clean) - 1 && var[i] != 0; i++) { unsigned char c = var[i]; clean[i] = (isalnum(c) || c == '_' || c == '.' || c == '-') ? c : '?'; } clean[i] = 0; fprintf(stderr, "cart: dropped %s, value is not %s\n", clean, why); } @@ -294,30 +334,37 @@ return TRUE; if (cartVarHoldsFileName(var)) { if (isServerUserFilePath(val)) return TRUE; logDroppedFileNameVar(var, "a trash or session-data file name"); return FALSE; } if (cartVarHoldsUrlOrFileName(var)) { if (isServerUserFileOrUrl(val)) return TRUE; logDroppedFileNameVar(var, "a URL or a trash or session-data file name"); return FALSE; } +if (cartVarHoldsFileNamePair(var)) + { + if (fileNamePairIsAcceptable(val)) + return TRUE; + logDroppedFileNameVar(var, "a pair of trash or session-data file names"); + return FALSE; + } return TRUE; } static void loadHash(struct hash *hash, char *contents) /* Load a hash from a cart-like string. */ { char *namePt, *dataPt, *nextNamePt; namePt = contents; while (namePt != NULL && namePt[0] != 0) { dataPt = strchr(namePt, '='); if (dataPt == NULL) errAbort("Mangled input string %s", namePt); *dataPt++ = 0; nextNamePt = strchr(dataPt, '&');