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/hgFileSearch/hgFileSearch.c src/hg/hgFileSearch/hgFileSearch.c
index 48f91e1..fe0f8f4 100644
--- src/hg/hgFileSearch/hgFileSearch.c
+++ src/hg/hgFileSearch/hgFileSearch.c
@@ -1,479 +1,481 @@
 /* Copyright (C) 2013 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 
 #include "common.h"
 #include "obscure.h"
 #include "hash.h"
 #include "cheapcgi.h"
 #include "htmshell.h"
 #include "jsHelper.h"
 #include "trackDb.h"
 #include "hdb.h"
 #include "web.h"
 #include "mdb.h"
 #include "hCommon.h"
 #include "hui.h"
 #include "fileUi.h"
 #include "search.h"
 #include "cart.h"
 #include "grp.h"
 
 #define FAKE_MDB_MULTI_SELECT_SUPPORT
 
 struct hash *trackHash = NULL;  // Is this needed?
 boolean measureTiming = FALSE;  /* DON'T EDIT THIS -- use CGI param "&measureTiming=." . */
 
 #define FILE_SEARCH_WHAT "Downloadable ENCODE Files"
 #define FILE_SEARCH_NAME FILE_SEARCH_WHAT " Search"
 
 #define FILE_SEARCH              "hgfs_Search"
 #define FILE_SEARCH_FORM         "fileSearch"
 #define FILE_SEARCH_CURRENT_TAB  "fsCurTab"
 #define FILE_SEARCH_ON_FILETYPE  "fsFileType"
 
 // These are common with trackSearch.  Should they be?
 #define TRACK_SEARCH_SIMPLE      "tsSimple"
 #define TRACK_SEARCH_ON_NAME     "tsName"
 #define TRACK_SEARCH_ON_GROUP    "tsGroup"
 #define TRACK_SEARCH_ON_DESCR    "tsDescr"
 #define TRACK_SEARCH_SORT        "tsSort"
 
 //#define USE_TABS
 
 static struct trackDb *tdbFilterBy(struct trackDb **pTdbList, char *name, char *description, 
                                    char *group)
 // returns tdbs that match supplied criterion, leaving unmatched in list passed in
 {
 // Set the word lists up once
 struct slName *nameList = NULL;
 if (name)
     nameList = slNameListOfUniqueWords(cloneString(name),TRUE); // TRUE means respect quotes
 struct slName *descList = NULL;
 if (description)
     descList = slNameListOfUniqueWords(cloneString(description),TRUE);
 
 struct trackDb *tdbList = *pTdbList;
 struct trackDb *tdbRejects = NULL;
 struct trackDb *tdbMatched = NULL;
 
 while (tdbList != NULL)
     {
     struct trackDb *tdb = slPopHead(&tdbList);
 
     if (!tdbIsComposite(tdb))
         slAddHead(&tdbRejects,tdb);
     else if (group && differentString(tdb->grp,group))
         slAddHead(&tdbRejects,tdb);
     else if (name && !searchNameMatches(tdb, nameList))
         slAddHead(&tdbRejects,tdb);
     else if (description && !searchDescriptionMatches(tdb, descList))
         slAddHead(&tdbRejects,tdb);
     else
         slAddHead(&tdbMatched,tdb);
     }
 *pTdbList = tdbRejects;
 
 //warn("matched %d tracks",slCount(tdbMatched));
 return tdbMatched;
 }
 
 static boolean mdbSelectsAddFoundComposites(struct slPair **pMdbSelects,struct trackDb *tdbsFound)
 // Adds a composite mdbSelect (if found in tdbsFound) to the head of the pairs list.
 // If tdbsFound is NULL, then add dummy composite search criteria
 {
 // create comma separated list of composites
 struct dyString *dyComposites = dyStringNew(256);
 struct trackDb *tdb = tdbsFound;
 for (;tdb != NULL; tdb = tdb->next)
     {
     if (tdbIsComposite(tdb))
         dyStringPrintf(dyComposites,"%s,",tdb->track);
     else if (tdbIsCompositeChild(tdb))
         {
         struct trackDb *composite = tdbGetComposite(tdb);
         dyStringPrintf(dyComposites,"%s,",composite->track);
         }
     }
 if (dyStringLen(dyComposites) > 0)
     {
     char *composites = dyStringCannibalize(&dyComposites);
     composites[strlen(composites) - 1] = '\0';  // drop the last ','
     slPairAdd(pMdbSelects,MDB_VAR_COMPOSITE,composites); 
     // Composite should not already be in the list, because it is only indirectly sortable
     return TRUE;
     }
 
 //warn("No composites found");
 dyStringFree(&dyComposites);
 return FALSE;
 }
 
 #ifdef USE_TABS
 static struct slRef *simpleSearchForTdbs(struct trix *trix,char **descWords,int descWordCount)
 // Performs the simple search and returns the found tracks.
 {
 struct slRef *foundTdbs = NULL;
 
 struct trixSearchResult *tsList;
 for (tsList = trixSearch(trix, descWordCount, descWords, tsmExpand);
      tsList != NULL;
      tsList = tsList->next)
     {
     struct trackDb *tdb = (struct track *) hashFindVal(trackHash, tsList->itemId);
     if (track != NULL)  // It is expected that this is NULL 
         {               // (e.g. when the trix references trackDb tracks which have no tables)
         refAdd(&foundTdbs, tdb);
         }
     }
 return foundTdbs;
 }
 #endif///def USE_TABS
 
 struct slName *tdbListGetGroups(struct trackDb *tdbList)
 // Returns a list of groups found in the tdbList
 // FIXME: Should be moved to trackDbCustom and shared
 {
 struct slName *groupList = NULL;
 char *lastGroup = "[]";
 struct trackDb *tdb = tdbList;
 for (;tdb!=NULL;tdb=tdb->next)
     {
     if (differentString(lastGroup,tdb->grp))
         lastGroup = slNameStore(&groupList, tdb->grp);
     }
 return groupList;
 }
 
 struct grp *groupsFilterForTdbList(struct grp **grps,struct trackDb *tdbList)
 {
 struct grp *grpList = *grps;
 *grps = NULL;
 struct slName *tdbGroups = tdbListGetGroups(tdbList);
 if (tdbList == NULL)
     return *grps;
 
 while (grpList != NULL)
     {
     struct grp *grp = slPopHead(&grpList);
     if (slNameInList(tdbGroups,grp->name))
         slAddHead(grps,grp);
     }
 slNameFreeList(&tdbGroups);
 slReverse(grps);
 return *grps;
 }
 
 static void doFileSearch(char *db,char *organism,struct cart *cart,struct trackDb *tdbList)
 {
 struct sqlConnection *conn = hAllocConn(db);
 boolean metaDbExists = sqlTableExists(conn, "metaDb");
 if (!sqlTableExists(conn, "metaDb"))
     {
     warn("Assembly %s %s does not support Downloadable Files search.", organism, hFreezeFromDb(db));
     hFreeConn(&conn);
     return;
     }
 char *nameSearch = cartOptionalString(cart, TRACK_SEARCH_ON_NAME);
 char *descSearch=NULL;
 char *fileTypeSearch = cartOptionalString(cart, FILE_SEARCH_ON_FILETYPE);
 boolean doSearch = sameWord(cartUsualString(cart, FILE_SEARCH,"no"), "search");
 #ifdef ONE_FUNC
 struct hash *parents = newHash(4);
 #endif///def ONE_FUNC
 int cols;
 
 #ifdef USE_TABS
 enum searchTab selectedTab = simpleTab;
 char *currentTab = cartUsualString(cart, FILE_SEARCH_CURRENT_TAB, "simpleTab");
 if (sameString(currentTab, "simpleTab"))
     {
     selectedTab = simpleTab;
     descSearch = cartOptionalString(cart, TRACK_SEARCH_SIMPLE);
     freez(&nameSearch);
     }
 else if (sameString(currentTab, "filesTab"))
     {
     selectedTab = filesTab;
     descSearch = cartOptionalString(cart, TRACK_SEARCH_ON_DESCR);
     }
 #else///ifndef USE_TABS
 descSearch = cartOptionalString(cart, TRACK_SEARCH_ON_DESCR);
 #endif///ndef USE_TABS
 
 #ifdef USE_TABS
 struct trix *trix;
 char trixFile[HDB_MAX_PATH_STRING];
 getSearchTrixFile(db, trixFile, sizeof(trixFile));
 trix = trixOpen(trixFile);
 #endif///def USE_TABS
 
 printf("<div style='max-width:1080px;'>");
 // FIXME: Do we need a form at all?
 printf("<form action='../cgi-bin/hgFileSearch' name='%s' id='%s' method='get'>\n\n", 
        FILE_SEARCH_FORM,FILE_SEARCH_FORM);
 cartSaveSession(cart);  // Creates hidden var of hgsid to avoid bad voodoo
 
 printf("<input type='hidden' name='db' value='%s'>\n", db);
 printf("<input type='hidden' name='%s' value=''>\n",TRACK_SEARCH_DEL_ROW);
 printf("<input type='hidden' name='%s' value=''>\n",TRACK_SEARCH_ADD_ROW);
 
 #ifdef USE_TABS
 printf("<input type='hidden' name='%s' id='currentTab' value='%s'>\n", 
        FILE_SEARCH_CURRENT_TAB, currentTab);
 printf("<div id='tabs' style='display:none; %s'>\n"
        "<ul>\n"
        "<li><a href='#simpleTab'><B style='font-size:.9em;font-family: arial, Geneva, "
        "Helvetica, san-serif;'>Search</B></a></li>\n"
        "<li><a href='#filesTab'><B style='font-size:.9em;font-family: arial, Geneva, "
        "Helvetica, san-serif;'>Files</B></a></li>\n"
        "</ul>\n",cgiBrowser()==btIE?"width:1060px;":"max-width:inherit;");
 
 // Files tab
 printf("<div id='simpleTab' style='max-width:inherit;'>\n");
 
 printf("<table id='simpleTable' style='width:100%%; font-size:.9em;'><tr><td colspan='2'>");
 printf("<input type='text' name='%s' id='simpleSearch' class='submitOnEnter' value='%s' "
-       "style='max-width:1000px; width:100%%;' onkeyup='findTracks.searchButtonsEnable(true);'>\n",
+       "style='max-width:1000px; width:100%%;'>\n",
        TRACK_SEARCH_SIMPLE,descSearch == NULL ? "" : descSearch);
+jsOnEventById("keyup", "simpleSearch", "findTracks.searchButtonsEnable(true);");
 
 printf("</td></tr><td style='max-height:4px;'></td></tr></table>");
 printf("<input type='submit' name='%s' id='searchSubmit' value='search' style='font-size:.8em;'>\n",
        FILE_SEARCH);
-printf("<input type='button' name='clear' value='clear' class='clear' style='font-size:.8em;' "
-       "onclick='findTracks.clear();'>\n");
+printf("<input type='button' name='clear' id='clear' value='clear' class='clear' style='font-size:.8em;'>\n");
+jsOnEventById("click","clear","findTracks.clear();");
 printf("<input type='submit' name='submit' value='cancel' class='cancel' "
        "style='font-size:.8em;'>\n");
 printf("</div>\n");
 #endif///def USE_TABS
 
 // Files tab
 printf("<div id='filesTab' style='width:inherit;'>\n"
         "<table id='filesTable' cellSpacing=0 style='width:inherit; font-size:.9em;'>\n");
 cols = 8;
 
 // Track Name contains
 printf("<tr><td colspan=3></td>");
 printf("<td nowrap><b style='max-width:100px;'>Track&nbsp;Name:</b></td>");
 printf("<td align='right'>contains</td>\n");
 printf("<td colspan='%d'>", cols - 4);
 printf("<input type='text' name='%s' id='nameSearch' class='submitOnEnter' value='%s' "
-       "onkeyup='findTracks.searchButtonsEnable(true);' style='min-width:326px; font-size:.9em;'>",
+       "style='min-width:326px; font-size:.9em;'>",
        TRACK_SEARCH_ON_NAME, nameSearch == NULL ? "" : nameSearch);
+jsOnEventById("keyup", "nameSearch", "findTracks.searchButtonsEnable(true);"); 
 printf("</td></tr>\n");
 
 // Description contains
 printf("<tr><td colspan=2></td><td align='right'>and&nbsp;</td>");
 printf("<td><b style='max-width:100px;'>Description:</b></td>");
 printf("<td align='right'>contains</td>\n");
 printf("<td colspan='%d'>", cols - 4);
 printf("<input type='text' name='%s' id='descSearch' value='%s' class='submitOnEnter' "
-       "onkeyup='findTracks.searchButtonsEnable(true);' style='max-width:536px; "
-       "width:536px; font-size:.9em;'>",
+       "style='max-width:536px; width:536px; font-size:.9em;'>",
        TRACK_SEARCH_ON_DESCR, descSearch == NULL ? "" : descSearch);
+jsOnEventById("keyup", "descSearch", "findTracks.searchButtonsEnable(true);");
 printf("</td></tr>\n");
 
 // Set up Group dropdown
 struct grp *grps = hLoadGrps(db);
 grps = groupsFilterForTdbList(&grps,tdbList);
 int numGroups = slCount(grps) + 1; // Add Any
 char **groups = needMem(sizeof(char *) * numGroups);
 char **labels = needMem(sizeof(char *) * numGroups);
 groups[0] = ANYLABEL;
 labels[0] = ANYLABEL;
 int ix=1;
 struct grp *grp = grps;
 for (; grp != NULL; grp = grp->next,ix++)
     {
     groups[ix] = cloneString(grp->name);
     labels[ix] = cloneString(grp->label);
     }
 
 printf("<tr><td colspan=2></td><td align='right'>and&nbsp;</td>\n");
 printf("<td><b style='max-width:100px;'>Group:</b></td>");
 printf("<td align='right'>is</td>\n");
 printf("<td colspan='%d'>", cols - 4);
 char *groupSearch = cartOptionalString(cart, TRACK_SEARCH_ON_GROUP);
-cgiMakeDropListFull(TRACK_SEARCH_ON_GROUP, labels, groups, numGroups, groupSearch, 
-                    "class='groupSearch' style='min-width:40%; font-size:.9em;'");
+cgiMakeDropListFullExt(TRACK_SEARCH_ON_GROUP, labels, groups, numGroups, groupSearch, 
+    NULL, NULL, "min-width:40%; font-size:.9em;", "groupSearch");
 printf("</td></tr>\n");
 
 // Track Type is (drop down)
 printf("<tr><td colspan=2></td><td align='right'>and&nbsp;</td>\n");
 printf("<td nowrap><b style='max-width:100px;'>Data Format:</b></td>");
 printf("<td align='right'>is</td>\n");
 printf("<td colspan='%d'>", cols - 4);
 char *dropDownHtml = fileFormatSelectHtml(FILE_SEARCH_ON_FILETYPE,fileTypeSearch,
-                                          "style='min-width:40%; font-size:.9em;'");
+                                          NULL, NULL, "min-width:40%; font-size:.9em;");
 if (dropDownHtml)
     {
     puts(dropDownHtml);
     freeMem(dropDownHtml);
     }
 printf("</td></tr>\n");
 
 // mdb selects
 struct slPair *mdbSelects = NULL;
 if (metaDbExists)
     {
     struct slPair *mdbVars = mdbVarsSearchable(conn,FALSE,TRUE); // Not tables, just files
     mdbSelects = mdbSelectPairs(cart, mdbVars);
     char *output = mdbSelectsHtmlRows(conn,mdbSelects,mdbVars,cols,TRUE); // just for fileSearch
     if (output)
         {
         puts(output);
         freeMem(output);
         }
     slPairFreeList(&mdbVars);
     }
 
 printf("</table>\n");
 printf("<input type='submit' name='%s' id='searchSubmit' value='search' style='font-size:.8em;'>\n", 
        FILE_SEARCH);
