ee89e6768015d512b5ca8db8a62e8a129dfd5c7f
braney
  Sun Aug 23 15:56:57 2026 -0700
hgSession, cartReset: send the content policy header on their own pages

Both CGIs write their own http header block, so neither reaches addHttpHeaders
in cart.c, the hook that carries the policy header.  hgSession lets each output
path write its own Content-Type, and cartReset gets its header from
htmShellWithHead.

Add the call ahead of each html Content-Type: the two main page functions in
hgSession.c, three more in backup.c, and the one in cartReset.  The json paths
and the tar download are left alone, since they print no html.

With the hg.conf option off this changes nothing.  With it on, both pages now
carry the header, and its nonce matches the one in the meta tag and on the
inline script tags.

diff --git src/hg/hgSession/backup.c src/hg/hgSession/backup.c
index 8583af67bf5..f484092aaa7 100644
--- src/hg/hgSession/backup.c
+++ src/hg/hgSession/backup.c
@@ -44,30 +44,31 @@
 if(strstr(format, "needLargeMem:") || strstr(format, "carefulAlloc:"))
     htmlVaWarn("Region selected is too large for calculation. Please specify a smaller region or try limiting to fewer data points.", args);
 else
     {
     // call previous handler
     popWarnHandler();
     vaWarn(format, args);
     }
 if(isErrAbortInProgress())
     noWarnAbort();
 }
 
 static void vaHtmlOpen(char *format, va_list args)
 /* Start up a page that will be in html format. */
 {
+cspWriteResponseHeader();
 puts("Content-Type:text/html\n");
 cartVaWebStart(cart, database, format, args);
 pushWarnHandler(errAbortHandler);
 }
 
 void htmlOpen(char *format, ...)
 /* Start up a page that will be in html format. */
 {
 va_list args;
 va_start(args, format);
 vaHtmlOpen(format, args);
 va_end(args);
 }
 
 void htmlClose()
@@ -165,30 +166,31 @@
     htmlOpen("Background Status");
     errAbort("No complete html found");
     htmlClose();
     return;
     }
 int start;
 for (start=end; start >= 0 && ! (startsWith("<html>", lines[start]) || startsWith("<HTML>", lines[start])) ; --start)
     /* do nothing */ ;
 if (start < 0)
     {
     htmlOpen("Background Status");
     errAbort("No html start tag found");
     htmlClose();
     return;
     }
+cspWriteResponseHeader();
 puts("Content-Type: text/html\n");
 int line;
 boolean autoRefreshFound = FALSE;
 boolean successfullyUploaded = FALSE;
 for (line=start; line <= end; line++)
     {
     puts(lines[line]);
     if (startsWith("setTimeout(function(){location = location;}", lines[line]))
 	autoRefreshFound = TRUE;
     if (startsWith("Output has been successfully uploaded", lines[line]))
 	successfullyUploaded = TRUE;
     }
 // if it looks like the background is no longer running, 
 // include the .err stdout output for more informative problem message
 char urlErr[512];
@@ -705,30 +707,31 @@
     }
 
 printf("<br>\n");
 printf("<br>\n");
 
 }
 
 
 void showDownloadSessionCtData(struct hashEl *downloadList)
 /* Show download page for the given session */
 {
 char query[512];
 char **row = NULL;
 struct sqlResult *sr = NULL;
 
+cspWriteResponseHeader();
 puts("Content-Type:text/html\n");
 cartWebStart(cart, NULL, "Backup Custom Tracks");
 jsInit();
 
 struct sqlConnection *conn = hConnectCentral();
 
 printf("<FORM ACTION=\"%s\" NAME=\"mainForm\" METHOD=POST "
        "ENCTYPE=\"multipart/form-data\">\n",
        hgSessionName());
 cartSaveSession(cart);
 
 
 sqlSafef(query, sizeof(query), "SELECT firstUse, contents from %s "
     "WHERE id=%lu",
     "sessionDb", cartSessionRawId(cart));