d27769317be7f92e199f69d75b10a7421eecf770 max Sat Sep 26 21:57:15 2026 -0700 hgGeneGraph: require a form submission and a checked answer to file a feedback report diff --git src/hg/hgGeneGraph/hgGeneGraph src/hg/hgGeneGraph/hgGeneGraph index 7ee374f979d..0f87b36bf9d 100755 --- src/hg/hgGeneGraph/hgGeneGraph +++ src/hg/hgGeneGraph/hgGeneGraph @@ -19,31 +19,31 @@ # dark blue, dashed = only low-throughput data # dark blue, thickness = low-throughput data + text # dark blue + dashed = only pathway data # code review # - os.system is not a security risk here, no variables go into the cmd line # - mysql statements are not escaped, instead all CGI vars are checked for non-alpha letters # hgFixed tables required for this script: ggLink (main table with gene-gene links), # 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.request, urllib.parse, urllib.error, operator, hashlib, html +import sys, cgi, os, string, urllib.request, urllib.parse, urllib.error, operator, hashlib, html, random from sys import exit from collections import defaultdict, namedtuple from os.path import * #These two lines should be commented out whenever committing this file into git. #They activate stack traces to stdout which can in certain cases reveal the mysql password, #when there is a Mysql connection problem. #import cgitb #cgitb.enable() # import the UCSC-specific library sys.path.append(join(dirname(__file__), "pyLib")) try: from hgLib3 import cgiArgs, cgiSetup, cgiString, printContentType, printMenuBar, \ sqlConnect, sqlQuery, errAbort, cfgOption, runCmd, cgiGetAll, printHgcHeader, \ @@ -1880,57 +1880,69 @@ print("

Interaction %s - %s

" % (causeGene, themeGene)) conn = sqlConnect(GGDB) rows = queryEventText(conn, causeGene, themeGene) print ("

") print ("Thank you for reporting errors, e.g.") print("

") print("This makes it easier for us to improve the text mining system or database imports.
") print("You can leave your email address in the comment if you want to give us the possibility to comment.

") print ("Optional comment:
") - print(('

' % basename(__file__))) + print(('' % basename(__file__))) print(('' % param)) + # A short arithmetic question, checked back in removeInteraction() against the salted + # hash of its answer, keeps this write-capable form from being submitted from anywhere + # but this page. + numA = random.randint(1, 9) + numB = random.randint(1, 9) + print(('' % saltedHash(str(numA+numB)))) print ('') print ("

") + print ('What is %d + %d?

' % (numA, numB)) print ('') print ('
') #printMsrNlpRows(rows) showSnipsLink(conn, causeGene, themeGene) #def namedtuple_factory(cursor, row): #""" #used as a function pointer to sqlite to have it return structs with names and not just arrays #""" #fields = [col[0] for col in cursor.description] #Row = collections.namedtuple("Row", fields) #return Row(*row) #def openSqlite(dbName): #" opens sqlite database and have it return structs (=namedtuples) " #tryCount = retries #con = None #con = sqlite3.connect(dbName, timeout=20) #con.row_factory = namedtuple_factory ##con.row_factory = sqlite3.Row -def removeInteraction(param, captcha, comment): +def removeInteraction(param, captcha, captchaHash, comment): + if os.environ.get("REQUEST_METHOD") != "POST": + errAbort("This action requires a form submission, not a link.") + if captcha is None or captchaHash is None or saltedHash(captcha.strip()) != captchaHash: + errAbort("The answer to the arithmetic question was not correct. Please go back and try again.") + fields = param.split(":") if len(fields)!=2: errAbort( "illegal CGI parameter") causeGene, themeGene = fields ip = html.escape(os.environ["REMOTE_ADDR"]) conn = hConnectCentral() if not sqlTableExists(conn, "ggFeedback"): # sqlQuery() reads cursor.description, which is None after a statement that returns no # rows, so it cannot run a CREATE sqlUpdate(conn, "CREATE TABLE ggFeedback (causeGene varchar(255), themeGene varchar(255), pmid varchar(255), " \ "comment varchar(10000), time TIMESTAMP, ip varchar(255), INDEX allIdx (causeGene, themeGene, pmid));") # name the columns: the positional version put NOW() into "comment" and the comment text @@ -2006,32 +2018,33 @@ print("
  • %s: %d" % (db, len(pairSet))) print("") def htmlMiddle(): " print html middle part " sys.stdout.flush() flag = getCgiVar("flag") if flag!=None: flagInteraction(flag) exit(0) remove = getCgiVar("remove") if remove!=None: captcha = getCgiVar("captcha") + captchaHash = getCgiVar("captchaHash", maxLen=40) comment = getCgiVar("comment", allowAnyChar=True, maxLen=10000) - removeInteraction(remove, captcha, comment) + removeInteraction(remove, captcha, captchaHash, comment) exit(0) docId = getCgiVar("docId") if docId!=None: showDoc(docId) exit(0) link = getCgiVar("link") if link!=None: showLink(link) exit(0) page = getCgiVar("page") if page=="stats": showStats()