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/hgUserSuggestion/hgUserSuggestion.c src/hg/hgUserSuggestion/hgUserSuggestion.c index ddfccc8..2b99f04 100644 --- src/hg/hgUserSuggestion/hgUserSuggestion.c +++ src/hg/hgUserSuggestion/hgUserSuggestion.c @@ -1,523 +1,519 @@ /* hgUserSuggestion - CGI-script to collect user's suggestion. */ /* Copyright (C) 2013 The Regents of the University of California * See README in this or parent directory for licensing information. */ #include "common.h" #include "errAbort.h" #include "hCommon.h" #include "jksql.h" #include "portable.h" #include "cheapcgi.h" #include "htmshell.h" #include "hdb.h" #include "hui.h" #include "cart.h" #include "hPrint.h" #include "dbDb.h" #include "web.h" #include "hash.h" #include "hgConfig.h" #include "hgUserSuggestion.h" #include "mailViaPipe.h" /* ---- Global variables. ---- */ struct cart *cart; /* The user's ui state. */ struct hash *oldVars = NULL; /* ---- Global helper functions ---- */ void checkHgConfForSuggestion() /* Abort if hg.conf has not been set up to accept suggestion */ { if (isEmpty(cfgOption(CFG_SUGGEST_MAILTOADDR)) || isEmpty(cfgOption(CFG_SUGGEST_MAILFROMADDR)) || isEmpty(cfgOption(CFG_FILTERKEYWORD)) || isEmpty(cfgOption(CFG_SUGGEST_MAIL_SIGNATURE)) || isEmpty(cfgOption(CFG_SUGGEST_MAIL_RETURN_ADDR)) || isEmpty(cfgOption(CFG_SUGGEST_BROWSER_NAME))) errAbort("This Genome Browser has not been configured to accept suggestions yet. Please contact the browser administrator for more information."); } char *mailToAddr() /* Return the address to send suggestion to */ { return cloneString(cfgOption(CFG_SUGGEST_MAILTOADDR)); } char *mailFromAddr() /* Return the bogus sender address to help filter out spam */ { return cloneString(cfgOption(CFG_SUGGEST_MAILFROMADDR)); } char *filterKeyword() /* Return the keyword used to filter out spam */ { return cloneString(cfgOption(CFG_FILTERKEYWORD)); } char *mailSignature() /* Return the signature to be used by outbound mail. */ { return cloneString(cfgOption(CFG_SUGGEST_MAIL_SIGNATURE)); } char *mailReturnAddr() /* Return the return addr. to be used by outbound mail. */ { return cloneString(cfgOption(CFG_SUGGEST_MAIL_RETURN_ADDR)); } char *browserName() /* Return the browser name like 'UCSC Genome Browser' */ { return cloneString(cfgOption(CFG_SUGGEST_BROWSER_NAME)); } static char *now() /* Return a mysql-formatted time like "2008-05-19 15:33:34". */ { char nowBuf[512]; time_t curtime; curtime = time (NULL); struct tm *theTime = localtime(&curtime); strftime(nowBuf, sizeof nowBuf, "%Y-%m-%d %H:%M:%S", theTime); return cloneString(nowBuf); } int spc_email_isvalid(const char *address) { /* Check the format of an email address syntactically. Return 1 if * valid, else 0 */ /* Code copied from the book: "Secure Programming Cookbook for C and C++" By: John Viega; Matt Messier Publisher: O'Reilly Media, Inc. Pub. Date: July 14, 2003 Print ISBN-13: 978-0-596-00394-4 */ int count = 0; const char *c, *domain; static char *rfc822_specials = "()<>@,;:\\\"[]"; /* first we validate the name portion (name@domain) */ for (c = address; *c; c++) { if (*c == '\"' && (c == address || *(c - 1) == '.' || *(c - 1) == '\"')) { while (*++c) { if (*c == '\"') break; if (*c == '\\' && (*++c == ' ')) continue; if (*c <= ' ' || *c >= 127) return 0; } if (!*c++) return 0; if (*c == '@') break; if (*c != '.') return 0; continue; } if (*c == '@') break; if (*c <= ' ' || *c >= 127) return 0; if (strchr(rfc822_specials, *c)) return 0; } if (c == address || *(c - 1) == '.') return 0; /* next we validate the domain portion (name@domain) */ if (!*(domain = ++c)) return 0; do { if (*c == '.') { if (c == domain || *(c - 1) == '.') return 0; count++; } if (*c <= ' ' || *c >= 127) return 0; if (strchr(rfc822_specials, *c)) return 0; } while (*++c); return (count >= 1); } boolean validateCategory(char *category) /* Validate the Category from the request */ { const char *cat[5] = {"Tracks", "Genome Assemblies", "Browser Tools", "Command-line Utilities", "Others"}; int i; for(i=0;i<5;i++) { if (strcmp(cat[i], category)==0) return TRUE; } return FALSE; } /* javascript functions */ void printMainForm() /* Create the main suggestion form */ { hPrintf( - "
\n"); + " \n"); +jsOnEventById("load","mainForm","document.forms.mainForm.name.focus();"); hPrintf( "

