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/lib/jsHelper.c src/hg/lib/jsHelper.c index 2c0210f244d..71be8666f3b 100644 --- src/hg/lib/jsHelper.c +++ src/hg/lib/jsHelper.c @@ -319,43 +319,46 @@ , stateString); return loadFunction; } char *jsCheckAllOnClickHandler(char *idPrefix, boolean state) /* Returns javascript for use as an onclick attribute value to check all/uncheck all * all checkboxes with given idPrefix. * state parameter determines whether to "check all" or "uncheck all" (TRUE means "check all"). */ { static char buf[512]; jsIncludeFile("utils.js", NULL); safef(buf, sizeof(buf), "setCheckBoxesWithPrefix(this, '%s', %s); return false", idPrefix, state ? "true" : "false"); return buf; } -void jsAddHgsidToLinks() -/* Emit the javascript that gives every link on this page to one of our own CGIs the current - * session id. See addHgsidToLinks() in utils.js: a track description page comes from - * whoever wrote the track or the hub, so the session id cannot be substituted into it on - * the server without also handing it to an that points somewhere else. */ +void jsFixUpPageLinks() +/* Emit the javascript that tidies up this page's links once it is rendered: our own CGI + * links get the current session id, and links that leave this server open in a new tab with + * rel="noopener noreferrer". See addHgsidToLinks() and offsiteLinksToNewTab() in utils.js. + * A track description page comes from whoever wrote the track or the hub, so the session id + * cannot be substituted into it on the server without also handing it to an that + * points somewhere else. */ { static boolean done = FALSE; if (done) return; done = TRUE; jsIncludeFile("utils.js", NULL); // jsInlineFinish() writes this at the end of the body, so the page is parsed by then -jsInline("addHgsidToLinks(document);\n"); +jsInline("addHgsidToLinks(document);\n" + "offsiteLinksToNewTab(document);\n"); } /* cgiMakeCheckAllSubmitButton really belongs in cheapcgi.c, but that is compiled without access to jsHelper.h */ void cgiMakeCheckAllSubmitButton(char *name, char *value, char *id, char *idPrefix, boolean state) /* Make submit button which uses javascript to apply check all or uncheck all to all * checkboxes with given idPrefix. * state parameter determines whether to "check all" or "uncheck all" (TRUE means "check all"). * id parameter may be NULL */ { cgiMakeOnClickSubmitButton(jsCheckAllOnClickHandler(idPrefix, state), name, value); } char *stripRegEx(char *str, char *regEx, int flags) {