21123cb5a9a1542b7ff01542f29078adc04764a0
max
  Sun Sep 6 07:31:34 2026 -0700
lib: check every component in isSafeRelativePath

The loop that walks the '/'-separated components stepped to the terminator that
replaced the slash instead of past it, so it ended after the first component and
the rest of the path went unchecked.  Callers are gfServer's dynamic mode, which
takes genome and genomeDataDir from the client.

diff --git src/lib/filePath.c src/lib/filePath.c
index 111874ed9ea..094257139c3 100644
--- src/lib/filePath.c
+++ src/lib/filePath.c
@@ -221,31 +221,31 @@
     return FALSE;
 char tmpPath[PATH_LEN];
 safecpy(tmpPath, sizeof(tmpPath), path);
 
 char *p = tmpPath;
 while (TRUE)
     {
     char *end = strchr(p, '/');
     if (end != NULL)
         *end = '\0';
     if (sameString(p, ".."))
         return FALSE;
     if (end == NULL)
         break;
     else
-        p = end++;
+        p = end + 1;
     }
 return TRUE;
 }
 
 char *resolveDotDots(char *pathOrUrl)
 /* Given a file path or URL, return a version with ".." components resolved.
  * For URLs, only the path portion is simplified (scheme :// is preserved).
  * Double slashes are collapsed for file paths but not for URLs.
  * Result should be freeMem'd. */
 {
 if (hasProtocol(pathOrUrl))
     {
     struct netParsedUrl npu;
     netParseUrl(pathOrUrl, &npu);
     eatExcessDotsInPath(npu.file);