User Suggestion Form

\n" "

If you have ideas about how we can improve the value of the Genome Browser to your research, " "we'd like to hear from you. Please provide a concise description below. " "A copy of the suggestion will be sent to your email address along with a reference number. " "You may follow up on the status of your request at any time by contacting us and quoting the reference number.

"); hPrintf("

Please note: this form is not the proper place to submit questions regarding browser use or bug reports. Use the links on our contact page instead.

"); hPrintf("

"); hPrintf( "
\n" "

\n" "

\n" "

\n"); hPrintf( "

\n"); hPrintf( "

\n" "


\n" "" "
\n"); hPrintf( "

\n" " \n" " \n" "

\n"); hPrintf( "
\n" - " \n" + " \n" " \n" "
\n" " \n" "
\n\n"); +jsOnEventById("click","sendButton","submitform();"); } void printValidateScript() /* javascript to validate form inputs */ { -hPrintf( - "
\n\n"); + ); } void printCheckCaptchaScript() /* javascript to check CAPTCHA code */ { -hPrintf( - "
\n\n"); + ); } void printSubmitFormScript() /* javascript to submit form */ { -hPrintf( - " \n\n"); + ); } void printSuggestionConfirmed(char *summary, char * refID, char *userAddr, char *adminAddr, char *details) /* display suggestion confirm page */ { hPrintf( "

Thank you for your suggestion!

"); hPrintf( "

" "You may follow up on the status of your request at any time by " "contacting us and quoting your reference number:

%s

" "A copy of this information has also been sent to you at %s.

", refID, userAddr); hPrintf( "

Click here if you wish to make additional suggestions.

"); hPrintf( "

" "Your suggestion summary:
" "%s
" "Your suggestion details:
" "

%s
" "

", summary, details); } void printInvalidForm() /* display invalid form page */ { hPrintf( "

Invalid Form.

"); hPrintf( "

" "The form is invalid. Please correct it and " - "submit again.

" + "submit again.

" ); +jsOnEventById("click", "goBack", "history.go(-1)"); } void printInvalidCategory(char *invalidCategory) /* display invalid category page */ { hPrintf( "

Invalid Category.

"); hPrintf( "

" "The category \"%s\" is invalid. Please correct it and " - "submit again.

", + "submit again.

", invalidCategory); +jsOnEventById("click", "goBack", "history.go(-1)"); } void printInvalidEmailAddr(char *invalidEmailAddr) /* display suggestion confirm page */ { hPrintf( "

Invalid email address format.

"); hPrintf( "

" "The email address \"%s\" is invalid. Please correct it and " - "submit again.

", + "submit again.

