2eb14fc0378432977d83366a3211c67f8913018d
max
  Mon Sep 21 06:21:51 2026 -0700
hgc and hgTrackUi: open off-site links in a new tab, with rel="noopener noreferrer"

offsiteLinksToNewTab() in utils.js runs over the rendered page alongside
addHgsidToLinks() and gives every http(s) link whose host is not ours a
target=_blank it does not already have, plus rel="noopener noreferrer".  Without
noopener the page that opens keeps a handle on the tab it came from and can
navigate it; without noreferrer the Referer header carries our own URL, which has
the session id in it.  The href on a track description page is written by whoever
wrote the track or the hub, so neither is theoretical.  A mailto: or an ftp: link
is left alone, and so is every link that stays on this server.

jsAddHgsidToLinks() now emits both calls and is renamed jsFixUpPageLinks().
hgTracks.js runs the same pass over the track description popup: the replace it
does before that already puts a target on every link in there, so what this adds
is the rel on the ones that leave.

refs #38380

diff --git src/hg/hgTrackUi/hgTrackUi.c src/hg/hgTrackUi/hgTrackUi.c
index d7edeb8f5e5..bbb9878110f 100644
--- src/hg/hgTrackUi/hgTrackUi.c
+++ src/hg/hgTrackUi/hgTrackUi.c
@@ -4772,31 +4772,31 @@
                 "<span style='background-color: #c3d4f4; "
                     "padding-left: 10px; padding-right: 10px;"
                     "margin-right: 10px; margin-left: -8px;'>"
                        "%s</span> %s", 
                 htmlEncode(tdb->parent->shortLabel), htmlEncode(tdb->shortLabel));
         }
     else
         dyStringPrintf(title, "%s", htmlEncode(tdb->shortLabel));
     char *titleEnd = (tdbIsSuper(tdb) ? "Tracks" :
                tdbIsDownloadsOnly(tdb) ? DOWNLOADS_ONLY_TITLE : "Track Settings");
     htmlNoEscape();     // allow HTML tags to format title blue bar (using short label)
     cartWebStart(cart, database, "%s %s", title->string, titleEnd);
     htmlDoEscape();
     trackUi(tdb, tdbList, ct, FALSE);
     printf("<BR>\n");
-    jsAddHgsidToLinks();
+    jsFixUpPageLinks();
     jsonPrintGlobals();
     webEnd();
     }
 }
 
 char *excludeVars[] = { "submit", "Submit", "g", "fileUrl", "track", "sourceDb", NULL, "ajax", NULL,};
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 long enteredMainTime = clock1000();
 /* 0, 0, == use default 10 second for warning, 20 second for immediate exit */
 issueBotWarning = earlyBotCheck(enteredMainTime, "hgTrackUi", delayFraction, 0, 0, "html");
 cgiSpoof(&argc, argv);
 cartEmptyShellNoContent(doMiddle, hUserCookie(), excludeVars, NULL);