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/hgHubConnect/hgHubConnect.c src/hg/hgHubConnect/hgHubConnect.c index e72e382..717ec44 100644 --- src/hg/hgHubConnect/hgHubConnect.c +++ src/hg/hgHubConnect/hgHubConnect.c @@ -91,46 +91,53 @@ dyStringPrintf(dy,"%s, ", trimmedName); if (dyShort->stringSize == 0 || (dyShort->stringSize+strlen(trimmedName)<=GENLISTWIDTH)) dyStringPrintf(dyShort,"%s, ", trimmedName); } char *genomesString = removeLastComma( dyStringCannibalize(&dy)); char *genomesShort = removeLastComma( dyStringCannibalize(&dyShort)); char tempHtml[1024+strlen(genomesString)+strlen(genomesShort)]; if (strlen(genomesShort) > GENLISTWIDTH) // If even the first element is too long, truncate it. genomesShort[GENLISTWIDTH] = 0; if (strlen(genomesShort)==strlen(genomesString)) { safef(tempHtml, sizeof tempHtml, "%s", genomesString); } else { + char id[256]; + char javascript[1024]; safef(tempHtml, sizeof tempHtml, - "<span id=Short%d " - "onclick=\"javascript:" + "<span id=Short%d>[+] %s...</span>" + "<span id=Full%d style=\"display:none\">[-]<br>%s</span>" + , row, genomesShort + , row, genomesString); + + safef(id, sizeof id, "Short%d", row); + safef(javascript, sizeof javascript, "document.getElementById('Short%d').style.display='none';" "document.getElementById('Full%d').style.display='inline';" - "return false;\">[+] %s...</span>" + "return false;" + , row, row); + jsOnEventById("click", id, javascript); - "<span id=Full%d " - "style=\"display:none\" " - "onclick=\"javascript:" + safef(id, sizeof id, "Full%d", row); + safef(javascript, sizeof javascript, "document.getElementById('Full%d').style.display='none';" "document.getElementById('Short%d').style.display='inline';" - "return false;\">[-]<br>%s</span>" - - , row, row, row, genomesShort - , row, row, row, genomesString); + "return false;" + , row, row); + jsOnEventById("click", id, javascript); } ourPrintCell(tempHtml); //ourPrintCell(removeLastComma( dyStringCannibalize(&dy))); } static void printGenomes(struct trackHub *thub, int row) /* print supported assembly names from trackHub */ { /* List of associated genomes. */ struct trackHubGenome *genomes = thub->genomeList; struct slName *list = NULL, *el; for(; genomes; genomes = genomes->next) { el = slNameNew(genomes->name); @@ -142,37 +149,42 @@ static void hgHubConnectUnlisted(struct hubConnectStatus *hubList, struct hash *publicHash) /* Put up the list of unlisted hubs and other controls for the page. */ /* uses publicHash to distingusih public hubs from unlisted ones */ /* NOTE: Destroys hubList */ { // put out the top of our page printf("<div id=\"unlistedHubs\" class=\"hubList\"> \n" "<table id=\"unlistedHubsTable\"> \n" "<thead><tr> \n" "<th colspan=\"6\" id=\"addHubBar\"><label for=\"hubUrl\">URL:</label> \n" "<input name=\"hubText\" id=\"hubUrl\" class=\"hubField\" " "type=\"text\" size=\"65\"> \n" - "<input name=\"hubAddButton\"" - "onClick=\"hubText.value=$.trim(hubText.value);if(validateUrl($('#hubUrl').val())) { " - "document.addHubForm.elements['hubUrl'].value=hubText.value;" - "document.addHubForm.submit();return true;} else { return false;}\" " + "<input name=\"hubAddButton\" id='hubAddButton' " "class=\"hubField\" type=\"button\" value=\"Add Hub\">\n" "</th> \n" "</tr> \n"); +jsOnEventById("click", "hubAddButton", + "var hubText = document.getElementById('hubUrl');" + "hubText.value=$.trim(hubText.value);" + "if(validateUrl($('#hubUrl').val())) { " + " document.addHubForm.elements['hubUrl'].value=hubText.value;" + " document.addHubForm.submit(); return true; } " + "else { return false; }" + ); // count up the number of unlisted hubs we currently have int unlistedHubCount = 0; struct hubConnectStatus *unlistedHubList = NULL; struct hubConnectStatus *hub, *nextHub; for(hub = hubList; hub; hub = nextHub) { nextHub = hub->next; // if url is not in publicHash, it's unlisted */ if (!((publicHash != NULL) && hashLookup(publicHash, hub->hubUrl))) { unlistedHubCount++; slAddHead(&unlistedHubList, hub); } @@ -189,72 +201,79 @@ } // time to output the big table. First the header printf( "<tr> " "<th>Display</th> " "<th>Hub Name</th> " "<th>Description</th> " "<th>Assemblies</th> " "</tr>\n" "</thead>\n"); // start first row printf("<tbody>"); +char id[256]; +char javascript[1024]; int count = 0; for(hub = unlistedHubList; hub; hub = hub->next) { char hubName[64]; safef(hubName, sizeof(hubName), "%s%u", hgHubConnectHubVarPrefix, hub->id); if (!cartUsualBoolean(cart, hubName, FALSE)) continue; if (count) webPrintLinkTableNewRow(); // ends last row and starts a new one count++; puts("<tr>"); ourCellStart(); - printf("<input name=\"hubDisconnectButton\"" - "onClick=" - "\" document.disconnectHubForm.elements['hubId'].value= '%d';" - "document.disconnectHubForm.submit();return true;\" " - "class=\"hubDisconnectButton\" type=\"button\" value=\"Disconnect\">\n", hub->id); + safef(id, sizeof id, "hubDisconnectButton%d", count); + printf("<input name=\"hubDisconnectButton\" id='%s' " + "class=\"hubDisconnectButton\" type=\"button\" value=\"Disconnect\">\n", id); + safef(javascript, sizeof javascript, + "document.disconnectHubForm.elements['hubId'].value='%d';" + "document.disconnectHubForm.submit(); return true;", hub->id); + jsOnEventById("click", id, javascript); ourCellEnd(); if (hub->trackHub != NULL) { ourPrintCellLink(hub->trackHub->shortLabel, hub->hubUrl); } else ourPrintCell(""); if (!isEmpty(hub->errorMessage)) { ourCellStart(); printf("<span class=\"hubError\">ERROR: %s </span>" "<a TARGET=_BLANK href=\"../goldenPath/help/hgTrackHubHelp.html#Debug\">Debug Help</a>\n", hub->errorMessage); + safef(id, sizeof id, "hubClearButton%d", count); // give people a chance to clear the error - printf("<input name=\"hubClearButton\"" - "onClick=\"document.resetHubForm.elements['hubCheckUrl'].value='%s';" - "document.resetHubForm.submit();return true;\" " + printf("<input name=\"hubClearButton\" id='%s' " "class=\"hubButton\" type=\"button\" value=\"Retry Hub\">" - , hub->hubUrl); + , id); + safef(javascript, sizeof javascript, + "document.resetHubForm.elements['hubCheckUrl'].value='%s';" + "document.resetHubForm.submit(); return true;", hub->hubUrl); + jsOnEventById("click", id, javascript); ourCellEnd(); } else if (hub->trackHub != NULL) { if (hub->trackHub->descriptionUrl != NULL) ourPrintCellLink(hub->trackHub->longLabel, hub->trackHub->descriptionUrl); else ourPrintCell(hub->trackHub->longLabel); } else ourPrintCell(""); if (hub->trackHub != NULL) printGenomes(hub->trackHub, count); else @@ -305,70 +324,72 @@ { char *trixFile = cfgOptionEnvDefault("HUBSEARCHTRIXFILE", "hubSearchTrixFile", "/gbdb/hubs/public.ix"); char *hubSearchTerms = cartOptionalString(cart, hgHubSearchTerms); char *cleanSearchTerms = cloneString(hubSearchTerms); boolean haveTrixFile = fileExists(trixFile); struct hash *urlSearchHash = NULL; printf("<div id=\"publicHubs\" class=\"hubList\"> \n"); // if we have a trix file, draw the search box if (haveTrixFile) { puts("Enter search terms to find in public track hub description pages:<BR>" "<input name=\"hubSearchTerms\" id=\"hubSearchTerms\" class=\"hubField\" " "type=\"text\" size=\"65\"> \n" - "<input name=\"hubSearchButton\"" - "onClick=" - "\" document.searchHubForm.elements['hubSearchTerms'].value=hubSearchTerms.value;" - "document.searchHubForm.submit();return true;\" " + "<input name=\"hubSearchButton\" id='hubSearchButton' " "class=\"hubField\" type=\"button\" value=\"Search Public Hubs\">\n"); + jsOnEventById("click", "hubSearchButton", + "document.searchHubForm.elements['hubSearchTerms'].value=$('#hubSearchTerms').val();" + "document.searchHubForm.submit();return true;"); puts("<BR><BR>\n"); } // if we have search terms, put out the line telling the user so if (haveTrixFile && !isEmpty(hubSearchTerms)) { printf("Displayed list <B>restricted by search terms:</B> %s\n", hubSearchTerms); - puts("<input name=\"hubDeleteSearchButton\"" - "onClick=" - "\" document.searchHubForm.elements['hubSearchTerms'].value=\'\';" - "document.searchHubForm.submit();return true;\" " + puts("<input name=\"hubDeleteSearchButton\" id='hubDeleteSearchButton' " "class=\"hubField\" type=\"button\" value=\"Show All Hubs\">\n"); + jsOnEventById("click", "hubDeleteSearchButton", + "document.searchHubForm.elements['hubSearchTerms'].value='';" + "document.searchHubForm.submit();return true;"); puts("<BR><BR>\n"); strLower(cleanSearchTerms); urlSearchHash = getUrlSearchHash(trixFile, cleanSearchTerms); } // make sure all the public hubs are in the hubStatus table. addPublicHubsToHubStatus(conn, publicTable, statusTable); struct hash *publicHash = newHash(5); char query[512]; bool hasDescription = sqlColumnExists(conn, publicTable, "descriptionUrl"); if (hasDescription) sqlSafef(query, sizeof(query), "select p.hubUrl,p.shortLabel,p.longLabel,p.dbList,s.errorMessage,s.id,p.descriptionUrl from %s p,%s s where p.hubUrl = s.hubUrl", publicTable, statusTable); else sqlSafef(query, sizeof(query), "select p.hubUrl,p.shortLabel,p.longLabel,p.dbList,s.errorMessage,s.id from %s p,%s s where p.hubUrl = s.hubUrl", publicTable, statusTable); struct sqlResult *sr = sqlGetResult(conn, query); char **row; int count = 0; boolean gotAnyRows = FALSE; +char jsId[256]; +char javascript[1024]; while ((row = sqlNextRow(sr)) != NULL) { ++count; char *url = row[0], *shortLabel = row[1], *longLabel = row[2], *dbList = row[3], *errorMessage = row[4], *descriptionUrl = row[6]; int id = atoi(row[5]); hashStore(publicHash, url); if ((urlSearchHash != NULL) && (hashLookup(urlSearchHash, url) == NULL)) continue; struct slName *dbListNames = slNameListFromComma(dbList); if (gotAnyRows) webPrintLinkTableNewRow(); @@ -384,83 +405,92 @@ "<th>Description</th> " "<th>Assemblies</th> " "</tr></thead>\n"); // start first row printf("<tbody> <tr>"); gotAnyRows = TRUE; } if (id != 0) { ourCellStart(); char hubName[32]; safef(hubName, sizeof(hubName), "%s%u", hgHubConnectHubVarPrefix, id); if (cartUsualBoolean(cart, hubName, FALSE)) - printf("<input name=\"hubDisconnectButton\"" - "onClick=" - "\" document.disconnectHubForm.elements['hubId'].value= '%d';" - "document.disconnectHubForm.submit();return true;\" " - "class=\"hubDisconnectButton\" type=\"button\" value=\"Disconnect\">\n", id); + { + safef(jsId, sizeof jsId, "hubDisconnectButton%d", count); + printf("<input name=\"hubDisconnectButton\" id='%s' " + "class=\"hubDisconnectButton\" type=\"button\" value=\"Disconnect\">\n", jsId); + safef(javascript, sizeof javascript, + "document.disconnectHubForm.elements['hubId'].value= '%d';" + "document.disconnectHubForm.submit();return true;", id); + jsOnEventById("click", jsId, javascript); + } else { // get first name off of list of supported databases char * name = dbListNames->name; // if the name isn't currently loaded, we assume it's a hub if (!hDbExists(name)) { char buffer[512]; safef(buffer, sizeof buffer, "hub_%d_%s", id, name); name = cloneString(buffer); } - printf("<input name=\"hubConnectButton\"" - "onClick=" - "\" document.connectHubForm.elements['hubUrl'].value= '%s';" + safef(jsId, sizeof jsId, "hubConnectButton%d", count); + printf("<input name=\"hubConnectButton\" id='%s' " + "class=\"hubButton\" type=\"button\" value=\"Connect\">\n", jsId); + safef(javascript, sizeof javascript, + "document.connectHubForm.elements['hubUrl'].value= '%s';" "document.connectHubForm.elements['db'].value= '%s';" - "document.connectHubForm.submit();return true;\" " - "class=\"hubButton\" type=\"button\" value=\"Connect\">\n", url,name); + "document.connectHubForm.submit();return true;", url,name); + jsOnEventById("click", jsId, javascript); } ourCellEnd(); } else errAbort("cannot get id for hub with url %s\n", url); ourPrintCellLink(shortLabel, url); if (isEmpty(errorMessage)) { if (hasDescription && !isEmpty(descriptionUrl)) ourPrintCellLink(longLabel, descriptionUrl); else ourPrintCell(longLabel); } else { ourCellStart(); printf("<span class=\"hubError\">ERROR: %s </span>" "<a href=\"../goldenPath/help/hgTrackHubHelp.html#Debug\">Debug Help</a>", errorMessage); + safef(jsId, sizeof jsId, "hubClearButton%d", count); printf( - "<input name=\"hubClearButton\"" - "onClick=\"document.resetHubForm.elements['hubCheckUrl'].value='%s';" - "document.resetHubForm.submit();return true;\" " + "<input name=\"hubClearButton\" id='%s' " "class=\"hubButton\" type=\"button\" value=\"Retry Hub\">" - , url); + , jsId); + safef(javascript, sizeof javascript, + "document.resetHubForm.elements['hubCheckUrl'].value='%s';" + "document.resetHubForm.submit();return true;", url); + jsOnEventById("click", jsId, javascript); ourCellEnd(); } printGenomeList(dbListNames, count); } sqlFreeResult(&sr); if (gotAnyRows) printf("</TR></tbody></TABLE>\n"); printf("</div>"); *pHash = publicHash; return gotAnyRows; } @@ -724,32 +754,32 @@ struct hash *publicHash = hgHubConnectPublic(); hgHubConnectUnlisted(hubList, publicHash); printf("</div>"); printf("<div class=\"tabFooter\">"); char *emailAddress = cfgOptionDefault("hub.emailAddress","genome@soe.ucsc.edu"); printf("<span class=\"small\">" "Contact <A HREF=\"mailto:%s\">%s</A> to add a public hub." "</span>\n", emailAddress,emailAddress); printf("</div>"); cgiMakeHiddenVar(hgHubConnectRemakeTrackHub, "on"); -printf("</div>\n"); puts("</FORM>"); +printf("</div>\n"); cartWebEnd(); } char *excludeVars[] = {"Submit", "submit", "hc_one_url", hgHubCheckUrl, hgHubDoClear, hgHubDoDisconnect,hgHubDoRedirect, hgHubDataText, hgHubConnectRemakeTrackHub, NULL}; int main(int argc, char *argv[]) /* Process command line. */ { long enteredMainTime = clock1000(); oldVars = hashNew(10); cgiSpoof(&argc, argv); cartEmptyShell(doMiddle, hUserCookie(), excludeVars, oldVars); cgiExitTime("hgHubConnect", enteredMainTime);