", invalidEmailAddr); +jsOnEventById("click", "goBack", "history.go(-1)"); } void sendSuggestionBack(char *sName, char *sEmail, char *sCategory, char *sSummary, char *sDetails, char *suggestID) /* send back the suggestion */ { /* parameters from hg.cong */ char *mailTo = mailToAddr(); char *mailFrom=mailFromAddr(); char *filter=filterKeyword(); char subject[512]; char msg[4608]; /* need to make larger */ safef(msg, sizeof(msg), "SuggestionID:: %s\nUserName:: %s\nUserEmail:: %s\nCategory:: %s\nSummary:: %s\n\n\nDetails::\n%s", suggestID, sName, sEmail, sCategory, sSummary, sDetails); safef(subject, sizeof(subject),"%s %s", filter, suggestID); // ignore returned result mailViaPipe(mailTo, subject, msg, mailFrom); } void sendConfirmMail(char *emailAddr, char *suggestID, char *summary, char *details) /* send user suggestion confirm mail */ { char subject[512]; char msg[4608]; char *remoteAddr=getenv("REMOTE_ADDR"); char brwName[512]; char returnAddr[512]; char signature[512]; char userEmailAddr[512]; safecpy(brwName,sizeof(brwName), browserName()); safecpy(returnAddr,sizeof(returnAddr), mailReturnAddr()); safecpy(signature,sizeof(signature), mailSignature()); safecpy(userEmailAddr, sizeof(userEmailAddr),emailAddr); safef(subject, sizeof(subject),"Thank you for your suggestion to the %s", brwName); safef(msg, sizeof(msg), " Someone (probably you, from IP address %s) submitted a suggestion to the %s regarding \"%s\".\n\n The suggestion has been assigned a reference number of \"%s\". If you wish to follow up on the progress of this suggestion with browser staff, you may contact us at %s. Please include the reference number of your suggestion in the email.\n\nThank you for your input,\n%s\n\nYour suggestion summary:\n%s\n\nYour suggestion details:\n%s", remoteAddr, brwName, summary, suggestID, returnAddr, signature, summary, details); // ignore returned result mailViaPipe(userEmailAddr, subject, msg, returnAddr); } void askForSuggest(char *organism, char *db) /* Put up the suggestion form. */ { printMainForm(); printValidateScript(); printCheckCaptchaScript(); printSubmitFormScript(); //cartSaveSession(cart); } void submitSuggestion() /* send the suggestion to ,.. */ { /* parameters from hg.cong */ char *filter=filterKeyword(); /* values from cart */ char *sName=cartUsualString(cart,"suggestName",""); char *sEmail=cartUsualString(cart,"suggestEmail",""); char *sCategory=cartUsualString(cart,"suggestCategory",""); char *sSummary=cartUsualString(cart,"suggestSummary",""); char *sDetails=cartUsualString(cart,"suggestDetails",""); char *sWebsite=cartUsualString(cart,"suggestWebsite",""); char suggestID[512]; safef(suggestID, sizeof(suggestID),"%s %s", sEmail, now()); char subject[512]; safef(subject, sizeof(subject),"%s %s", filter, suggestID); /* reject if the hidden field is not blank */ if (isNotEmpty(sWebsite)) { printInvalidForm(); cartSetString(cart, "suggestWebsite", ""); return; } /* reject suggestion if category is invalid */ if (!validateCategory(sCategory)) { printInvalidCategory(sCategory); return; } /* Send back suggestion only with valid user email address */ if (spc_email_isvalid(sEmail) != 0) { /* send back the suggestion */ sendSuggestionBack(sName, sEmail, sCategory, sSummary, sDetails, suggestID); /* send confirmation mail to user */ sendConfirmMail(sEmail,suggestID, sSummary, sDetails); /* display confirmation page */ printSuggestionConfirmed(sSummary, suggestID, sEmail, mailReturnAddr(), sDetails); } else { /* save all field value in cart */ printInvalidEmailAddr(sEmail); } cartRemove(cart, "do.suggestSendMail"); } void doMiddle(struct cart *theCart) /* Write header and body of html page. */ { char *db, *organism; cart = theCart; getDbAndGenome(cart, &db, &organism, oldVars); cartWebStart(theCart, db, "UCSC Genome Browser: Suggestion Box"); checkHgConfForSuggestion(); if (cartVarExists(cart, "do.suggestSendMail")) { submitSuggestion(); cartRemove(cart, "do.suggestSendMail"); return; } askForSuggest(organism,db); cartWebEnd(); } /* Null terminated list of CGI Variables we don't want to save * permanently. */ char *excludeVars[] = {"Submit", "submit", "Clear", 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("hgUserSuggestion", enteredMainTime); return 0; }