e035146f3dc0ce0eaac009b1a37b416b31b068ea
galt
  Wed Aug 1 13:37:07 2018 -0700
refs #21729. Added CSP and nonce and javascript-separation routines to the Python library hgLib in PyLib.  Changed hgGeneGraph to use those functions and stop XSS attacks. Modified commit for demonstration.

diff --git src/hg/hgGeneGraph/hgGeneGraph src/hg/hgGeneGraph/hgGeneGraph
index 9ac9129..c6a271b 100755
--- src/hg/hgGeneGraph/hgGeneGraph
+++ src/hg/hgGeneGraph/hgGeneGraph
@@ -28,32 +28,32 @@
 # ggLinkEvent (details about link), ggEventDb (details about links from databases), 
 # ggEventText (details about links from text mining), ggDoc (details about documents for ggEventText)
 # ggGeneName (symbols), ggGeneClass (HPRD/Panther class)
 
 # these are default python modules on python 2.7, no errors expected here
 import sys, cgi, os, string, urllib, operator, hashlib
 from sys import exit
 from collections import defaultdict, namedtuple
 from os.path import *
 
 # import the UCSC-specific library
 sys.path.append(join(dirname(__file__), "pyLib"))
 try:
     from hgLib import cgiArgs, cgiSetup, cgiString, printContentType, printMenuBar, \
             sqlConnect, sqlQuery, errAbort, cfgOption, runCmd, cgiGetAll, printHgcHeader, \
-            printHgcSection, webStartGbNoBanner, htmlPageEnd, hConnectCentral, sqlTableExists, \
-            readSmallFile
+            printHgcSection, getNonce, getCspMetaHeader, jsOnEventById, jsInlineFinish, webStartGbNoBanner, htmlPageEnd, hConnectCentral, sqlTableExists, \
+            readSmallFil
 except:
     print("Content-type: text/html\n")
     print("Cannot find the directory cgi-bin/pyLib in Apache. This is an installation error.")
     print("All all parts of cgi-bin installed? Did you do 'make' in kent/src/hg/pyLib?")
 
 import MySQLdb
 
 # not using feedback button for now. Fan would have liked it, but not sure how we can write
 # to any form of database.
 
 # the list of allowed chars in cgi args: digits, letters and dashes
 legalChars = set(string.digits)
 legalChars.update(set(string.letters))
 legalChars.update("_-./: ")
 
