a53b9958fa734f73aeffb9ddfe2fbad1ca65f90c galt Mon Jan 30 16:18:41 2017 -0800 Check-in of CSP2 Content-Security-Policy work. All C-language CGIs should now support CSP2 in browser to stop major forms of XSS javascript injection. Javascript on pages is gathered together, and then emitted in a single script block at the end with a nonce that tells the browser, this is js that we generated instead of being injected by a hacker. Both inline script from script blocks and inline js event handlers had to be pulled out and separated. You will not see js sprinkled through-out the page now. Older browsers that support CSP1 or that do not understand CSP at all will still work, just without protection. External js libraries loaded at runtime need to be added to the CSP policy header in src/lib/htmshell.c. diff --git src/hg/lib/web.c src/hg/lib/web.c index 6c29c02..455e911 100644 --- src/hg/lib/web.c +++ src/hg/lib/web.c @@ -21,52 +21,54 @@ #include "search.h" #include "geoMirror.h" #include "trackHub.h" #include "versionInfo.h" #ifndef GBROWSE #include "axtInfo.h" #include "wikiLink.h" #include "googleAnalytics.h" #include "jsHelper.h" #endif /* GBROWSE */ /* flag that tell if the CGI header has already been outputed */ boolean webHeadAlreadyOutputed = FALSE; -/* flag that tell if text CGI header hsa been outputed */ +/* flag that tell if text CGI header has been outputed */ boolean webInTextMode = FALSE; struct hash *includedResourceFiles = NULL; static char *dbCgiName = "db"; static char *orgCgiName = "org"; static char *cladeCgiName = "clade"; static char *extraStyle = NULL; /* globals: a cart and db for use in error handlers. */ static struct cart *errCart = NULL; static char *errDb = NULL; void textVaWarn(char *format, va_list args) { vprintf(format, args); puts("\n"); } void softAbort() { +if (!webInTextMode) + webEnd(); exit(0); } void webPushErrHandlers(void) /* Push warn and abort handler for errAbort(). */ { if (webInTextMode) pushWarnHandler(textVaWarn); else pushWarnHandler(webVaWarn); pushAbortHandler(softAbort); hDumpStackPushAbortHandler(); } void webPushErrHandlersCartDb(struct cart *cart, char *db) @@ -165,87 +167,89 @@ if (withHtmlHeader) { char *newString, *ptr1, *ptr2; char *browserVersion; if (btIE == cgiClientBrowser(&browserVersion, NULL, NULL) && *browserVersion < '8') puts(""); else puts(""); // Strict would be nice since it fixes atleast one IE problem (use of :hover CSS pseudoclass) puts( "" "\n" "" "\n" ); + generateCspMetaHeader(stdout); printf("\t%s\n", headerText); webPragmasEtc(); printf("\t"); /* we need to take any HTML formatting out of the titlebar string */ newString = cloneString(textOutBuf); for(ptr1=newString, ptr2=textOutBuf; *ptr2 ; ptr2++) { if (*ptr2 == '<') { for(; *ptr2 && (*ptr2 != '>'); ptr2++) ; } else *ptr1++ = *ptr2; } *ptr1 = 0; htmlTextOut(newString); printf(" \n "); webIncludeResourceFile("HGStyle.css"); if (extraStyle != NULL) puts(extraStyle); printf("\n"); printBodyTag(stdout); htmlWarnBoxSetup(stdout);// Sets up a warning box which can be filled with errors as they occur puts(commonCssStyles()); } + +/* Put up the hot links bar. */ + +char *menuStr = menuBar(theCart, db); +if(menuStr) + { + puts(menuStr); + } + webStartSectionTables(); if (withLogo) { puts(""); if (isEncode) { puts("" "\"ENCODE" ""); puts("\"ENCODE"); } else { puts(""); } puts("" "\n" "" "\n" ); } -/* Put up the hot links bar. */ - -char *menuStr = menuBar(theCart, db); -if(menuStr) - { - puts(menuStr); - } - if(!skipSectionHeader) /* this HTML must be in calling code if skipSectionHeader is TRUE */ { webFirstSection(textOutBuf); }; webPushErrHandlers(); /* set the flag */ webHeadAlreadyOutputed = TRUE; errAbortSetDoContentType(FALSE); } /* static void webStartWrapperDetailedInternal() */ void webStartWrapperDetailedArgs(struct cart *theCart, char *db, char *headerText, char *format, va_list args, boolean withHttpHeader, boolean withLogo, boolean skipSectionHeader, boolean withHtmlHeader) /* output a CGI and HTML header with the given title in printf format */ @@ -413,85 +417,91 @@ * webEnd does. */ { webEndSection(); puts("\n"); } void webEnd() /* output the footer of the HTML page */ { if(!webInTextMode) { webEndSectionTables(); #ifndef GBROWSE googleAnalytics(); #endif /* GBROWSE */ + jsInlineFinish(); puts( ""); webPopErrHandlers(); } } static void webStartGbOptionalBanner(struct cart *cart, char *db, char *title, boolean doBanner, boolean hgGateway) /* Start HTML with new header and footer design by JWest. Optionally display banner above menubar. Use flag with hgGateway, till that is migrated. */ { puts("Content-type:text/html\n"); + +char *csp = getCspMetaHeader(); if (hgGateway) { printf( #include "jWestHeader.h" - , title); + , title, title); } else { printf( #include "gbHeader.h" , title); } if (doBanner) { printf( #include "jWestBanner.h" , title); } +freeMem(csp); + webPushErrHandlersCartDb(cart, db); htmlWarnBoxSetup(stdout); // Add hotlinks bar char *navBar = menuBar(cart, db); if (navBar) { puts(navBar); // Override nice-menu.css's menu background and fonts: puts(""); } webHeadAlreadyOutputed = TRUE; errAbortSetDoContentType(FALSE); } void webStartGbNoBanner(struct cart *cart, char *db, char *title) /* Start HTML with new header and footer design by jWest, but no banner */ { webStartGbOptionalBanner(cart, db, title, FALSE, FALSE); } void webEndGb() /* End HTML that was started with webStartJWest. */ { googleAnalytics(); +jsInlineFinish(); puts(""); webPopErrHandlers(); } void webStartJWest(struct cart *cart, char *db, char *title) /* Start HTML with new banner and footer design by jWest (with modifications). */ { webStartGbOptionalBanner(cart, db, title, TRUE, TRUE); } void webEndJWest() /* End HTML that was started with webStartJWest. */ { webEndGb(); } @@ -534,31 +544,31 @@ /* in text mode, have a different error */ if(webInTextMode) printf("\n\n\n %s\n\n", title); vprintf(format, args); printf("\n"); printf("\n\n"); webEnd(); va_end(args); exit(0); } -void printCladeListHtml(char *genome, char *onChangeText) +void printCladeListHtml(char *genome, char *event, char *javascript) /* Make an HTML select input listing the clades. */ { char **row = NULL; char *clades[128]; char *labels[128]; char *defaultClade = hClade(genome); char *defaultLabel = NULL; int numClades = 0; struct sqlConnection *conn = hConnectCentral(); // after hClade since it access hgcentral too // get only the clades that have actual active genomes char query[4096]; sqlSafef(query, sizeof query, "SELECT DISTINCT(c.name), c.label " // mysql 5.7: SELECT list w/DISTINCT must include all fields in ORDER BY list (#18626) ", c.priority " @@ -581,35 +591,35 @@ struct slPair *names = trackHubGetCladeLabels(); for(; names; names = names->next) { clades[numClades] = names->name; labels[numClades] = names->val; if (sameWord(defaultClade, clades[numClades])) defaultLabel = clades[numClades]; numClades++; if (numClades >= ArraySize(clades)) internalErr(); } cgiMakeDropListFull(cladeCgiName, labels, clades, numClades, - defaultLabel, onChangeText); + defaultLabel, event, javascript); } static void printSomeGenomeListHtmlNamedMaybeCheck(char *customOrgCgiName, - char *db, struct dbDb *dbList, char *onChangeText, boolean doCheck) + char *db, struct dbDb *dbList, char *event, char *javascript, boolean doCheck) /* Prints to stdout the HTML to render a dropdown list * containing a list of the possible genomes to choose from. * param db - a database whose genome will be the default genome. * If NULL, no default selection. * param onChangeText - Optional (can be NULL) text to pass in * any onChange javascript. */ { char *orgList[1024]; int numGenomes = 0; struct dbDb *cur = NULL; struct hash *hash = hashNew(10); // 2^^10 entries = 1024 char *selGenome = hGenome(db); char *values [1024]; char *cgiName; @@ -617,100 +627,100 @@ { if (!hashFindVal(hash, cur->genome) && (!doCheck || hDbExists(cur->name))) { hashAdd(hash, cur->genome, cur); orgList[numGenomes] = trackHubSkipHubName(cur->genome); values[numGenomes] = cur->genome; numGenomes++; if (numGenomes >= ArraySize(orgList)) internalErr(); } } cgiName = (customOrgCgiName != NULL) ? customOrgCgiName : orgCgiName; cgiMakeDropListFull(cgiName, orgList, values, numGenomes, - selGenome, onChangeText); + selGenome, event, javascript); hashFree(&hash); } -void printSomeGenomeListHtmlNamed(char *customOrgCgiName, char *db, struct dbDb *dbList, char *onChangeText) +void printSomeGenomeListHtmlNamed(char *customOrgCgiName, char *db, struct dbDb *dbList, char *event, char *javascript) /* Prints to stdout the HTML to render a dropdown list * containing a list of the possible genomes to choose from. * param db - a database whose genome will be the default genome. * If NULL, no default selection. - * param onChangeText - Optional (can be NULL) text to pass in - * any onChange javascript. */ + * param event e.g. "change" + * javascript - Optional (can be NULL) onEvent javascript. */ { return printSomeGenomeListHtmlNamedMaybeCheck(customOrgCgiName, db, dbList, - onChangeText, TRUE); + event, javascript, TRUE); } void printLiftOverGenomeList(char *customOrgCgiName, char *db, - struct dbDb *dbList, char *onChangeText) + struct dbDb *dbList, char *event, char *javascript) /* Prints to stdout the HTML to render a dropdown list * containing a list of the possible genomes to choose from. * Databases in dbList do not have to exist. * param db - a database whose genome will be the default genome. * If NULL, no default selection. - * param onChangeText - Optional (can be NULL) text to pass in - * any onChange javascript. */ + * param event e.g. "change" + * javascript - Optional (can be NULL) onEvent javascript. */ { return printSomeGenomeListHtmlNamedMaybeCheck(customOrgCgiName, db, dbList, - onChangeText, FALSE); + event, javascript, FALSE); } -void printSomeGenomeListHtml(char *db, struct dbDb *dbList, char *onChangeText) +void printSomeGenomeListHtml(char *db, struct dbDb *dbList, char *event, char *javascript) /* Prints the dropdown list using the orgCgiName */ { -printSomeGenomeListHtmlNamed(NULL, db, dbList, onChangeText); +printSomeGenomeListHtmlNamed(NULL, db, dbList, event, javascript); } -void printGenomeListHtml(char *db, char *onChangeText) +void printGenomeListHtml(char *db, char *event, char *javascript) /* Prints to stdout the HTML to render a dropdown list * containing a list of the possible genomes to choose from. * param db - a database whose genome will be the default genome. * If NULL, no default selection. - * param onChangeText - Optional (can be NULL) text to pass in - * any onChange javascript. */ + * param event e.g. "change" + * javascript - Optional (can be NULL) onEvent javascript. */ { -printSomeGenomeListHtml(db, hGetIndexedDatabases(), onChangeText); +printSomeGenomeListHtml(db, hGetIndexedDatabases(), event, javascript); } -void printBlatGenomeListHtml(char *db, char *onChangeText) +void printBlatGenomeListHtml(char *db, char *event, char *javascript) /* Prints to stdout the HTML to render a dropdown list * containing a list of the possible genomes to choose from. * param db - a database whose genome will be the default genome. * If NULL, no default selection. - * param onChangeText - Optional (can be NULL) text to pass in - * any onChange javascript. */ + * param event e.g. "change" + * javascript - Optional (can be NULL) onEvent javascript. */ { -printSomeGenomeListHtml(db, hGetBlatIndexedDatabases(), onChangeText); +printSomeGenomeListHtml(db, hGetBlatIndexedDatabases(), event, javascript); } -void printGenomeListForCladeHtml(char *db, char *onChangeText) +void printGenomeListForCladeHtml(char *db, char *event, char *javascript) /* Prints to stdout the HTML to render a dropdown list containing * a list of the possible genomes from selOrganism's clade to choose from. * selOrganism is the default for the select. */ { -printSomeGenomeListHtml(db, hGetIndexedDatabasesForClade(db), onChangeText); +printSomeGenomeListHtml(db, hGetIndexedDatabasesForClade(db), event, javascript); } void printAllAssemblyListHtmlParm(char *db, struct dbDb *dbList, - char *dbCgi, bool allowInactive, char *javascript) + char *dbCgi, bool allowInactive, char *event, char *javascript) /* Prints to stdout the HTML to render a dropdown list containing the list * of assemblies for the current genome to choose from. By default, * this includes only active assemblies with a database (with the * exception of the default assembly, which will be included even * if it isn't active). * param db - The default assembly (the database name) to choose as selected. * If NULL, no default selection. * param allowInactive - if set, print all assemblies for this genome, * even if they're inactive or have no database */ { char *assemblyList[128]; char *values[128]; int numAssemblies = 0; @@ -735,88 +745,88 @@ selAssembly = cur->name; if (allowInactive || ((cur->active || sameWord(cur->name, db)) && (trackHubDatabase(db) || sqlDatabaseExists(cur->name)))) { assemblyList[numAssemblies] = cur->description; values[numAssemblies] = cur->name; numAssemblies++; if (numAssemblies >= ArraySize(assemblyList)) internalErr(); } } cgiMakeDropListFull(dbCgi, assemblyList, values, numAssemblies, - selAssembly, javascript); + selAssembly, event, javascript); } void printSomeAssemblyListHtmlParm(char *db, struct dbDb *dbList, - char *dbCgi, char *javascript) + char *dbCgi, char *event, char *javascript) /* Find all the assemblies from the list that are active. * Prints to stdout the HTML to render a dropdown list containing the list * of the possible assemblies to choose from. * param db - The default assembly (the database name) to choose as selected. * If NULL, no default selection. */ { - printAllAssemblyListHtmlParm(db, dbList, dbCgi, TRUE, javascript); + printAllAssemblyListHtmlParm(db, dbList, dbCgi, TRUE, event, javascript); } -void printSomeAssemblyListHtml(char *db, struct dbDb *dbList, char *javascript) +void printSomeAssemblyListHtml(char *db, struct dbDb *dbList, char *event, char *javascript) /* Find all assemblies from the list that are active, and print * HTML to render dropdown list * param db - default assembly. If NULL, no default selection */ { -printSomeAssemblyListHtmlParm(db, dbList, dbCgiName, javascript); +printSomeAssemblyListHtmlParm(db, dbList, dbCgiName, event, javascript); } -void printAssemblyListHtml(char *db, char *javascript) +void printAssemblyListHtml(char *db, char *event, char *javascript) /* Find all the assemblies that pertain to the selected genome * Prints to stdout the HTML to render a dropdown list containing * a list of the possible assemblies to choose from. * Param db - The assembly (the database name) to choose as selected. * If NULL, no default selection. */ { struct dbDb *dbList = hGetIndexedDatabases(); -printSomeAssemblyListHtml(db, dbList, javascript); +printSomeAssemblyListHtml(db, dbList, event, javascript); } -void printAssemblyListHtmlExtra(char *db, char *javascript) +void printAssemblyListHtmlExtra(char *db, char *event, char *javascript) { /* Find all the assemblies that pertain to the selected genome Prints to stdout the HTML to render a dropdown list containing a list of the possible assemblies to choose from. param curDb - The assembly (the database name) to choose as selected. If NULL, no default selection. */ struct dbDb *dbList = hGetIndexedDatabases(); -printSomeAssemblyListHtmlParm(db, dbList, dbCgiName, javascript); +printSomeAssemblyListHtmlParm(db, dbList, dbCgiName, event, javascript); } void printBlatAssemblyListHtml(char *db) { /* Find all the assemblies that pertain to the selected genome Prints to stdout the HTML to render a dropdown list containing a list of the possible assemblies to choose from. param curDb - The assembly (the database name) to choose as selected. If NULL, no default selection. */ struct dbDb *dbList = hGetBlatIndexedDatabases(); -printSomeAssemblyListHtml(db, dbList, NULL); +printSomeAssemblyListHtml(db, dbList, NULL, NULL); } static char *getDbForGenome(char *genome, struct cart *cart) /* Function to find the default database for the given Genome. It looks in the cart first and then, if that database's Genome matches the passed-in Genome, returns it. If the Genome does not match, it returns the default database that does match that Genome. param Genome - The Genome for which to find a database param cart - The cart to use to first search for a suitable database name return - The database matching this Genome type */ { @@ -1552,36 +1562,38 @@ char query[1056]; sqlSafef(query, sizeof query, "select domain from gbNode%s where node = %d", geoSuffix, node); char *newDomain = sqlQuickString(centralConn, query); char *oldDomain = cgiServerName(); char *port = cgiServerPort(); char *uri = cgiRequestUri(); char *sep = strchr(uri, '?') ? "&" : "?"; int newUriSize = strlen(uri) + 1024; char *newUri = needMem(newUriSize); char *oldUri = needMem(newUriSize); safef(oldUri, newUriSize, "http%s://%s:%s%s%sredirect=manual&source=%s", cgiServerHttpsIsOn() ? "s" : "", oldDomain, port, uri, sep, oldDomain); safef(newUri, newUriSize, "http%s://%s:%s%s%sredirect=manual&source=%s", cgiServerHttpsIsOn() ? "s" : "", newDomain, port, uri, sep, oldDomain); - printf("" + printf("" "
" "

" " You might want to navigate to your nearest mirror - %s" "

" "
\n"); + jsInlineFinish(); exit(0); } hDisconnectCentral(¢ralConn); } } }