-printf("<input type='button' name='clear' value='clear' class='clear' style='font-size:.8em;' "
-       "onclick='findTracks.clear();'>\n");
+printf("<input type='button' name='clear' id='clear' value='clear' class='clear' style='font-size:.8em;'>\n");
+jsOnEventById("click", "clear", "findTracks.clear();");
 printf("<input type='submit' name='submit' value='cancel' class='cancel' "
        "style='font-size:.8em;'>\n");
 printf("</div>\n");
 
 #ifdef USE_TABS
 printf("</div>\n"); // End tabs div
 #endif///def USE_TABS
 
 if (nameSearch != NULL && !strlen(nameSearch))
     nameSearch = NULL;
 if (descSearch != NULL && !strlen(descSearch))
     descSearch = NULL;
 if (groupSearch != NULL && sameString(groupSearch, ANYLABEL))
     groupSearch = NULL;
 
 printf("</form>\n");
 printf("</div>"); // Restricts to max-width:1000px;
 cgiDown(0.8);
 
 if (measureTiming)
     uglyTime("Generated search controls");
 
 
 #ifdef USE_TABS
 if (doSearch && selectedTab==simpleTab && isEmpty(descSearch))
     doSearch = FALSE;
 #endif///def USE_TABS
 
 if (doSearch)
     {
     // Now search
 #ifdef USE_TABS
     struct slRef *foundTdbs = NULL;
     if (selectedTab==simpleTab)
         {
         foundTdbs = simpleSearchForTdbs(trix,descWords,descWordCount);
         // What to do now?
         if (measureTiming)
             uglyTime("Searched for tracks");
 
         // Sort and Print results
         if (selectedTab!=filesTab)
             {
             enum sortBy sortBy = cartUsualInt(cart,TRACK_SEARCH_SORT,sbRelevance);
             int tracksFound = slCount(foundTdbs);
             if (tracksFound > 1)
                 findTracksSort(&tracks,sortBy);
 
             displayFoundTracks(cart,tracks,tracksFound,sortBy);
 
             if (measureTiming)
                 uglyTime("Displayed found files");
             }
         }
     else if (selectedTab==filesTab && mdbPairs != NULL)
 #endif ///def USE_TABS
         {
         if (nameSearch || descSearch || groupSearch)
             {  // Use nameSearch, descSearch and groupSearch to narrow down the list of composites.
 
             if (isNotEmpty(nameSearch) || isNotEmpty(descSearch) || isNotEmpty(groupSearch))
                 {
                 struct trackDb *tdbList = hTrackDb(db);
                 struct trackDb *tdbsMatch = tdbFilterBy(&tdbList, nameSearch, descSearch, 
                                                         groupSearch);
 
                 // Now we have a list of tracks, so we need a unique list of composites to add
                 doSearch = mdbSelectsAddFoundComposites(&mdbSelects,tdbsMatch);
                 }
             }
 
         if (doSearch && mdbSelects != NULL && isNotEmpty(fileTypeSearch))
             fileSearchResults(db, conn, cart, mdbSelects, fileTypeSearch);
         else
             printf("<DIV id='filesFound'><BR>No files found.<BR></DIV><BR>\n");
 
         if (measureTiming)
             uglyTime("Searched for files");
         }
 
     slPairFreeList(&mdbSelects);
     }
 hFreeConn(&conn);
 
 webNewSection("About " FILE_SEARCH_NAME);
 printf("Search for downloadable ENCODE files by entering search terms in "
        "the Track name or Description fields and/or by making selections with "
        "the group, data format, and/or ENCODE metadata drop-downs.");
 printf("<BR><a target='_blank' href='../goldenPath/help/fileSearch.html'>more help</a>\n");
 }
 
 void doMiddle(struct cart *cart)
 /* Write body of web page. */
 {
 struct trackDb *tdbList = NULL;
 char *organism = NULL;
 char *db = NULL;
 getDbAndGenome(cart, &db, &organism, NULL);
 char *chrom = cartUsualString(cart, "c", hDefaultChrom(db));
 measureTiming = isNotEmpty(cartOptionalString(cart, "measureTiming"));
 
 // QUESTION: Do We need track list ???  trackHash ??? Can't we just get one track and no children
 trackHash = trackHashMakeWithComposites(db,chrom,&tdbList,FALSE);
 
 cartWebStart(cart, db, "Search for " FILE_SEARCH_WHAT " in the %s %s Assembly", 
              organism, hFreezeFromDb(db));
 
 webIncludeResourceFile("HGStyle.css");
 webIncludeResourceFile("jquery-ui.css");
 webIncludeResourceFile("ui.dropdownchecklist.css");
 jsIncludeFile("jquery.js", NULL);
 jsIncludeFile("jquery-ui.js", NULL);
 jsIncludeFile("ui.dropdownchecklist.js",NULL);
 jsIncludeFile("utils.js",NULL);
 
 // This line is needed to get the multi-selects initialized
 jsIncludeFile("ddcl.js",NULL);
-printf("<script type='text/javascript'>$(document).ready(function() "
-       "{ findTracks.updateMdbHelp(0); });</script>\n");
+jsInline("$(document).ready(function() "
+       "{ findTracks.updateMdbHelp(0); });\n");
 
 doFileSearch(db,organism,cart,tdbList);
 
 
 printf("<BR>\n");
 webEnd();
 }
 
 char *excludeVars[] = { "submit", "Submit", "g", "ajax", "clearCache", 
                         FILE_SEARCH,TRACK_SEARCH_ADD_ROW,TRACK_SEARCH_DEL_ROW};
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 long enteredMainTime = clock1000();
 cgiSpoof(&argc, argv);
 cartEmptyShell(doMiddle, hUserCookie(), excludeVars, NULL);
 cgiExitTime("hgFileSearch", enteredMainTime);
 return 0;
 }
 
 // TODO:
 // 1) Done: Limit to first 1000
 // 2) Work out simple verses advanced tabs
 // 3) work out support for non-encode downloads
 // 4) Make an hgTrackSearch to replace hgTracks track search ??   
 //    Simpler code, but may not be good idea because of composite reshaping in cart vars