@@ -133,31 +133,31 @@
 #<script src="//code.jquery.com/jquery-1.10.2.js"></script>
 #<script src="//code.jquery.com/ui/1.11.0/jquery-ui.js"></script>
 #<script src="../js/readmore.min.js"></script>
 
 #<link rel="stylesheet" href="//code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css">
 #<link rel="stylesheet" href="../style/HGStyle.css" type="text/css" />
 #<link rel='stylesheet' href='../style/nice_menu.css' type='text/css' />
 
 def printInlineAndStyles():
     #print('<script src="//code.jquery.com/ui/1.11.0/jquery-ui.js"></script>')
     print('<script src="//cdn.rawgit.com/jedfoster/Readmore.js/master/readmore.min.js"></script>')
 
 
 
     print("""
-<script type="text/javascript">
+<script type="text/javascript" nonce='%s'>
   $(function() {
     //$( document ).uitooltip();
     //$( document ).uitooltip();
     $('[data-toggle="tooltip"]').bsTooltip(); // bootstrap does not really allow HTML in the title attribute
 
     // use jquery ui tooltips for the graph
     var opt = {
         items: "area",
         track : true,
         content: function() {return $(this).prop('title')}
     };
     $("area").uitooltip(opt);
 
     // when user opens annotate genes menu, close the tooltip
     //$('#colorLink').click( function () { console.log($(this)); $(this).tooltip("close"); } );
@@ -203,35 +203,35 @@
 }
 .ui-tooltip
 {
       font-size:10pt;
       font-family:Helvetica;
       padding: 3px;
 }
 
 ul { padding-left: 15px; margin: 0px; padding-top:3px;}
 
 .tooltip.right  { margin-left: 20px; }
 .tooltip.left  { margin-left: -20px; }
 
 </style>
 
-    """)
+    """ % getNonce())
 
 def htmlHeader():
     " print start of page "
-    webStartGbNoBanner("", "Genome Browser Gene Interaction Graph")
+    webStartGbNoBanner(getCspMetaHeader(), "Genome Browser Gene Interaction Graph")
 
     print('<body class="hgc cgi">')
     printMenuBar()
 
     db = getCgiVar("db")
     printHgcHeader(db, "Gene Interactions Track",
             "Gene interactions and pathways from curated databases and text-mining",
             addGoButton=False, infoUrl=MANUALURL, infoMouseOver="Open help page")
 
 def mustBeClean(str):
     """ make sure a string contains only letters and digits """
     if str==None:
         return str
 
     str = urllib.unquote(str)
@@ -886,99 +886,104 @@
         q = 'SELECT text from ggGeneClass where gene="%s"' % gene
         rows = sqlQuery(conn, q)
         if len(rows)!=0:
             geneDescs[gene].append(rows[0].text)
     return geneDescs
 
 def printHttpHead(format):
     " print content type header "
     mimeType = mimeTypes[format]
     print "Content-Type: %s" % mimeType
     if format in ["pdf", "svg", "sif"]:
         fname = "graph."+format
         print "Content-Disposition: attachment; filename=%s" % fname
     print
 
-def printCheckbox(name, isChecked, addAttr):
+def printCheckbox(name, isChecked, event, codeStr):
     " print a html checkbox "
     addStr = ""
     if isChecked:
         addStr = 'checked="true" '
-    print '<input type="checkbox" name="%s" value="on" %s%s />' % (name, addStr, addAttr)
+    print '<input type="checkbox" id="%s" name="%s" value="on" %s />' % (name, name, addStr)
+    if event != "":
+	jsOnEventById(event, name, codeStr)
 
 def getFilterStatus():
     """ return a set of the support tags that should be shown, based on the CGI var 'supportLevel'.
     """
     supportLevel  = getCgiVar("supportLevel", "text")
     tagSet = set(["pwy"])
     if supportLevel=="ppi" or supportLevel=="text":
         tagSet.add("ppi")
     if supportLevel=="text":
         tagSet.add("text")
     return tagSet
 
-def printDropdown(cgiArgName, selectedName, options, addStr=""):
+def printDropdown(cgiArgName, selectedName, options, event="", codeStr=""):
     " print a html dropdown <select> box. "
-    print("<select name='%s'%s>" % (cgiArgName, addStr))
+    print("<select id='%s' name='%s'>" % (cgiArgName, cgiArgName))
     for name, label in options:
         addStr = ""
         if name==selectedName:
             addStr = " selected"
-
         print('<option value="%s"%s>%s</option>' % (name, addStr, label))
     print("</select>")
+    if event != "":
+	jsOnEventById(event, cgiArgName, codeStr)
+
 
 def printFilterMenu(targetGene, addNeighbors):
     # form to filter by type
     print "<small>"
     print '<form name="filterForm" action="%s" method="GET">' % makeSelfUrl({}, True)
     print """<b>Gene:</b>""" 
     print '<input type="text" size="10" name="gene" value="%s" />' % targetGene
     print "&nbsp;"
     print '<input type="submit" name="1" value="OK">'
     print "&nbsp;&nbsp;&nbsp;"
 
 
     supportLevel = getCgiVar("supportLevel", "text")
     if supportLevel not in ["text", "pwy", "ppi"]:
         errAbort("Illegal value for the supportLevel argument. Can only be text,pwy or ppi.")
 
-    onChangeAttr = '''onchange="document.forms['filterForm'].submit();"'''
+    event = "change"
+    codeStr = "document.forms['filterForm'].submit();"
 
-    printDropdown("supportLevel", supportLevel, [("text", "Show all interactions, even only text-mining support"), ("ppi", "Show only interactions with some database support"), ("pwy", "Show only interactions with pathway database support")], addStr=onChangeAttr )
+    printDropdown("supportLevel", supportLevel, [("text", "Show all interactions, even only text-mining support"), ("ppi", "Show only interactions with some database support"), ("pwy", "Show only interactions with pathway database support")], event, codeStr )
 
     #filterTags = getFilterStatus()
     #printCheckbox("pwy", "pwy" in filterTags, onChangeAttr)
     #print '<span style="background:%s;color:white" title="Show interactions manually curated by a pathway database, like WikiPathways, KEGG or Reactome, in dark-blue" onclick="">Pathways</span>' % LTCOLOR
 
     #printCheckbox("ppi", "ppi" in filterTags, onChangeAttr)
     #print '<span style="background:%s;color:white" title="Show interactions manually curated by a protein interaction database, like IntAct or BioGrid, in light-blue">Protein Interaction</span>' % HTCOLOR
 
     #printCheckbox("text", "text" in filterTags, onChangeAttr)
     #print '<span style="background:%s;color:white" title="Show interactions automatically extracted from PubMed abstracts with text-mining software in grey." onclick="">Text-Mining</span>' % TEXTCOLOR
 
     print "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
 
     #if addNeighbors:
         #addNeighborLink = makeSelfLink("Show only %s links" % targetGene, {"onlyDirect":"1"})
         #print("Only %s-interacting genes and only the most-mentioned interactions are shown. (%s)<br>" % (targetGene, noNeighborLink))
     #else:
         #addNeighborLink = makeSelfLink("Show links between neighbors", {"onlyDirect":None})
         #print("Only %s interactions are shown (%s)<br>" % (targetGene, addNeighborLink))
     #print(addNeighborLink)
     hideIndirectStatus = (getCgiVar("hideIndirect")=="on")
-    printCheckbox("hideIndirect", hideIndirectStatus, onChangeAttr)
+    printCheckbox("hideIndirect", hideIndirectStatus, event, codeStr)
     print '<span style="font-weight:bold">Hide non-%s interactions</span>' % targetGene
     print "&nbsp;&nbsp;&nbsp;"
 
     geneCount = getCgiVar("geneCount", DEFGENECOUNT)
     print "<b>Show top"
     print '<input name="geneCount" type="text" size="3" value="%s">' % geneCount
     print "Genes on graph</b>"
     print "&nbsp;&nbsp;&nbsp;"
 
     printSelfHiddenVars({}, skipList=["supportLevel", "hideIndirect", "gene"])
 
     print "</form>"
     print "</small>"
 
 def printDropDownMenu(label, entries, tooltip, selectedLabel=None):
@@ -2007,18 +2012,19 @@
         graphLinks, lowLinks = buildGraph(conn, gene, geneCount, MINSUPP, addNeighbors)
         weightedLinks, minAbsCount = flattenLink(graphLinks)
         printGraph(conn, weightedLinks, alg, addNeighbors, gene, format)
         sys.exit(0)
 
 
     printContentType()
 
     if cgiString("debug") is not None:
         global DEBUG
         DEBUG = True
 
     htmlHeader()
     printInlineAndStyles()
     htmlMiddle()
+    jsInlineFinish()
     htmlPageEnd()
 
 main()