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_<db> 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_<db> saying why its scan cannot see this one.

diff --git src/hg/cgilib/pcrResult.c src/hg/cgilib/pcrResult.c
index ff9961f7d0d..4a69012c33f 100644
--- src/hg/cgilib/pcrResult.c
+++ src/hg/cgilib/pcrResult.c
@@ -1,265 +1,270 @@
 /* pcrResult -- support for internal track of hgPcr results. */
 
 /* Copyright (C) 2014 The Regents of the University of California 
  * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */
 
 #include "common.h"
 #include "hdb.h"
 #include "hui.h"
 #include "obscure.h"
 #include "targetDb.h"
 #include "pcrResult.h"
 #include "trackHub.h"
+#include "trashDir.h"
 
 
 char *pcrResultCartVar(char *db)
 /* Returns the cart variable name for PCR result track info for db. 
  * Don't free the result! */
 {
 static char buf[1024];
 safef(buf, sizeof(buf), "%s_%s", PCR_RESULT_TRACK_NAME, db);
 return buf;
 }
 
 #define setPtIfNotNull(pt, val) if (pt != NULL) *pt = val
 
 boolean pcrResultParseCart(char *db, struct cart *cart, char **retPslFile,
 			   char **retPrimerFile,
 			   struct targetDb **retTarget)
 /* Parse out hgPcrResult cart variable into components and make sure
  * they are valid.  If so, set *ret's and return TRUE.  Otherwise, null out 
  * *ret's and return FALSE (and clear the cart variable if it exists).  
  * ret's are ignored if NULL. */
 {
 char *cartVar = pcrResultCartVar(cartString(cart, "db"));
 char *hgPcrResult = cartOptionalString(cart, cartVar);
 if (isEmpty(hgPcrResult))
     {
     setPtIfNotNull(retPslFile, NULL);
     setPtIfNotNull(retPrimerFile, NULL);
     setPtIfNotNull(retTarget, NULL);
     return FALSE;
     }
 static char buf[2048];
 char *words[3];
 int wordCount;
 safecpy(buf, sizeof(buf), hgPcrResult);
 wordCount = chopLine(buf, words);
 if (wordCount < 2)
     errAbort("Badly formatted hgPcrResult variable: %s", hgPcrResult);
 char *pslFile = words[0];
 char *primerFile = words[1];
 char *targetName = (wordCount > 2) ? words[2] : NULL;
 struct targetDb *target = NULL;
 if (!trackHubDatabase(db))
     target = targetDbLookup(db, targetName);
 
-if (!fileExists(pslFile) || !fileExists(primerFile) ||
+/* Both names came back out of the cart, and the files are opened and echoed to the user
+ * below, so make sure they name files the server made for this user rather than some other
+ * file on the machine.  Refs #37623. */
+if (!isServerUserFilePath(pslFile) || !isServerUserFilePath(primerFile) ||
+    !fileExists(pslFile) || !fileExists(primerFile) ||
     (wordCount > 2 && target == NULL))
     {
     cartRemove(cart, cartVar);
     setPtIfNotNull(retPslFile, NULL);
     setPtIfNotNull(retPrimerFile, NULL);
     setPtIfNotNull(retTarget, NULL);
     return FALSE;
     }
 setPtIfNotNull(retPslFile, cloneString(pslFile));
 setPtIfNotNull(retPrimerFile, cloneString(primerFile));
 setPtIfNotNull(retTarget, target);
 if (retTarget == NULL)
     targetDbFreeList(&target);
 return TRUE;
 }
 
 void pcrResultGetPrimers(char *fileName, char **retFPrimer, char **retRPrimer, char *primerKey)
 /* Given a file whose first line is 2 words (forward primer, reverse primer)
  * set the ret's to upper-cased primer sequences. If primerKey is non NULL, the
  * first two words of the line joined with "_" must equal the primerKey:
  * primerKey == word1_word2
  * Used when there are multiple pcr results in a single track
  * Do not free the statically allocated ret's. */
 {
 static char fPrimer[1024], rPrimer[1024];;
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 struct dyString *primerPair = dyStringNew(0);
 char *words[2];
 while (lineFileRow(lf, words))
     {
     dyStringClear(primerPair);
     dyStringPrintf(primerPair, "%s_%s", words[0], words[1]);
     if (!primerKey || (primerKey && sameString(primerPair->string, primerKey)))
         {
         if (retFPrimer != NULL)
             {
             safecpy(fPrimer, sizeof(fPrimer), words[0]);
             touppers(fPrimer);
             *retFPrimer = fPrimer;
             }
         if (retRPrimer != NULL)
             {
             safecpy(rPrimer, sizeof(rPrimer), words[1]);
             touppers(rPrimer);
             *retRPrimer = rPrimer;
             }
         break;
         }
     }
 dyStringFree(&primerPair);
 lineFileClose(&lf);
 }
 
 static boolean checkPcrResultCoordinates(boolean targetSearchResult, char *tName, int tStart,
             int tEnd, char *item, int itemStart, int itemEnd, char *chrom)
 /* Check the coordinates of a pcrResult match so we know which list of psls
  * to add the match to */
 {
 if (targetSearchResult)
     {
     if (sameString(tName, item) && tStart == itemStart && tEnd == itemEnd)
         return TRUE;
     }
 else if (sameString(tName, chrom) && tStart == itemStart && tEnd == itemEnd)
     {
     return TRUE;
     }
 return FALSE;
 }
 
 void pcrResultGetPsl(char *fileName, struct targetDb *target, char *item,
 		     char *chrom, int itemStart, int itemEnd,
 		     struct psl **retItemPsl, struct psl **retOtherPsls, char *fPrimer, char *rPrimer)
 /* Read in psl from file.  If a psl matches the given item position, set 
  * retItemPsl to that; otherwise add it to retOtherPsls.  Die if no psl
  * matches the given item position. */
 {
 struct lineFile *lf = lineFileOpen(fileName, TRUE);
 struct psl *itemPsl = NULL, *otherPsls = NULL;
 char *pslFields[21];
 boolean targetSearchResult = stringIn("__", item) != NULL;
 while (lineFileRow(lf, pslFields))
     {
     struct psl *psl = pslLoad(pslFields);
     boolean gotIt = FALSE;
     // if "_" is in the item name, we look up the result(s) by the primer pair, else
     // we just show all psls
     if (stringIn("_", item) && !sameString(psl->qName, "n/a"))
         {
         char *pair = cloneString(psl->qName);
         char *under = strchr(pair, '_');
         *under = '\0';
         char *thisFPrimer = pair;
         char *thisRPrimer = under+1;
         if (!differentWord(thisFPrimer, fPrimer) && !differentWord(thisRPrimer, rPrimer))
             {
             gotIt = checkPcrResultCoordinates(targetSearchResult, psl->tName, psl->tStart,
                     psl->tEnd, item, itemStart, itemEnd, chrom);
             if (gotIt)
                 itemPsl = psl;
             else
                 slAddHead(&otherPsls, psl);
             }
         }
     else
         {
         gotIt = checkPcrResultCoordinates(targetSearchResult, psl->tName, psl->tStart,
                 psl->tEnd, item, itemStart, itemEnd, chrom);
         if (gotIt)
             itemPsl = psl;
         else if (!(fPrimer && rPrimer))
             slAddHead(&otherPsls, psl);
         }
 
     }
 lineFileClose(&lf);
 if (itemPsl == NULL)
     {
     if (target != NULL)
         errAbort("Did not find record for amplicon in %s sequence %s",
              target->description, item);
     else
         errAbort("Did not find record for amplicon at %s:%d-%d",
              chrom, itemStart, itemEnd);
     }
 if (retItemPsl != NULL)
     *retItemPsl = itemPsl;
 else
     pslFree(&itemPsl);
 if (retOtherPsls != NULL)
     {
     slSort(&otherPsls, pslCmpTarget);
     *retOtherPsls = otherPsls;
     }
 else
     pslFreeList(&otherPsls);
 }
 
 struct trackDb *pcrResultFakeTdb()
 /* Construct a trackDb record for PCR Results track. */
 {
 struct trackDb *tdb;
 AllocVar(tdb);
 tdb->track = cloneString(PCR_RESULT_TRACK_NAME);
 tdb->table = cloneString(PCR_RESULT_TRACK_NAME);
 tdb->shortLabel = cloneString(PCR_RESULT_TRACK_LABEL);
 tdb->longLabel = cloneString(PCR_RESULT_TRACK_LONGLABEL);
 tdb->grp = cloneString("map");
 tdb->type = cloneString("psl .");
 tdb->priority = 100.01;
 tdb->canPack = TRUE;
 tdb->visibility = tvPack;
 tdb->html = hFileContentsOrWarning(hHelpFile(PCR_RESULT_TRACK_NAME));
 trackDbPolish(tdb);
 if (tdb->settingsHash == NULL)
     tdb->settingsHash = hashNew(0);
 hashAdd(tdb->settingsHash, BASE_COLOR_DEFAULT, cloneString("diffBases"));
 hashAdd(tdb->settingsHash, BASE_COLOR_USE_SEQUENCE,
 	cloneString(PCR_RESULT_TRACK_NAME));
 hashAdd(tdb->settingsHash, SHOW_DIFF_BASES_ALL_SCALES, cloneString("."));
 hashAdd(tdb->settingsHash, INDEL_DOUBLE_INSERT, cloneString("on"));
 hashAdd(tdb->settingsHash, INDEL_QUERY_INSERT, cloneString("on"));
 hashAdd(tdb->settingsHash, INDEL_POLY_A, cloneString("on"));
 hashAdd(tdb->settingsHash, "nextItemButton", cloneString("off"));
 return tdb;
 }
 
 char *pcrResultItemAccName(char *acc, char *name, struct psl *origPsl)
 /* If a display name is given in addition to the acc, concatenate them
  * into a single name that must match a non-genomic target item's name
  * in the targetDb .2bit.  Do not free the result. */
 {
 static char accName[256];
 if (isEmpty(name))
     if (origPsl)
         return cloneString(origPsl->qName);
     else
         safecpy(accName, sizeof(accName), acc);
 else
     safef(accName, sizeof(accName), "%s__%s", acc, name);
 return accName;
 }
 
 char *pcrResultItemAccession(char *nameIn)
 /* If nameIn contains a concatenated accession and display name, returns
  * just the accession.  Do not free the result.*/
 {
 char *ptr = strstr(nameIn, "__");
 if (ptr != NULL)
     {
     static char nameOut[128];
     safecpy(nameOut, sizeof(nameOut), nameIn);
     nameOut[ptr-nameIn] = '\0';
     return nameOut;
     }
 return nameIn;
 }
 
 char *pcrResultItemName(char *nameIn)
 /* If nameIn contains a concatenated accession and display name, returns
  * just the name.  If accession only, returns NULL.  Do not free the result.*/
 {
 char *ptr = strstr(nameIn, "__");
 if (ptr != NULL)
     return ptr+2;
 return NULL;
 }